4. Controllo di flusso
4. Controllo di flusso
I ricevitori devono limitare la quantità di dati che sono tenuti a bufferizzare, al fine di impedire a un mittente veloce di sopraffarli o a un mittente malintenzionato di consumare una grande quantità di memoria. Per consentire a un ricevitore di limitare gli impegni di memoria per una connessione, i flussi sono controllati sia individualmente che attraverso una connessione nel suo insieme. Un ricevitore QUIC controlla la quantità massima di dati che il mittente può inviare su un flusso così come su tutti i flussi in qualsiasi momento, come descritto nelle Sezioni 4.1 e 4.2.
Analogamente, per limitare la concorrenza all'interno di una connessione, un endpoint QUIC controlla il numero cumulativo massimo di flussi che il suo peer può iniziare, come descritto nella Sezione 4.6.
I dati inviati nei frame CRYPTO non sono controllati allo stesso modo dei dati del flusso. QUIC si affida all'implementazione del protocollo crittografico per evitare il buffering eccessivo di dati; vedere [QUIC-TLS]. Per evitare il buffering eccessivo a più livelli, le implementazioni QUIC DOVREBBERO fornire un'interfaccia per l'implementazione del protocollo crittografico per comunicare i suoi limiti di buffering.
4.1 Controllo di flusso dei dati
QUIC utilizza uno schema di controllo di flusso basato su limiti in cui un ricevitore pubblicizza il limite del totale di byte che è preparato a ricevere su un dato flusso o per l'intera connessione. Questo porta a due livelli di controllo di flusso dei dati in QUIC:
-
Controllo di flusso del flusso, che impedisce a un singolo flusso di consumare l'intero buffer di ricezione per una connessione limitando la quantità di dati che possono essere inviati su ciascun flusso.
-
Controllo di flusso della connessione, che impedisce ai mittenti di superare la capacità del buffer di un ricevitore per la connessione limitando il totale dei byte di dati del flusso inviati nei frame STREAM su tutti i flussi.
I mittenti NON DEVONO inviare dati in eccesso rispetto a uno dei due limiti.
Un ricevitore stabilisce limiti iniziali per tutti i flussi tramite parametri di trasporto durante l'handshake (Sezione 7.4). Successivamente, un ricevitore invia frame MAX_STREAM_DATA (Sezione 19.10) o MAX_DATA (Sezione 19.9) al mittente per pubblicizzare limiti più grandi.
Un ricevitore può pubblicizzare un limite maggiore per un flusso inviando un frame MAX_STREAM_DATA con l'ID di flusso corrispondente. Un frame MAX_STREAM_DATA indica l'offset di byte assoluto massimo di un flusso. Un ricevitore potrebbe determinare l'offset di controllo di flusso da pubblicizzare in base all'offset corrente dei dati consumati su quel flusso.
Un ricevitore può pubblicizzare un limite maggiore per una connessione inviando un frame MAX_DATA, che indica il massimo della somma degli offset di byte assoluti di tutti i flussi. Un ricevitore mantiene una somma cumulativa di byte ricevuti su tutti i flussi, che è utilizzata per verificare violazioni dei limiti di dati di connessione o flusso pubblicizzati. Un ricevitore potrebbe determinare il limite massimo di dati da pubblicizzare in base alla somma dei byte consumati su tutti i flussi.
Una volta che un ricevitore ha pubblicizzato un limite per la connessione o un flusso, non è un errore pubblicizzare un limite più piccolo, ma il limite più piccolo non ha alcun effetto.
Un ricevitore DEVE chiudere la connessione con un errore di tipo FLOW_CONTROL_ERROR se il mittente viola i limiti di dati di connessione o flusso pubblicizzati; vedere Sezione 11 per i dettagli sulla gestione degli errori.
Un mittente DEVE ignorare qualsiasi frame MAX_STREAM_DATA o MAX_DATA che non aumenta i limiti di controllo di flusso.
Se un mittente ha inviato dati fino al limite, non sarà in grado di inviare nuovi dati ed è considerato bloccato. Un mittente DOVREBBE inviare un frame STREAM_DATA_BLOCKED o DATA_BLOCKED per indicare al ricevitore che ha dati da scrivere ma è bloccato dai limiti di controllo di flusso. Se un mittente è bloccato per un periodo più lungo del timeout di inattività (Sezione 10.1), il ricevitore potrebbe chiudere la connessione anche quando il mittente ha dati disponibili per la trasmissione. Per impedire la chiusura della connessione, un mittente limitato dal controllo di flusso DOVREBBE inviare periodicamente un frame STREAM_DATA_BLOCKED o DATA_BLOCKED quando non ha pacchetti che elicitano ACK in volo.
4.2 Aumento dei limiti di controllo di flusso
Le implementazioni decidono quando e quanto credito pubblicizzare nei frame MAX_STREAM_DATA e MAX_DATA, ma questa sezione offre alcune considerazioni.
Per evitare di bloccare un mittente, un ricevitore PUÒ inviare un frame MAX_STREAM_DATA o MAX_DATA più volte all'interno di un round trip o inviarlo abbastanza presto da consentire il tempo per la perdita del frame e il successivo recupero.
I frame di controllo contribuiscono al sovraccarico della connessione. Pertanto, inviare frequentemente frame MAX_STREAM_DATA e MAX_DATA con piccole modifiche è indesiderabile. D'altra parte, se gli aggiornamenti sono meno frequenti, sono necessari incrementi maggiori ai limiti per evitare di bloccare un mittente, richiedendo impegni di risorse maggiori al ricevitore. Esiste un compromesso tra impegno di risorse e sovraccarico nel determinare quanto grande pubblicizzare un limite.
Un ricevitore può utilizzare un meccanismo di autotuning per regolare la frequenza e la quantità di credito aggiuntivo pubblicizzato in base a una stima del tempo di round trip e al tasso al quale l'applicazione ricevente consuma i dati, simile alle implementazioni TCP comuni. Come ottimizzazione, un endpoint potrebbe inviare frame relativi al controllo di flusso solo quando ci sono altri frame da inviare, garantendo che il controllo di flusso non causi l'invio di pacchetti extra.
Un mittente bloccato non è tenuto a inviare frame STREAM_DATA_BLOCKED o DATA_BLOCKED. Pertanto, un ricevitore NON DEVE attendere un frame STREAM_DATA_BLOCKED o DATA_BLOCKED prima di inviare un frame MAX_STREAM_DATA o MAX_DATA; farlo potrebbe comportare che il mittente sia bloccato per il resto della connessione. Anche se il mittente invia questi frame, attenderli comporterà che il mittente sia bloccato per almeno un intero round trip.
Quando un mittente riceve credito dopo essere stato bloccato, potrebbe essere in grado di inviare una grande quantità di dati in risposta, risultando in congestione a breve termine; vedere Sezione 7.7 di [QUIC-RECOVERY] per una discussione su come un mittente può evitare questa congestione.
4.3 Prestazioni del controllo di flusso
Se un endpoint non può garantire che il suo peer abbia sempre credito di controllo di flusso disponibile maggiore del prodotto banda-ritardo del peer su questa connessione, il suo throughput di ricezione sarà limitato dal controllo di flusso.
La perdita di pacchetti può causare lacune nel buffer di ricezione, impedendo all'applicazione di consumare dati e liberare spazio nel buffer di ricezione.
L'invio di aggiornamenti tempestivi dei limiti di controllo di flusso può migliorare le prestazioni. L'invio di pacchetti solo per fornire aggiornamenti di controllo di flusso può aumentare il carico di rete e influenzare negativamente le prestazioni. L'invio di aggiornamenti di controllo di flusso insieme ad altri frame, come i frame ACK, riduce il costo di tali aggiornamenti.
4.4 Gestione della cancellazione del flusso
Gli endpoint devono eventualmente concordare sulla quantità di credito di controllo di flusso che è stata consumata su ogni flusso, per poter contabilizzare tutti i byte per il controllo di flusso a livello di connessione.
Alla ricezione di un frame RESET_STREAM, un endpoint smonterà lo stato per il flusso corrispondente e ignorerà ulteriori dati in arrivo su quel flusso.
RESET_STREAM termina una direzione di un flusso in modo brusco. Per un flusso bidirezionale, RESET_STREAM non ha alcun effetto sul flusso di dati nella direzione opposta. Entrambi gli endpoint DEVONO mantenere lo stato di controllo di flusso per il flusso nella direzione non terminata fino a quando quella direzione non entra in uno stato terminale.
4.5 Dimensione finale del flusso
La dimensione finale è la quantità di credito di controllo di flusso consumata da un flusso. Assumendo che ogni byte contiguo sul flusso sia stato inviato una volta, la dimensione finale è il numero di byte inviati. Più in generale, questo è uno in più dell'offset del byte con l'offset più grande inviato sul flusso, o zero se non sono stati inviati byte.
Un mittente comunica sempre in modo affidabile la dimensione finale di un flusso al ricevitore, indipendentemente da come il flusso viene terminato. La dimensione finale è la somma dei campi Offset e Length di un frame STREAM con un flag FIN, notando che questi campi potrebbero essere impliciti. In alternativa, il campo Final Size di un frame RESET_STREAM porta questo valore. Ciò garantisce che entrambi gli endpoint concordino su quanto credito di controllo di flusso è stato consumato dal mittente su quel flusso.
Un endpoint conoscerà la dimensione finale per un flusso quando la parte ricevente del flusso entra nello stato "Size Known" o "Reset Recvd" (Sezione 3). Il ricevitore DEVE utilizzare la dimensione finale del flusso per contabilizzare tutti i byte inviati sul flusso nel suo controllore di flusso a livello di connessione.
Un endpoint NON DEVE inviare dati su un flusso a o oltre la dimensione finale.
Una volta che una dimensione finale per un flusso è nota, non può cambiare. Se viene ricevuto un frame RESET_STREAM o STREAM che indica un cambiamento nella dimensione finale del flusso, un endpoint DOVREBBE rispondere con un errore di tipo FINAL_SIZE_ERROR; vedere Sezione 11 per i dettagli sulla gestione degli errori. Un ricevitore DOVREBBE trattare la ricezione di dati a o oltre la dimensione finale come un errore di tipo FINAL_SIZE_ERROR, anche dopo che un flusso è chiuso. La generazione di questi errori non è obbligatoria, perché richiedere che un endpoint generi questi errori significa anche che l'endpoint deve mantenere lo stato della dimensione finale per i flussi chiusi, il che potrebbe significare un impegno di stato significativo.
4.6 Controllo della concorrenza
Un endpoint limita il numero cumulativo di flussi in entrata che un peer può aprire. Solo i flussi con un ID di flusso inferiore a (max_streams * 4 + first_stream_id_of_type) possono essere aperti; vedere Tabella 1. I limiti iniziali sono impostati nei parametri di trasporto; vedere Sezione 18.2. I limiti successivi sono pubblicizzati utilizzando frame MAX_STREAMS; vedere Sezione 19.11. Limiti separati si applicano ai flussi unidirezionali e bidirezionali.
Se viene ricevuto un parametro di trasporto max_streams o un frame MAX_STREAMS con un valore maggiore di 2^60, ciò consentirebbe un ID di flusso massimo che non può essere espresso come intero a lunghezza variabile; vedere Sezione 16. Se uno dei due viene ricevuto, la connessione DEVE essere chiusa immediatamente con un errore di connessione di tipo TRANSPORT_PARAMETER_ERROR se il valore offensivo è stato ricevuto in un parametro di trasporto o di tipo FRAME_ENCODING_ERROR se è stato ricevuto in un frame; vedere Sezione 10.2.
Gli endpoint NON DEVONO superare il limite impostato dal loro peer. Un endpoint che riceve un frame con un ID di flusso che supera il limite che ha inviato DEVE trattare questo come un errore di connessione di tipo STREAM_LIMIT_ERROR; vedere Sezione 11 per i dettagli sulla gestione degli errori.
Una volta che un ricevitore ha pubblicizzato un limite di flusso utilizzando il frame MAX_STREAMS, pubblicizzare un limite più piccolo non ha alcun effetto. I frame MAX_STREAMS che non aumentano il limite di flusso DEVONO essere ignorati.
Come con il controllo di flusso dei flussi e della connessione, questo documento lascia alle implementazioni decidere quando e quanti flussi devono essere pubblicizzati a un peer tramite MAX_STREAMS. Le implementazioni potrebbero scegliere di aumentare i limiti quando i flussi vengono chiusi, per mantenere il numero di flussi disponibili per i peer approssimativamente costante.
Un endpoint che è incapace di aprire un nuovo flusso a causa dei limiti del peer DOVREBBE inviare un frame STREAMS_BLOCKED (Sezione 19.14). Questo segnale è considerato utile per il debug. Un endpoint NON DEVE attendere di ricevere questo segnale prima di pubblicizzare credito aggiuntivo, poiché farlo significherebbe che il peer sarebbe bloccato per almeno un intero round trip, e potenzialmente indefinitamente se il peer sceglie di non inviare frame STREAMS_BLOCKED.