Melhoramentos nos Reconhecimentos TCP

Porque esta solução não satisfazia,  o protocolo TCP sofreu vários melhoramentos na forma como garante transmissões confiáveis.

Temporizador

O temporizador, após uma repetição por se ter esgotado, duplica a sua dimensão. Se se voltar a esgotar no mesmo pacote, volta a duplicar a sua dimensão. E assim sucessivamente, até conseguir ser reconhecido sem esgotar a temporização. Cumprido o objetivo, mal passe ao pacote seguinte, o temporizador retorna ao seu valor calculado e atualizado, como sempre.

Este procedimento funciona já como uma forma primária de controlo de congestionamento. Se um pacote não é reconhecido dentro do período do temporizador é muito provavelmente porque a rede está congestionada. Ao retardar a retransmissão de pacotes o TCP evita congestionar ainda mais a rede.

Reconhecimento Seletivo

Outra modificação é a introdução do reconhecimento seletivo ou SACK (Selective ACKnowledgement). O reconhecimento seletivo corresponde à capacidade de o destinatário poder reconhecer pacotes independentemente de os mesmos chegarem ou não pela ordem, olhando simplesmente à sua integridade.

Assim sendo, o destinatário passa a poder ter na sua janela de receção pacotes enviados e reconhecidos havendo lacunas entre eles, o mesmo acontecendo com a janela do remetente.

Mas então como é que o remetente sabe que existe uma lacuna?

Agora, o TCP sempre que recebe um pacote fora de ordem mas em boas condições, emite um reconhecimento para esse pacote e um reconhecimento para o último pacote que recebeu por ordem, mantendo assim o mesmo procedimento de reconhecimento negativo para o remetente em relação ao último pacote recebido pela ordem devida, que ele deverá repetir ao fim de 3 ACKs.

Mas então como é que o TCP envia dois reconhecimentos simultaneamente?

O SACK, amplamente usado e aceite por quase todas as implementações TCP, tem que ser negociado entre os terminais remetente e destinatário (Browser e Servidor Web). Tal negociação é feita durante a apresentação, na secção opções do cabeçalho.

As opções do cabeçalho! É isso.

Pois é, é precisamente dessa forma que o TCP resolve o seu problema. Envia um reconhecimento para o último pacote recebido por ordem e nas opções envia um SACK com os números inicial e final de cada conjunto sequencial de pacotes que já tenha recebido fora de ordem. Assim, o remetente já sabe que não tem necessidade de repetir o envio desses pacotes.

Figura-14-20
Figura 1

Alguns  destes passos podem ser seguidos na Figura 1, que passamos a comentar (clique ou toque na imagem e abra-a noutra janela para seguir a descrição):

Trata-se do envio de 20 Pacotes TCP do Servidor Web para o Browser. A janela de receção acordada é de 10 Pacotes, valor que se mantém constante durante a transmissão. Também foi acordada a utilização de SACK.

O pacote 2 perde-se durante o envio. Ao receber o pacote 3, o Browser envia um ACK para o pacote 1 (o último recebido por ordem) e um SACK denunciando a aceitação do pacote 3. Procede da mesma forma para os pacotes 4, 5 e 6.

Ao receber o 3º ACK para o pacote 1, o Servidor procede ao reenvio do pacote 2. Quando o Browser o recebe envia um ACK do pacote 2 e procede ao deslizamento da janela para o fim do último pacote recebido por ordem. Os restantes (1, 2, 3, 4, 5 e 6) são entregues à aplicação (o 1 já estava logo de início).

Do lado do servidor, quando recebe o ACK do pacote 2, desliza a janela de receção e reinicia o timeout a partir do último pacote recebido por ordem.

De cada vez que um pacote é recebido na ordem correta o timeout é reiniciado e colocado a partir desse pacote. Daí o aparecimento de números após os timeout. Pretendem ilustrar a sua versão.

O ACK do pacote 9 perde-se. O Browser não tem maneira de saber, pois o Servidor espera-o atrasado e fora de ordem. Nada lhe diz que não tenha sido entregue. Portanto só vai repetir o envio do pacote quando o timeout se esgotar. E neste caso, o timeout é reiniciado no mesmo ponto pelo dobro do valor.

O Browser vai receber de novo o pacote 9, descarta-o mas envia um ACK, pois de outra forma o Servidor nunca mais deslizava a janela e continuaria a repeti-lo após o esgotamento de cada timeout. Quando o ACK para esse pacote chega o Servidor reinicia o timeout pelo seu valor normal a partir desse ponto.

Também há um ligeiro adiantamento do pacote 11 e um atraso do pacote 12, o que provoca o facto de chegarem fora de ordem. Quando o 12 chega, o Browser tem falta do 11 e então emite um ACK para o 10 (o último que chegou por ordem) e um SACK para o 12. Como o 11 chega logo de seguida o Browser emite um ACK para o 11 e não volta a emitir um ACK para o 10.

