Sabemos que TCP é um protocolo orientado a conexão e portanto é necessário controlar a quantidade de dados que já foi transmitida/recebida, o originador envia um pouquinho de dados e o destino precisa confirmar que o recebeu, quando essa confirmação não é recebida a tempo, o dado é reenviado.

 

O protocolo TCP utiliza um método de controle de congestionamento de tráfego chamado ‘janelamento’, esse janelamento informa a quantidade de dados que o host é capaz de receber antes de poder processar esses dados e confirmar o recebimento a quem os enviou.

 

Esse campo no cabeçalho TCP possui 2 bytes, o que nativamente permite um janelamento de até 65,535 bytes.

 

No artigo utilizo os termos ‘janelamento, window size e buffer’, porém estarei sempre me referindo a mesma coisa.

 

Vamos considerar o exemplo abaixo, o host 192.168.1.46 iniciando uma sessão com 142.250.79.195.

 

 

 

No SYN inicial, o IP 192.168.1.46 informa ao IP 142.250.79.195 qual o tamanho de seu buffer, podemos ver que é 64.240, sendo assim o IP 142.250.79.195 poderá enviar até 64.240 bytes para 192.168.1.46 antes que receba/espere a confirmação do recebimento desses dados via um TCP ACK.

 

 

 

 

A comunicação foi iniciada por 192.168.1.46, porém no momento que a sessão é estabelecida ambas as pontas podem enviar e receber dados, então é necessário também conhecer o valor de janelamento do servidor. No sentido inverso, o servidor 142.250.79.195 informa que o seu buffer é de 65.535 bytes.

 

 

 

 

O último pacote antes da sessão ser estabelecida é o ‘ACK’, nele podemos ver que o tamanho do janelamento foi alterado para 512, porém o tamanho calculado do buffer (‘Calculated Window Size’) é de 131.072 bytes, esse é o real/efetivo valor da janela, vamos brevemente entender o que está acontecendo aqui.

 

 

 

Atualmente vivemos numa época que conexões de alta velocidade são realidade e os servidores que hospedam as aplicações estão cada vez mais avançados, isso deixa claro que um buffer de até 65.535 bytes é relativamente pequeno, porém conforme mencionado no início do artigo (e demonstrado abaixo), o campo ‘Window Size’ possui apenas 2 bytes, nos limitando a esse valor. Para conseguirmos informar o destino/origem que possuímos um buffer maior que 65535 bytes é utilizado uma das opções do TCP chamado ‘TCP Window Scaling’, esse valor informa o endpoint que o seu buffer é na verdade ‘x’ vezes o anunciado, no exemplo acima o host 192.168.1.46 anuncia que seu buffer é de efetivamente, 131.073 bytes, ou 512 x 256.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Para o nosso exemplo pode não parecer muito, mas utilizando esse campo é possível por padrão anunciar um valor no fator de escala de até 16.384, isso permite anúncio de buffers de até 1Gb. Algumas situações permitem buffers ainda maiores.

 

Importante salientar que inicialmente o TCP não possuía essa facilidade, o que mudou depois da RFC 1323 (https://www.ietf.org/rfc/rfc1323.txt), a qual foi sendo adotada aos poucos por fabricantes e desenvolvedores.

 

Vamos ver abaixo um exemplo do controle de congestionamento/janelamento em ação.

 

No nosso exemplo, um client ‘X’ envia um TCP ACK para ‘X’ informando que no momento é capaz de receber até 24.320 bytes.

 

 

O servidor nesse momento começa a calcular a quantidade de dados que enviou, se não receber nenhuma mensagem da origem atualizando esses dados, ele irá parar de mandar mais informações assim que o buffer do destino encher.

 

Isso é possível ser verificado no wireshark, abaixo é possível ver uma mensagem do servidor notificando que o buffer informado pela origem está cheio. A partir desse momento o servidor não enviará mais dados ao client até receber a confirmação que ele recebeu as informações que foram enviadas até agora e que seu buffer tem espaço livre novamente.

 

 

 

Podemos ver abaixo um TCP ACK do client informando que seu buffer está esgotado. Enquanto o buffer do usuário não ser liberado TCP keepalives são trocados entre as duas pontas para manter a sessão estabelecida.

 

 

Assim que o buffer é liberado, o client envia uma mensagem TCP Window Update para o servidor informando que novamente possui espaço livre no buffer e está pronto para receber dados, isso se repete diversas vezes até que os dados solicitados sejam devidamente recebidos/transferidos.

 

 

É importante sabermos enxergar esse tipo de situação, pois em teoria, o processo de limpeza de buffer deveria ser rápido o suficiente para acomodar uma rápida transmissão de dados, mas muitas vezes servidores mal dimensionados ou aplicações mal otimizadas (entre outros motivos) podem gerar esse problema de forma corriqueira, na percepção do usuário houve um ‘engasgo’ na rede (mesmo a rede não sendo a culpada), isso se agrava ainda mais quando a quantidade de dados transmitida é alta, o usuário vai frequentemente sentir esse problema e nós como analistas de rede receberíamos um report informando que a ‘rede está lenta’ ou ‘a aplicação x está lenta’, etc...

 

Por fim, é importante reforçar que esse controle se aplica apenas a TCP, o protocolo UDP não possui/realiza esse tipo de controle, ele continua enviando dados sem se preocupar se o destino está conseguindo processá-los ou não.