9.10. TCP – um fluxo confiável de bytes

O TCP, o Transmission Control Protocol, é o outro serviço da camada de transporte construído sobre o IP. Enquanto o UDP é melhor descrito como “envie um pacote e torça”, o TCP é “abra uma conexão entre dois pontos e trate-a como um duto bidirecional de bytes que o outro lado com certeza recebe, em ordem e exatamente uma vez”. A maior parte do tráfego da internet o utiliza, e a maior parte do que a câmera faz na rede também.

9.10.1. O que o TCP acrescenta ao IP

O TCP faz muito mais do que o UDP. Ele acrescenta:

  • Uma conexão. Antes de qualquer dado fluir, os dois pontos trocam um breve handshake para concordarem que estão se comunicando. A conexão tem um estado – “aberta”, “meio-fechada”, “fechada” – que ambos os lados acompanham.

  • Entrega confiável. Cada byte que o remetente envia é confirmado pelo destinatário. Qualquer coisa não confirmada dentro de um tempo limite é enviada novamente. A aplicação nunca vê um byte perdido; ela vê um atraso enquanto o protocolo reenvia.

  • Entrega em ordem. Os bytes chegam na mesma ordem em que foram enviados. Mesmo que os pacotes cheguem ao destinatário fora de ordem, o TCP os reordena antes que a aplicação os leia.

  • Controle de fluxo. Se o destinatário é lento, o remetente é avisado para diminuir o ritmo; a conexão se adapta à taxa do lado mais fraco.

  • Controle de congestionamento. Se a rede no meio começa a descartar pacotes, o remetente recua até que as coisas se recuperem. Isso impede que uma única conexão derrube um enlace saturado.

Tudo isso é automático. A API Python que a aplicação usa é simplesmente send(bytes) e recv(n); o TCP cuida de todo o resto por baixo.

9.10.2. O handshake

Uma conexão TCP é aberta com uma troca de três vias antes que qualquer dado possa passar:

Um diagrama com duas colunas rotuladas como "client" e "server". Uma seta do client para o server rotulada "SYN", depois uma seta do server para o client rotulada "SYN-ACK", depois uma seta do client para o server rotulada "ACK". Abaixo disso uma seta grossa rotulada "data flowing both ways".

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

O cliente envia um pacote SYN (synchronise) pedindo para abrir. O servidor responde com SYN-ACK (synchronise + acknowledge), aceitando. O cliente envia um ACK final para confirmar. Após esse ciclo de ida e volta, ambos os lados concordam que a conexão está aberta e sincronizaram seus contadores para rastrear quais bytes foram enviados e recebidos.

O handshake custa um tempo de ida e volta de latência antes que o primeiro byte útil passe. Para redes locais isso é um milissegundo; para conexões transcontinentais isso é cerca de 100 ms. Esse é o principal custo do TCP e a razão pela qual mensagens curtas e únicas às vezes ficam melhores usando o UDP.

9.10.2.1. O handshake de fechamento

As conexões TCP também fecham com uma troca (um FIN de cada lado). Qualquer extremidade pode fechar sua metade da conexão de forma independente; um estado meio-fechado, em que um lado terminou de enviar mas o outro ainda está se comunicando, é válido, embora incomum. Normalmente a aplicação apenas chama close() e deixa o protocolo cuidar da sequência de encerramento.

9.10.3. O que o TCP não garante

Algumas coisas que às vezes se supõe que o TCP faça, mas que ele não faz:

  • Limites 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, ela mesma precisa acrescentar essa delimitação (uma quebra de linha, um prefixo de comprimento, o que for). Enviar documentos JSON por TCP, por exemplo, exige que cada documento seja separado por uma quebra de linha ou algum outro marcador.

  • Criptografia. O TCP transporta os bytes que a aplicação lhe entregou, em texto aberto, por todo o caminho. Se o conteúdo precisa ser confidencial, a aplicação precisa envolver a conexão em TLS (veja Sockets criptografados e TLS).

  • Autenticação. O TCP garante que os bytes chegaram intactos. Ele não diz nada sobre quem os enviou. A autenticação também é uma preocupação de camada superior.

  • Vivacidade em uma conexão silenciosa. Se nenhum dos lados envia dados por um longo tempo, a conexão tecnicamente ainda está aberta, mas não consegue detectar que a outra extremidade travou ou desapareceu. Sondas de keepalive podem ser habilitadas no socket para resolver isso quando importa.

9.10.4. Quando usar o TCP

O TCP é a resposta certa para quase qualquer conversa que se encaixe no formato “o cliente abre uma conexão com um servidor, eles trocam alguns bytes e a conexão fecha quando terminam”. Requisições HTTP e HTTPS, sessões SSH, consultas a bancos de dados, transferências de arquivos, uploads de imagens – tudo por TCP.

Recorra ao UDP apenas quando a conversa não se encaixar nesse formato: mensagens independentes e autocontidas em que a perda é aceitável, tráfego multicast, sincronização de tempo, resoluções de nomes ou casos extremamente sensíveis à latência em que o custo do handshake é proibitivo.

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