O Servidor, como não recebeu o ACK em triplicado para o 10, não repete o 11, pois considera que ele foi recebido pelo Browser.

Ambos continuam ajustando as suas janelas e timeout até que se conclua a transmissão.

Controlo de Congestionamento TCP

O congestionamento de uma rede é um dos principais fatores que originam atrasos de propagação. E pode-se dar em qualquer enlace do percurso que o pacote deve seguir até ao destinatário.

Vamos aqui envolver alguns conceitos que só serão trabalhados na Camada de Rede, mas que são necessários para o entendimento daquilo que vamos analisar.

Assim, quando um pacote viaja de um remetente para um destinatário, como é evidente não segue um percurso direto. Ele não é como o TGV, sendo mais como o comboio correio que pára em todas as estações.

Lembram-se dos carrinhos que transportavam as folhas da nossa mensagem? Pelo caminho eles vão encontrar muitos cruzamentos (as estações do comboio) e estradas de tamanhos e velocidades diferentes.

Mas, quando chegam ao cruzamento precisam de saber por que estrada seguir. Por isso, em cada cruzamento se encontra um sinaleiro, o Roteador (Router).

As estradas entre o nosso computador e o primeiro sinaleiro, entre os diversos sinaleiros que os carrinhos vão encontrar ao longo do seu caminho e entre o último sinaleiro e o destinatário, chamam-se enlaces (links).

As diferentes estradas terão diferentes capacidades,  os sinaleiros serão mais ou menos eficientes. Tal como partem carrinhos do nosso computador, muitos outros carrinhos partem de outros computadores e vão dar aos mesmos cruzamentos.

Se os carrinhos orientados pelos sinaleiros que são encaminhados para uma das uma estradas de saída, são mais do que a capacidade de escoamento dessa estrada, gera-se aquilo  a que no trânsito do nosso dia a dia chamamos um engarrafamento. O TCP chama-lhe congestionamento.

Mas então como é que o TCP sabe de todos os cruzamentos e estradas que vão de Lisboa a Paris, se têm e quais têm engarrafamentos?

Não sabe, como é evidente. Por isso arranja métodos alternativos que lhe permitem pressupor se há ou não congestionamentos para poder atuar de forma a evitá-los.

É aqui que a janela de receção, que até agora só nomeámos para o controlo do fluxo, vai representar um papel fundamental. Vai ser através do tamanho dessa janela que o TCP vai regular a quantidade de pacotes que coloca na  rede em simultâneo.

Figura-14-21
Figura 2

Então vamos lá a ver como, utilizando a  Figura 2.

Vamos começar por considerar uma variável  JC (Janela de Congestionamento) que determina em cada momento o tamanho dessa janela.

Ao enviar uma mensagem, um conjunto de pacotes, o TCP começa por uma forma que se convencionou nomear como Partida Lenta.

Na partida lenta, o tamanho da janela, JC, começa por ter a dimensão MSS, ou seja, a dimensão de um segmento. Logo que seja recebido o primeiro reconhecimento, isto é, após um RTT (Round Trip Time) o TCP duplica o tamanho de JC (neste caso para 2 MSS). Quando receber cada um dos reconhecimentos o TCP volta a duplicar em cada um o tamanho de JC (agora será 4 MSS e logo de seguida 8 MSS) e assim sucessivamente (crescimento exponencial) até que receba um primeiro sinal de perca de um pacote.

O TCP reconhece a perda de um pacote por esgotamento do temporizador ou por receção de 3 reconhecimentos para o mesmo pacote. A partir daqui chamar-se-á perda de pacote.

Quando assim acontece o TCP começa por determinar o valor correspondente a metade do valor de JC na ocasião e cria uma nova variável a que vamos chamar LF de Limite de Fronteira (Threshold). Logo de seguida o TCP vai ter dois tipos de comportamento distintos, conforme a situação que originou a falha:

  • Se foi ultrapassado o limite do temporizador, reinicia uma partida lenta, duplicando como descrevemos o valor de JC até atingir o valor de LF. A partir daí passa a proceder com um aumento linear de MSS, adicionando a JC o valor de 1 MSS por cada reconhecimento recebido, de novo até receber sinal de perda de um pacote.
  • Se recebeu 3 reconhecimentos para o mesmo pacote, reduz o tamanho de JC para metade, isto é, para LF e a partir daí começa com um aumento linear do valor da JC a 1 MSS por cada reconhecimento, de novo até receber sinal de perda de um pacote.

Como facilmente se depreende, o valor de LF é dinâmico e é corrigido a cada vez que uma indicação de perda de pacote é recebida.

Mas porquê a cada indicação de perda de um pacote?

A perda de um pacote resulta na maior parte dos casos de congestionamentos. Quando um Roteador tem numa das estradas de acesso uma fila superior a um determinado número de pacotes, simplesmente deixa cair os excedentes.

