9.10. TCP – um fluxo fiável de bytes

O TCP, Transmission Control Protocol (Protocolo de Controlo de Transmissão), é o outro serviço da camada de transporte sobre IP. Se o UDP pode ser descrito como «envia um pacote e torce para o melhor», o TCP é «abre uma ligação entre dois extremos e trata-a como um canal bidirecional de bytes que o outro lado recebe definitivamente, por ordem e exactamente uma vez». A maior parte do tráfego na internet utiliza-o, e a maior parte do que a câmara faz na rede também.

9.10.1. O que o TCP acrescenta ao IP

O TCP faz muito mais do que o UDP. Acrescenta:

  • Uma ligação. Antes de qualquer dado fluir, os dois extremos trocam um curto handshake para acordar que estão a comunicar. A ligação tem um estado – «aberta», «meio-fechada», «fechada» – que ambos os lados monitorizam.

  • Entrega fiável. Cada byte que o emissor coloca é confirmado pelo receptor. Tudo o que não for confirmado dentro de um tempo limite é enviado novamente. A aplicação nunca vê um byte perdido; vê um atraso enquanto o protocolo reencaminha.

  • Entrega por ordem. Os bytes chegam na mesma ordem em que foram enviados. Mesmo que os pacotes cheguem ao receptor fora de ordem, o TCP reordena-os antes de a aplicação os ler.

  • Controlo de fluxo. Se o receptor for lento, o emissor recebe instrução para abrandar; a ligação adapta-se à taxa do extremo mais lento.

  • Controlo de congestionamento. Se a rede intermédia começar a descartar pacotes, o emissor recua até as coisas se recuperarem. Isto impede que uma única ligação derrube um elo saturado.

Tudo isto é automático. A API Python que a aplicação utiliza é simplesmente send(bytes) e recv(n); o TCP trata de tudo o resto por baixo.

9.10.2. O handshake

Uma ligação TCP abre-se com uma troca de três vias antes de qualquer dado ser permitido:

A diagram with two columns labelled "client" and "server". An arrow from client to server labelled "SYN", then an arrow from server to client labelled "SYN-ACK", then an arrow from client to server labelled "ACK". Below that a thick arrow labelled "data flowing both ways".

O handshake de três vias. Assim que ambos os lados tenham confirmado, a ligação está aberta e os dados podem fluir.

O cliente envia um pacote SYN (sincronizar) a pedir para abrir. O servidor responde com SYN-ACK (sincronizar + confirmar), aceitando. O cliente envia um ACK final para confirmar. Após esta ida e volta, ambos os lados concordam que a ligação está aberta e sincronizaram os seus contadores para monitorizar quais os bytes que foram enviados e recebidos.

O handshake custa uma latência de ida-e-volta antes do primeiro byte útil passar. Para redes locais é uma milissegundo; para ligações intercontinentais é aproximadamente 100 ms. Este é o principal custo do TCP e a razão pela qual mensagens curtas e pontuais são por vezes mais adequadas usando UDP.

9.10.2.1. O handshake de encerramento

As ligações TCP também encerram com uma troca (um FIN de cada lado). Qualquer extremo pode encerrar a sua metade da ligação de forma independente; um estado half-closed onde um lado terminou de enviar mas o outro ainda está a comunicar é legal, embora incomum. A aplicação normalmente apenas chama close() e deixa o protocolo tratar da sequência de encerramento.

9.10.3. O que o TCP não garante

Algumas coisas que o TCP por vezes se assume que faz, mas não faz:

  • Fronteiras de mensagem. A aplicação envia um fluxo de bytes, não um fluxo de mensagens. Duas chamadas send(b"hello") podem chegar como um único recv() de b"hellohello", ou como dois recv()s de tamanhos variados. Se a aplicação quiser delimitação de mensagens, tem de a adicionar ela própria (uma nova linha, um prefixo de comprimento, o que for). Enviar documentos JSON sobre TCP, por exemplo, requer que cada documento seja separado por uma nova linha ou outro marcador.

  • Encriptação. O TCP transporta os bytes que a aplicação lhe forneceu, em claro, todo o caminho. Se o conteúdo precisar de ser confidencial, a aplicação tem de envolver a ligação em TLS (ver Sockets encriptados e TLS).

  • Autenticação. O TCP garante que os bytes chegaram intactos. Não diz nada sobre quem os enviou. A autenticação é também uma preocupação de camadas superiores.

  • Actividade numa ligação silenciosa. Se nenhum dos lados enviar dados durante muito tempo, a ligação está tecnicamente ainda aberta mas não consegue detectar que o outro extremo falhou ou desapareceu. Sondas Keepalive podem ser activadas no socket para corrigir isto quando for relevante.

9.10.4. Quando usar TCP

O TCP é a resposta certa para quase qualquer conversa que se enquadre na forma «o cliente abre uma ligação a um servidor, trocam alguns bytes, a ligação fecha quando terminar». Pedidos HTTP e HTTPS, sessões SSH, consultas a bases de dados, transferências de ficheiros, carregamentos de imagens – tudo sobre TCP.

Recorra ao UDP apenas quando a conversa não se enquadra nessa forma: mensagens independentes e auto-contidas onde a perda é aceitável, tráfego multicast, sincronização de tempo, pesquisas de nomes, ou casos extremamente sensíveis à latência onde o custo do handshake é proibitivo.

Com portas, UDP e TCP todos disponíveis, a história da camada de transporte está concluída. A API Python que expõe ambos está na página seguinte: Objetos socket.