0Pricing
C# Academy · Lezione

Streaming dal client e bidirezionale

Costruisca canali di streaming dal client e di streaming bidirezionale full-duplex per scenari ad alta velocità di trasferimento.

Streaming dal client e bidirezionale è una lezione C# Academy gratuita su CoddyKit. Questa è la lezione 3 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento C# Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso C# Academy include 4 lezioni in totale.

Streaming dal client e streaming bidirezionale

Oltre alle RPC unarie e allo streaming dal server, gRPC supporta lo streaming dal client (il client invia molti messaggi e il server risponde una sola volta) e lo streaming bidirezionale (entrambi i lati inviano più messaggi simultaneamente sulla stessa connessione).

Streaming dal client: definizione Proto

Aggiunga la parola chiave stream prima del tipo di richiesta. Il client invia una sequenza di messaggi e, al termine, il server restituisce una singola risposta.

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; }

Implementare lo streaming dal client sul server

Utilizzi IAsyncStreamReader<T> per leggere i messaggi del client non appena arrivano. Chiami MoveNext() oppure esegua l'iterazione con 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 };
}

Chiamare lo streaming dal client

Apra la chiamata di streaming, scriva i messaggi con RequestStream.WriteAsync(), quindi segnali il completamento con CompleteAsync(). Attenda la risposta.

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");

Streaming bidirezionale: definizione Proto

Aggiunga stream su entrambi i lati per ottenere una comunicazione full-duplex. Sia il client sia il server possono inviare messaggi in qualsiasi momento, indipendentemente l'uno dall'altro.

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

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

Implementare lo streaming bidirezionale sul server

Legga da IAsyncStreamReader e scriva in IServerStreamWriter contemporaneamente. Utilizzi Task.WhenAll per eseguire entrambi i cicli simultaneamente.

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);
}

Streaming bidirezionale dal client

Invii e riceva messaggi contemporaneamente avviando due task: uno per la scrittura dei messaggi e uno per la lettura delle risposte.

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;

Controllo del flusso e backpressure

HTTP/2 dispone di un controllo del flusso integrato. Se il consumer è lento, gRPC mette automaticamente in pausa il mittente, evitando l'esaurimento della memoria. Non memorizzi manualmente i messaggi in un buffer: lasci che sia il flusso a gestirli.

// 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

Chiusura a metà e completamento del flusso

Nello streaming bidirezionale, ciascun lato può chiudere la propria metà di scrittura continuando a leggere dall'altro lato. In questo modo il client può segnalare di aver terminato l'invio, continuando ad attendere le risposte del server.

// 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);
}

Utilizzare i channel per uno streaming bidirezionale thread-safe

Combini System.Threading.Channels con lo streaming bidirezionale per ottenere un modello produttore-consumatore chiaro, che distribuisce in modo sicuro i messaggi a numerosi listener.

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);
}

Caso reale: pipeline di analisi in tempo reale

Un servizio di acquisizione dati accetta un flusso di eventi di telemetria dai dispositivi IoT e restituisce in streaming statistiche aggregate in tempo reale: un caso d'uso perfetto per lo streaming bidirezionale.

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 });
    }
}

Verifica rapida

Nello streaming dal client, quando invia il server la sua singola risposta?

Riepilogo: streaming dal client e bidirezionale

Punti chiave:

  • Streaming dal client: il client invia molti messaggi e il server invia una risposta — ideale per il caricamento di file
  • Streaming bidirezionale: entrambi i lati inviano messaggi indipendentemente — ideale per chat e flussi di dati in tempo reale
  • Utilizzi ReadAllAsync() per consumare i flussi con await foreach
  • Chiami RequestStream.CompleteAsync() per chiudere a metà il lato di scrittura del client
  • Il controllo del flusso di HTTP/2 gestisce automaticamente la backpressure: non memorizzi in un buffer interi flussi
  • System.Threading.Channels si abbina bene allo streaming bidirezionale negli scenari di distribuzione a più destinatari

Domande Frequenti

La lezione «Streaming dal client e bidirezionale» è gratuita?

Sì — il testo completo di «Streaming dal client e bidirezionale» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso C# Academy, passa a CoddyKit PRO. Il corso C# Academy include 4 lezioni in totale.

Cosa imparerò in «Streaming dal client e bidirezionale»?

Costruisca canali di streaming dal client e di streaming bidirezionale full-duplex per scenari ad alta velocità di trasferimento. Eserciti C# Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare C# Academy?

Non è richiesta alcuna esperienza precedente. C# Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 3 di 4.

Quanto tempo richiede la lezione «Streaming dal client e bidirezionale»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione C# Academy?

Sì. Ogni lezione C# Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Fondamenti di gRPC e Protobuf
  2. RPC unarie e streaming dal server
  3. Streaming dal client e bidirezionale
  4. Scadenze, annullamento e interceptor
← Torna a C# Academy