0Pricing
C# Academy · Lektion

Client- und bidirektionales Streaming

Erstellen Sie Client-Streaming- und vollduplexe bidirektionale Streaming-Kanäle für Szenarien mit hohem Datendurchsatz.

Client- und bidirektionales Streaming ist eine kostenlose C# Academy-Lektion auf CoddyKit. Dies ist Lektion 3 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des C# Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der C# Academy-Kurs umfasst insgesamt 4 Lektionen.

Client-Streaming und bidirektionales Streaming

Über unäres und Server-Streaming hinaus unterstützt gRPC auch Client-Streaming (der Client sendet viele Nachrichten, der Server antwortet einmal) und bidirektionales Streaming (beide Seiten senden gleichzeitig mehrere Nachrichten über eine Verbindung).

Client-Streaming: Proto-Definition

Fügen Sie das Schlüsselwort stream vor dem Anfragetyp ein. Der Client sendet eine Folge von Nachrichten, und sobald er fertig ist, gibt der Server eine einzelne Antwort zurück.

service UploadService {
  // Client streams chunks, server returns a summary
  rpc UploadFile (stream FileChunk) returns (UploadSummary);
}

message FileChunk   { bytes data = 1; string filename = 2; }
message UploadSummary { int64 bytes_received = 1; string checksum = 2; }

Client-Streaming auf dem Server implementieren

Verwenden Sie IAsyncStreamReader<T>, um Clientnachrichten zu lesen, sobald sie eintreffen. Rufen Sie MoveNext() auf oder iterieren Sie mit ReadAllAsync().

public override async Task<UploadSummary> UploadFile(
    IAsyncStreamReader<FileChunk> requestStream,
    ServerCallContext context)
{
    long totalBytes = 0;
    using var ms = new MemoryStream();

    await foreach (var chunk in requestStream.ReadAllAsync(context.CancellationToken))
    {
        await ms.WriteAsync(chunk.Data.Memory, context.CancellationToken);
        totalBytes += chunk.Data.Length;
    }

    var checksum = ComputeMd5(ms.ToArray());
    return new UploadSummary { BytesReceived = totalBytes, Checksum = checksum };
}

Client-Streaming vom Client aus aufrufen

Öffnen Sie den Streaming-Aufruf, schreiben Sie Nachrichten mit RequestStream.WriteAsync(), und signalisieren Sie anschließend mit CompleteAsync() den Abschluss. Warten Sie die Antwort ab.

using var call = client.UploadFile();

var fileBytes = await File.ReadAllBytesAsync("large-file.bin");
const int chunkSize = 64 * 1024; // 64 KB

for (int offset = 0; offset < fileBytes.Length; offset += chunkSize)
{
    var chunk = fileBytes.Skip(offset).Take(chunkSize).ToArray();
    await call.RequestStream.WriteAsync(new FileChunk
    {
        Filename = "large-file.bin",
        Data = Google.Protobuf.ByteString.CopyFrom(chunk)
    });
}

await call.RequestStream.CompleteAsync(); // signal end
var summary = await call;                // await server response
Console.WriteLine($"Uploaded {summary.BytesReceived} bytes");

Bidirektionales Streaming: Proto-Definition

Fügen Sie auf beiden Seiten stream für Vollduplex-Kommunikation hinzu. Client und Server können jederzeit unabhängig voneinander Nachrichten senden.

service ChatService {
  rpc Chat (stream ChatMessage) returns (stream ChatMessage);
}

message ChatMessage {
  string user    = 1;
  string content = 2;
  int64  sent_at = 3;
}

Bidirektionales Streaming auf dem Server implementieren

Lesen Sie gleichzeitig aus IAsyncStreamReader und schreiben Sie in IServerStreamWriter. Verwenden Sie Task.WhenAll, um beide Schleifen gleichzeitig auszuführen.

public override async Task Chat(
    IAsyncStreamReader<ChatMessage> requestStream,
    IServerStreamWriter<ChatMessage> responseStream,
    ServerCallContext context)
{
    // Broadcast to all connected clients
    var readTask = Task.Run(async () =>
    {
        await foreach (var msg in requestStream.ReadAllAsync(context.CancellationToken))
        {
            _chatHub.Broadcast(msg);
        }
    });

    var writeTask = Task.Run(async () =>
    {
        await foreach (var msg in _chatHub.GetMessagesAsync(context.CancellationToken))
        {
            await responseStream.WriteAsync(msg);
        }
    });

    await Task.WhenAll(readTask, writeTask);
}

Bidirektionales Streaming vom Client aus

Senden und empfangen Sie gleichzeitig, indem Sie zwei Tasks starten – einen zum Schreiben von Nachrichten und einen zum Lesen von Antworten.

using var call = client.Chat();

// Read task
var readTask = Task.Run(async () =>
{
    await foreach (var msg in call.ResponseStream.ReadAllAsync())
        Console.WriteLine($"{msg.User}: {msg.Content}");
});

// Write task
while (Console.ReadLine() is string text && text != "exit")
{
    await call.RequestStream.WriteAsync(
        new ChatMessage { User = "Alice", Content = text,
                          SentAt = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() });
}
await call.RequestStream.CompleteAsync();
await readTask;

