Ciao a tutti e bentrovati.

Quest’oggi parliamo del protocollo TCP. Come ben sapete, la sua proprietà più importante è l’affidabilità, cioè è presente un meccanismo di “delivery receipt” che permette al ricevitore di informare il mittente se ha ricevuto correttamente i dati, in modo da poterli ritrasmettere nel caso in cui andassero persi. Esiste una bibliografia vastissima sull’argomento, quindi eviterò di annoiarvi e mi limito ad un semplice ma importante esempio di teoria.

Pacchetto 1 - Mittente (10.0.0.15)
Ipv4: Src = 10.0.0.15, Dest = 192.168.0.1, Next Protocol = TCP, Total IP Length = 81
Tcp: SrcPort=49278, DstPort=HTTPS(443), PayloadLen=41, Seq=45498, Win=255

Pacchetto 2 - Destinatario (192.168.0.1)
Ipv4: Src = 192.168.0.1, Dest = 10.0.0.15, Next Protocol = TCP, Total IP Length = 40
Tcp: SrcPort=HTTPS(443), DstPort=49278, PayloadLen=0, Ack=45539, Win=32768

Il mittente invia un segmento con Sequence Number 45498. Tale parametro (Seq) non indica il numero del pacchetto, ma indica il sequence number del primo byte di dati contenuto nel pacchetto. Essendoci 41 bytes di payload in questo segmento,il mittente sta trasmettendo i bytes dello stream dal 45498esimo al 45538esimo.

Ricevuto il pacchetto, il destinatario invia un ACK. Per confermare l’avvenuta ricezione, il campo ACK deve essere popolato con il sequence number del prossimo byte che il ricevente si aspetta di ricevere (in questo caso quindi 45539)

Qualora il pachetto con i dati fosse andato perso, il rcevente continuerebbe a rispondere “ACK 45498” richiedendo il pacchetto con tale sequence number. Il mittente si accorgerebbe che il ricevitore sta richiedendo un pacchetto già inviato e procederà con la ritrasmissione. Questa garanzia di affidabilità rende il TCP un protocollo utilizzato in maniera vastissima in tutto il mondo. ma a che costo? Ogni volta, “sprechiamo” 40 bytes (20 di header IP e 20 di header TCP) per un ACK. Questo potrebbe avere notevoli conseguenze di performance specialmente per pacchetti con payload piccolo come quello del nostro esempio. Avremmo che una grande percentuale del traffico che c’è in rete è dedicato agli ACK, nella fattispecie ben il 33% del numero totale di bytes che viaggiano sul cavo!

Nella implementazione del TCP in Windows, si è pensato di utilizzare il meccanismo dei Delay ACKs. L’idea di fondo è: minimizzare il numero di ACK, mandando una conferma di corretta ricezione unica per un gruppo di pacchetti ricevuti e non sempre per pacchetto singolo.

By default quindi, Windows aspetta che siano ricevuti almeno due pacchetti prima di inviare un ACK. Matematicamente, questo permette di ridurre del 50% lo spreco dei bytes inviati sul canale per inviare ACK.

La conseguente domanda è legittima: cosa succede se il mittente manda un solo pacchetto? Qualora venga inviato un pacchetto singolo, si attiva un timer (DelayACK timer). By default, se non vengono ricevuti ulteriori pacchetti, un ACK viene inviato dopo 200 millisecondi.

Entrambi questi valori sono personalizzabili tramite chiavi di registro sotto HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Tcpip\Parameters

TcpAckFrequency specifica il numero di pacchetti che possono essere ricevuti prima di inviare ACK. Il valore di default è 2 e il range di valori validi è 0-255.

TcpDelAckTicks specifica il valore del DelayACK timer, espresso in centinaia di millisecondi. Il valore di default è 2, e il range di valori validi è 2-6.

Microsoft sconsiglia di modificare questi valori prima di aver effettuato un accurato studio dell’ambiente e dell’infrastrutura di rete!!

Ho spesso lavorato su problemi di “lentezza” di trasferimenti dati TCP in cui poteva sembrare che i 200ms di attesa, ripetuti molte molte volte all’interno della stessa connessione, fossero la componente principale dei ritardi accumulati. Questo si verifica spesso quando i dati vengono inviati “a spezzoni”, cioè con pacchetti singoli oppure con sottoinsiemi di pacchetti di numeri dispari, che devono aspettare necessariamente l’ACK prima di poter continuare con la trasmissione (penso ad esempio a sistemi transazionali). Per questo motivo, potrebbe avere senso diminuire il valore di TcpAckFrequency a 1 per mandare un ACK di ogni pacchetto, al costo di consumare molta più banda ma minimizzando I ritardi. Al contrario, mandando ACK ogni volta, potremmo causare congestioni e anche se non abbiamo dei palesi “buchi” di 200ms, avremo riduzione della quantità di dati inviata e quindi un tempo totale di trasmissione fisica ancora maggiore. è assolutamente determinante quindi analizzare il comportamento dell’applicazione e la modalità in cui invia/riceve i dati, e non limitare l’analisi al livello TCP. Se fate una ricerca su internet, potrete apprezzare la incredibile varietà di risultati misurati dagli utenti nei loro tests effettuati modificando i valori di TcpAckFrequency e TcpDelAckTicks .

Condivido con voi una regola d’oro per il tuning:

TcpAckFrequency * MSS < Recv_Window

Il prodotto tra il valore di TcpAckFrequency e la Maximum Segment size del TCP (MSS, dimensione di un pacchetto al netto degli header IP e TCP) deve essere inferiore al valore della finestra TCP Window comunicata dal ricevente. Se così non fosse, il mittente potrebbe bloccare l’invio perchè la dimensione massima della finestra è stata raggiunta… ma gli ACK non sono ancora arrivati perchè stiamo aspettando altri pacchetti per mandare un ACK cumulativo!

In conclusione, Microsoft mette a disposizione questi strumenti di precisione per andare a lavorare sul tuning a bassissimo livello, lavorando su bytes e milisecondi. Per trovare le migliori applicazioni, non ci resta che testare!

Ciao a tutti e alla prossima

Stefano Gagliardi
Support Engineer
Microsoft Enterprise Platform Support