Então, o TCP conclui  que o sinal de perda de pacote muito provavelmente indica um congestionamento num dos cruzamentos de acesso ao destinatário. E procede em conformidade, reduzindo o número de pacotes que lança na rede. Como todos os TCP de acesso a esse cruzamento vão proceder da mesma forma, o congestionamento vai efetivamente reduzir-se e permitir a normalização do tráfego.

Seria ótimo que em todos os cruzamentos que temos de atravessar no acesso ao nosso destino existissem sinaleiros que, em caso de engarrafamento, telefonassem para  as povoações que originam o tráfego a indicar-lhes que o reduzissem. Chegaríamos todos ao destino certamente muito mais depressa.

E porquê reações distintas para situações diferentes?

Se pensarmos bem, vamos perceber a diferença das indicações.

Se o remetente receber uma indicação de 3 reconhecimentos para o mesmo pacote é porque outros dois pacotes entretanto lá chegaram, portanto passaram pelo dito congestionamento e então a redução não é tão drástica. Reduz para LF e passa a avançar devagar. Entra no estado de prevenção de congestionamento.

Se o remetente receber indicação proveniente de esgotamento do temporizador é porque entretanto nenhum dos outros pacotes chegou ao destino. O engarrafamento deve ser complicado e a solução é drástica. Começa de novo com uma partida lenta e só depois de LF passa a prevenção de congestionamento.

E porquê o crescimento exponencial e só depois o crescimento linear?

Se o remetente adotasse logo de início o crescimento linear da JC, provavelmente desnecessariamente a transmissão ir-se-ia tornar demasiado lenta sem necessidade. Assim, começando com um crescimento exponencial da JC, o TCP rapidamente vai crescer e poder determinar o estado de congestionamento da rede. Aí acerta o valor a partir do qual o crescimento vai ser linear, já com alguma justificação.

O controlo de congestionamento sofreu entretanto uma grande evolução com a implementação das flags ECE e CWR, que em conjunto implementam um processo de controlo de congestionamento, o ECN (Explicit Congestion Notification – Notificação expressa de congestionamento).

Porque este processo de controlo de congestionamento tem por fim evitar a queda de pacotes numa fila de espera de um Roteador, esse mesmo roteador marca-o com indicação de congestionamento. Aqui começa o problema. É que o Roteador não abre o pacote até à Camada Transporte. Fica-se pela Camada de Rede, onde vigora o protocolo IP (de que ainda não falámos). Por isso, a sua indicação tem que ser incluída nesse protocolo, pelo preenchimento de dois bits que indicam :

00 – Não suporta ECN
10 – Suporta ECN
01 – Suporta ECN (Para servidores)
11 – CE (Congestion Encountered – Encontrada congestão)

Quanto envia um pacote, o remetente marca-o com 01 ou 10 se suporta ECN e o Roteador onde encontrou congestionamento marca-o com CE (11).

Ao chegar ao destinatário, o pacote aberto na camada rede indica congestionamento no percurso e o destinatário, ao preencher o TCP implementa a flag ECE ou seja ECN – Echo, isto é, ecoa para o remetente o sinal de congestionamento no protocolo TCP.

Nestas circunstâncias, o remetente reduz a dimensão de JC, como se de uma perda se tratasse e implementa a flag CWR (Congestion Window Reduced). Até que o destinatário receba este pacote com CWR a 1, continua a emitir pacotes com a flag ECE a 1.

Este processo só funciona em Roteadores que usem AQM (Active Queue Management – Gestão ativa de fila) e RED (Random Early Detection – Deteção aleatória antecipada). Nestes casos, os Roteadores conseguem, à custa de algoritmos que fazem análise à evolução do tráfego, prever antecipadamente que o buffer de uma interface de  entrada vai saturar e então, marcar com ECN os pacotes antes que tal aconteça, evitando assim deixar cair pacotes como forma de sincronização TCP.

O suporte ECN para TCP vem implementado nas versões Windows 7 e Server 2008 (de origem desativado), nas versões recentes de Linux e MAC OS X 10.5 e 10.6.

A plena utilização deste método, que torna efetivamente a transmissão de mensagens mais eficiente e rápida, exige que todos os intervenientes do processo sejam ECN Capable.

A utilização de ECN é facultativa e a sua utilização deve ser negociada entre as estações terminais, através de opções nas mensagens de apresentação.

Figura-14-24
Figura 3

Bom, e estamos em condições de passar os pacotes para a Camada de rede, que vai tratar do seu endereço e encaminhamento.

Mas antes vamos ver qual é o formato tipo de um segmento UDP (Figura 3).

Porto da fonte, porto de destino e checksum têm o mesmo significado que no segmento TCP. Comprimento total é o comprimento do segmento, incluindo cabeçalho, em Bytes.

Já fizemos atrás algumas considerações sobre UDP e que tipo de aplicações o utilizam. Acrescento que também as aplicações de consulta o utilizam, como por exemplo DNS e DHCP, que descreveremos mais adiante.