Gestão da conexão TCP
Como já falámos, o protocolo TCP é orientado à conexão. Concretamente o que isto quer dizer é que antes de qualquer comunicação o TCP estabelece uma comunicação entre o Browser e o servidor Web.
Também já vimos na camada aplicação, que existem conexões persistentes e não persistentes e conexões pare e espere e paralelas.
Iniciar a conexão
Quando o Browser envia um pacote para o socket, o TCP começa a estabelecer a ligação com o servidor. Para isso envia um pacote sem dados com indicação do seu porto de conexão, com indicação do seu número de sequência (35 p.e., mas escolhido de forma aleatória para cada conexão) e implementa a flag SYN a 1, é sinal de que pretende estabelecer ligação com o servidor WEB. Envia a mensagem para o Well Known Port do servidor específico para HTTP, o porto 80 (Figura 1 nota 1).
O servidor, ao perceber que tem uma mensagem vai lê-la e verifica que é um Browser a dizer-lhe que quer estabelecer uma conexão com ele, o que verifica pelo estado de SYN a 1. O servidor então aloca buffers e variáveis para esta conexão, incrementa o número de sequência escolhido pelo Browser e coloca-o no campo do número de reconhecimento(35+1=36), escolhe aleatoriamente o seu número de sequência (p.e. 57), preenche com ele o campo do número de sequência e mantém a flag SYN a 1. Depois envia a mensagem ao Browser, também sem dados (Figura 1 nota 2).
Quando o Browser recebe esta mensagem do servidor lê-a desta maneira:
“Aceito estabelecer ligação contigo, o meu número de sequência é 57 e espero um pacote com um número de sequência igual ao meu número de reconhecimento”.
Então, aloca buffer e variáveis a esta conexão, coloca SYN a 0, coloca no seu campo de número sequencial o valor do campo de reconhecimento do servidor (36), coloca no seu campo do número de reconhecimento o valor do número sequencial do servidor incrementado (57+1=58) e envia o pacote ao servidor (Figura 1 nota 3).
Chama-se ao que acabámos de descrever uma apresentação de 3 vias (three way hand-shake) e é caracterizada por uma sequência SYN → SIN/ACK → ACK:
- SYN – O cliente envia um SYN o servidor e passa a conexão ao estado de SYN_SENT.
- SYN/ACK – O servidor responde com SYN/ACK e passa a conexão ao estado de SYN_RCVD.
- ACK – O cliente envia um ACK de volta ao servidor e passa a conexão ao estado de ESTABLISHED, que ao recebê-lo passa também a conexão ao estado de ESTABLISHED.
Na 3ª mensagem o Browser já pode enviar dados, concretamente os da requisição HTTP.
A conexão TCP é uma conexão ponto a ponto, entre dois pontos terminais, no nosso caso Browser e Servidor Web. Por isso este protocolo não serve para mensagens tipo multicast.
Terminar a conexão
Tanto o Browser como o servidor podem acabar a conexão.
Vamos, tal como na Figura 1, admitir que é o Browser que fecha a conexão. Assim, a camada aplicação envia um comando de fecho à camada transporte.
Então, a camada transporte envia ao servidor um pacote com a flag FIN implementada a 1, passando ao estado de FIN_WAIT_1 (Figura 1 nota 4).
Isto indica ao servidor a sua intenção de fechar a conexão, ao que o servidor responde com um pacote de ACK (Acknowledge). O servidor passa ao estado de CLOSE_WAIT (Figura 1 nota 5).
Quando o browser recebe este pacote passa ao estado de FIN_WAIT_2. Não faz mais nada. Só fica à espera.
O servidor envia um segundo pacote, esse já com a flag FIN implementada a 1, o que indica ao browser que vai fechar a conexão, passando ao estado de LAST_ACK até à receção do último ACK do cliente (Figura 1 nota 6).
O Browser, quando recebe este pacote do servidor, devolve um pacote de ACK e passa ao estado de TIME_WAIT (Figura 1 nota 7). O tempo durante o qual o Browser se mantém neste estado é tipicamente de 30 segundos, 1 ou 2 minutos, dependendo das especificações das aplicações. Concluído este período o Browser liberta o porto de conexão, o buffer e as variáveis de estado e passa a conexão ao estadp de CLOSED.
Quando recebe o pacote com ACK o servidor liberta o buffer, as variáveis de estado e o porto, no caso em que este seja específico desta conexão e passa ao estado de LISTENING.
Os estados que descrevemos são definidos para fecho ativo e fecho passivo. Ativo é aquele que inicia a terminação e passivo o que aceita. No caso da Figura 1, o browser é ativo e o servidor passivo.
Durante a conexão (sessão)
Como dissemos antes, o TCP garante uma transferência confiável de dados, o que representa uma tarefa de grande responsabilidade pois não só transfere dados que podem exigir enorme rigor como a transmissão dos mesmos vai ser feita por uma camada não confiável, onde os pacotes se podem perder, deteriorar ou desorganizar, o que lhe será perfeitamente indiferente.
Como também já vimos, um pacote com ACK implementado representa uma confirmação por parte do destinatário de que um determinado pacote chegou em condições.
O TCP usa reconhecimentos cumulativos, isto é, quando recebe um ACK para um determinado pacote, estando por confirmar um ou mais pacotes enviados antes deste, parte do princípio que aqueles também chegaram em condições.
E porquê tanta certeza?
Porque o TCP foi baseado no protocolo GBN (Go-Back-N). No protocolo GBN, o destinatário descarta qualquer pacote recebido fora de ordem, isto é, quando recebe um pacote antes de ter recebido o imediatamente anterior, deita-o fora e envia um pacote ACK ao remetente referente ao último pacote que recebeu, em repetição. Isto garante ao remetente que, quando recebe um ACK de um pacote para ele adiantado, pois faltam-lhe ACKs de pacotes anteriores, o destinatário já recebeu esses pacotes. A repetição do ACK para o mesmo pacote, informa o remetente de qual foi o último pacote que o destinatário recebeu por ordem.
E funciona assim porque o TCP não usa reconhecimentos negativos ou NACK (Not ACKnowledged).
Mas nada como começarmos por olhar para um gráfico de uma transmissão correta representado na Figura 2, em que todos os pacotes chegam ao seu destino por ordem e o respetivo reconhecimento é recebido pelo remetente antes de se esgotar a temporização.
Ao olhar para o gráfico saltam à vista três conceitos de que ainda não falámos: a janela de receção, o temporizador (timeout) e a forma de evolução dos números sequenciais e de reconhecimento.
Janela de Receção
A janela de receção é usada pelo TCP como um controlador de fluxo de pacotes. Como já vimos, em ambos os lados da conexão são criados buffers para armazenamento temporário de dados.
Do lado do destinatário a aplicação pode processar os dados dos pacotes a uma velocidade inferior ao seu envio pelo remetente, o que originará uma inundação do buffer do destinatário. Para que isso não aconteça, o destinatário fornece ao remetente, no cabeçalho do pacote, o tamanho da janela de receção, calculado pelo mesmo em função da velocidade de entrega à aplicação e espaço no buffer, por forma a que o buffer nunca atinja o estado de cheio. Portanto, a janela de receção é um valor que o remetente deve ter em conta no envio de pacotes, para não saturar o buffer do destinatário.
No caso do gráfico, o valor da janela de receção (N) é de 4 pacotes, o que só funciona a título exemplificativo. Assim, uma vez atingido o número de 4 pacotes enviados e ainda não reconhecidos, o remetente pára a emissão. Conforme vão chegando reconhecimentos dos pacotes enviados, a janela vai deslizando e permitindo o envio de novos pacotes que entretanto estão em lista de espera.
Durante uma transmissão, o valor da janela de receção é permanentemente ajustado de acordo com diversas análises que vão sendo feitas por ambos os lados, sendo o seu valor sempre inscrito no cabeçalho do pacote enviado.
A janela de receção vai ser também utilizada pelo controlo de congestionamento do TCP, como forma de regular o fluxo de pacotes em função das indicações que o próprio for fornecendo. Já lá vamos.
Timeout
O Timeout (temporizador) é um valor temporal que, uma vez esgotado origina a repetição de um pacote para o qual ainda não tenha recebido um ACK. No TCP só existe um Temporizador, que é reiniciado de cada vez que o reconhecimento esperado chega, passando o seu início para o pacote seguinte.
O temporizador é calculado em função do RTT (Round Trip Time), que é o tempo que medeia entre o envio e a receção do reconhecimento de um pacote.
O TCP vai permanentemente fazendo o cálculo da média do valor de RTT, uma vez que cada pacote tem um RTT diferente. Calcula também o desvio padrão para o valor médio de RTT. Finalmente o valor do temporizador é obtido somando ao RTT médio o quádruplo do desvio padrão.
Números de sequência e números de reconhecimento.
Relembramos que o número de sequência foi escolhido aleatoriamente pelo Browser aquando da primeira mensagem de apresentação e que o número de reconhecimento foi escolhido aleatoriamente pelo Servidor Web aquando da resposta ao Browser.
Vamos agora analisar a regra de evolução destes números durante a sessão. Vamos ter em conta que o TCP vê em curso na transmissão uma cadeia de bytes de dados em lugar de segmentos.
O Browser, quando finalizou a apresentação em 3 vias enviou ao servidor um número de sequência de 36 e um número de reconhecimento de 58. Diz o browser:
“O meu bloco de dados começa no byte 36 e aguardo um pacote teu que só reconheço se o bloco de dados começar no byte 58”.
Admitamos que incluiu nesse pacote o pedido GET da página desejada e que esta mensagem GET não continha dados, transportando portanto 0 Bytes em dados.
Admitamos que o servidor tem um total de 5.000 bytes de dados úteis (mensagem HTTP) para transmitir e que os vai dividir em segmentos com 500 bytes no campo de dados.
Terá portanto que enviar 10 segmentos. O tamanho de segmento (MSS) que será de 520 bytes (não há opções), foi previamente combinado entre os dois terminais.
Assim sendo o servidor vai numerar os bytes da cadeia de dados a transmitir. O primeiro pacote inicia-se a 0, o segundo a 500, o último a 4.500. Mas, como o valor do número de reconhecimento transmitido pelo browser foi 58, isto é, o browser disse ao servidor que esperava que o próximo pacote que ele enviasse começasse no byte 58, o servidor atribui aos segmentos os números de sequência 58, 558, 1058,… e 4558.
Agora o servidor já pode responder ao browser. Soma o número de bytes recebidos ao número que o browser diz corresponder ao início do seu campo de dados (36+0=36) e envia-lhe o primeiro segmento com dados. Nesse segmento indica que o seu número de sequência é 58 e o seu número de reconhecimento é 36. Diz o servidor:
“Aqui vai um pacote em que os dados começam no byte 58. Fico à espera que me envies um pacote que só reconheço se o bloco de dados se iniciar com o byte 36.”
Quando o browser recebe este pacote, verifica que o mesmo contém 500 bytes no campo de dados. Soma o valor do número de sequência ao número de bytes recebido e fica a saber que o próximo campo de dados que receber do servidor terá um número de sequência igual a 558. Então envia-lhe um pacote com ACK, com número de sequência 36 e com número de reconhecimento 558. Diz o browser:
“Recebi o teu pacote com 500 bytes. O meu bloco de dados começa no byte 36 e o próximo pacote que espero de ti só reconheço se o bloco de dados começar no byte 558”.
O servidor recebe o ACK e verifica que o bloco de dados do pacote começa a 36. Como não há dados, o próximo número de sequência a esperar do browser será 36. Então prepara o segundo pacote de dados, que já tinha numerado como começando no byte 558 e envia-o para o browser. Diz o servidor para o browser:
“Cá vai mais um pacote com dados que começa no byte 558. Espero um pacote teu que só reconheço se começar no byte 36”.
E assim sucessivamente até à finalização da conexão. Podemos então resumir a regra da seguinte forma:
- O número de sequência corresponde ao byte da corrente de dados em envio em que se inicia o bloco de dados no emissor desse segmento.
- O número de reconhecimento representa o número do byte da corrente de dados em receção em que o emissor espera que comece o bloco de dados que o destinatário lhe vai enviar de seguida. Só assim reconhecerá o pacote.
É assim que o TCP identifica os pacotes da mesma comunicação e os pacotes de ACK respetivos, no meio de uma confusão de pacotes de várias transmissões em que os pacotes podem andar misturados e desordenados.
Protocolo GBN ou Go-Back-N
Com base no gráfico apresentado na Figura 3, vamos desenvolver uma melhor análise deste protocolo.
Admitamos que a janela de receção é igual ou superior a 10 pacotes. Neste caso o remetente descarrega de imediato na rede os 10 pacotes entregues pela aplicação.
Suponhamos que o 2º pacote não chega ao destino. Baseado unicamente no reconhecimento cumulativo o destinatário não tem forma de dizer ao remetente que lhe falta um pacote na cadeia. A única forma é pelo reconhecimento em duplicado (em triplicado e veremos porquê) do último pacote recebido, que é o primeiro. Claro que quando tal reconhecimento chega ao remetente, já todos os pacotes estão na rede e provavelmente recebidos em condições pelo destinatário.
O que o protocolo Go-Back-N faz neste caso é exatamente o que o nome diz (anda para trás N), isto é, o remetente volta a enviar todos os pacotes desde o último reconhecido e pelo valor da janela de receção, portanto reenvia todos os 9 pacotes que seguem o primeiro.
Com janelas de receção de milhares de pacotes e não se torna difícil visualizar, com o atual volume de pacotes a circular, o congestionamento que isto iria causar.
Do gráfico que ilustra este protocolo GBN ressalta uma observação: sempre que um pacote enviado é reconhecido, o temporizador é reiniciado e o seu início passa para o ponto de emissão do próximo pacote a ser emitido. Para além de ser dinâmico em dimensão, também vai deslizando conforme os reconhecimentos.
Nesta figura vê-se que a partir do pacote perdido não há mais pacotes reconhecidos, pois todos os ACK são feitos para o pacote 1. Conforme a seta indica, ao fim do 3º ACK para o pacote 1 o remetente começa a repetir os pacotes para além do pacote 1 (já reconhecido) até ao limite da dimensão da janela de receção (neste caso superior aos pacotes que faltam transmitir).
Todos os pacotes para além do pacote em falta, embora devidamente recebidos pelo destinatário, são descartados e geram um ACK para o último pacote recebido pela ordem.