Flusssteuerung und Rückdruck

HTTP/2 verfügt über eine integrierte Flusssteuerung. Wenn der Verbraucher langsam ist, pausiert gRPC den Sender automatisch und verhindert so einen Speicherüberlauf. Puffern Sie Nachrichten nicht manuell, sondern überlassen Sie dies dem Stream.

// DO: Let gRPC flow control handle backpressure
await foreach (var chunk in requestStream.ReadAllAsync(ct))
{
    await ProcessChunkAsync(chunk); // naturally paced
}

// DON'T: Buffering all chunks defeats flow control
var allChunks = new List<FileChunk>();
await foreach (var chunk in requestStream.ReadAllAsync(ct))
    allChunks.Add(chunk); // may OOM on large uploads

Halbseitiges Schließen und Stream-Abschluss

Beim bidirektionalen Streaming kann jede Seite ihre Schreibseite schließen und weiterhin von der anderen Seite lesen. Dadurch kann der Client signalisieren, dass er „das Senden beendet“ hat, während er weiterhin auf Serverantworten wartet.

// Client signals it's done sending
await call.RequestStream.CompleteAsync(); // half-close

// Continue reading server responses after half-close
await foreach (var reply in call.ResponseStream.ReadAllAsync())
{
    Console.WriteLine(reply.Content);
}

Channels für threadsicheres bidirektionales Streaming verwenden

Kombinieren Sie System.Threading.Channels mit bidirektionalem Streaming, um ein übersichtliches Producer-Consumer-Muster zu erhalten, das Nachrichten sicher an viele Listener verteilt.

private readonly Channel<ChatMessage> _broadcast =
    Channel.CreateUnbounded<ChatMessage>();

// Producer: receives from each client stream
public async Task ReceiveLoopAsync(
    IAsyncStreamReader<ChatMessage> reader, CancellationToken ct)
{
    await foreach (var msg in reader.ReadAllAsync(ct))
        await _broadcast.Writer.WriteAsync(msg, ct);
}

// Consumer: writes to each client's response stream
public async Task SendLoopAsync(
    IServerStreamWriter<ChatMessage> writer, CancellationToken ct)
{
    await foreach (var msg in _broadcast.Reader.ReadAllAsync(ct))
        await writer.WriteAsync(msg);
}

Praxisbeispiel: Echtzeit-Analysepipeline

Ein Datenerfassungsdienst nimmt einen Stream von Telemetrieereignissen aus IoT-Geräten entgegen und streamt aggregierte Statistiken in Echtzeit zurück – ein idealer Anwendungsfall für bidirektionales Streaming.

service TelemetryService {
  rpc StreamTelemetry(stream TelemetryEvent) returns (stream AggregatedStats);
}

// Server implementation sends a rolling aggregate every 100 events:
public override async Task StreamTelemetry(
    IAsyncStreamReader<TelemetryEvent> requests,
    IServerStreamWriter<AggregatedStats> responses,
    ServerCallContext context)
{
    int count = 0;
    double total = 0;
    await foreach (var e in requests.ReadAllAsync(context.CancellationToken))
    {
        total += e.Value;
        if (++count % 100 == 0)
            await responses.WriteAsync(
                new AggregatedStats { Count = count, Average = total / count });
    }
}

Kurze Prüfung

Beim Client-Streaming: Wann sendet der Server seine einzelne Antwort?

Zusammenfassung: Client-Streaming und bidirektionales Streaming

Wichtige Erkenntnisse:

  • Client-Streaming: Der Client sendet viele Nachrichten, der Server eine Antwort – ideal für Datei-Uploads
  • Bidirektionales Streaming: Beide Seiten senden unabhängig voneinander – ideal für Chats und Live-Datenströme
  • Verwenden Sie ReadAllAsync(), um Streams mit await foreach zu verarbeiten
  • Rufen Sie RequestStream.CompleteAsync() auf, um die Schreibseite des Clients halbseitig zu schließen
  • Die HTTP/2-Flusssteuerung übernimmt den Rückdruck automatisch – puffern Sie nicht den gesamten Stream
  • System.Threading.Channels eignet sich gut für bidirektionales Streaming in Szenarien mit Nachrichtenverteilung

Häufig gestellte Fragen

Ist die Lektion „Client- und bidirektionales Streaming“ kostenlos?

Ja — der vollständige Text von „Client- und bidirektionales Streaming“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des C# Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der C# Academy-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Client- und bidirektionales Streaming“?

Erstellen Sie Client-Streaming- und vollduplexe bidirektionale Streaming-Kanäle für Szenarien mit hohem Datendurchsatz. Du übst C# Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um C# Academy zu starten?

Keine Vorkenntnisse erforderlich. C# Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 3 von 4.

Wie lange dauert die Lektion „Client- und bidirektionales Streaming“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser C# Academy-Lektion Code schreiben und ausführen?

Ja. Jede C# Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. Grundlagen von gRPC und Protobuf
  2. Unäre und serverseitige Streaming-RPCs
  3. Client- und bidirektionales Streaming
  4. Deadlines, Abbruch und Interceptors
← Zurück zu C# Academy