O QUIC é um protocolo de transporte que roda sobre UDP e faz, dentro de uma única conexão criptografada, o que o TCP e o TLS faziam como pilha: entrega confiável, controle de fluxo e de congestionamento, e o handshake do TLS 1.3 (RFC 9000). O lado do handshake está contado em TLS 1.2 vs TLS 1.3 vs DTLS vs QUIC. Este artigo é sobre as três propriedades que mais se atribuem ao QUIC, streams que não se bloqueiam, recuperação mais rápida de perdas e conexões que sobrevivem a uma troca de endereço, e sobre onde cada uma delas termina.
Streams: ordenados por dentro, independentes entre si
Uma conexão QUIC carrega muitos streams. Dentro de um stream, a entrega é rigorosa: o endpoint tem de conseguir entregar à aplicação os dados do stream como um fluxo de bytes ordenado, guardando em buffer o que chegar fora de ordem (RFC 9000, seção 2.2). Entre streams não existe essa promessa, e esse é o ponto. Quando um pacote se perde, só os streams que tinham dados naquele pacote esperam a retransmissão, enquanto os outros continuam avançando (RFC 9000, seção 13). Sobre TCP, a mesma perda travaria todos os streams que dividem a conexão, porque o TCP entrega uma única sequência ordenada de bytes; é o head-of-line blocking de transporte que o manteve e o HTTP/3 elimina.
A mesma seção da especificação traz a ressalva: quando dados de vários streams viajam num mesmo pacote QUIC, perder esse pacote bloqueia todos esses streams, e a recomendação às implementações é colocar o menor número possível de streams num pacote sem desperdiçar espaço. E a independência entre streams acaba onde começam as dependências da própria aplicação. Se uma página só consegue usar o recurso B depois que o recurso A chegou, B espera por A, seja qual for o transporte; se uma atualização de compressão de headers viaja num stream e uma requisição em outro depende dela, essa requisição também espera. Streams independentes eliminam uma ordem que o transporte impunha, não a ordem de que a aplicação precisa.
Perdas: sinais mais claros, a mesma física
O QUIC nunca retransmite um pacote. Os números de pacote só crescem, então registram a ordem em que os pacotes foram enviados, e a ordem de entrega dos dados vem dos offsets dos streams; quando um pacote com dados se perde, os frames que ainda precisam chegar são enviados de novo em pacotes novos, com números novos (RFC 9002, seção 4.2). Isso elimina a ambiguidade que o TCP tem quando um ACK pode se referir tanto à transmissão original quanto à retransmissão, o que deixa mais precisos a medição do RTT e a detecção de perdas.
Isso não cria capacidade. O controle de congestionamento no QUIC funciona por caminho, não por stream: o controlador descrito na especificação é parecido com o NewReno do TCP, e todos os streams de uma conexão no mesmo caminho dividem esse controlador (RFC 9002, seção 7). Um caminho congestionado deixa lentos todos os streams que passam por ele, e um link com perdas continua custando o que custa.
Migração: uma conexão identificada por IDs
Uma conexão TCP é identificada pelos dois endereços e portas, então quando um celular passa do para a rede móvel a conexão acabou. O QUIC identifica a conexão por connection IDs que cada lado escolhe para o outro, justamente para que mudanças de endereçamento abaixo dele não entreguem pacotes no lugar errado (RFC 9000, seção 5.1). Isso torna a migração possível, sob regras (RFC 9000, seção 9):
- Só depois do handshake. Um endpoint não pode começar a migrar antes de o handshake estar confirmado.
- Só o cliente se move. A especificação limita a migração a endereços novos do cliente, fora o endereço preferido anunciado por um servidor, e é o cliente que inicia toda migração.
- O caminho novo é conferido. Um endpoint que percebe qualquer mudança no endereço do outro lado, proposital ou causada por um (network address translation) que remapeou o fluxo, tem de validar o caminho novo antes de confiar nele, com um desafio que o outro lado precisa devolver por aquele caminho (RFC 9000, seção 8.2).
- O servidor pode recusar. Um servidor que envia o transport parameter
disable_active_migrationavisa o cliente para não mandar pacotes de um endereço local novo para o endereço usado no handshake.
Nada disso ajuda numa rede que nem leva UDP até o servidor: ali a conexão não chega a ser estabelecida, e a própria especificação do diz que, quando problemas de conectividade como UDP bloqueado impedem uma conexão QUIC, os clientes devem tentar uma versão do HTTP sobre TCP (RFC 9114, seção 3.1). E um caminho que muda porque o tráfego agora atravessa outro load balancer, firewall ou NAT só continua funcionando se esse equipamento encaminhar pacotes por connection ID e deixar o UDP passar; o protocolo dá os meios, a implantação decide se os usa.
Como ler o QUIC em operação
O resumo prático: um stream lento não trava mais os vizinhos, a menos que eles tenham dividido o pacote perdido ou dependam dele na aplicação; a perda é detectada com mais precisão, mas o congestionamento continua compartilhado por caminho; e uma conexão consegue acompanhar o cliente até um endereço novo depois que o handshake foi confirmado e o caminho novo respondeu ao desafio. Quando uma captura ou um log contradiz essas expectativas, as perguntas são qual dessas condições falhou, e se a rede no caminho leva UDP e connection IDs do jeito que o QUIC precisa.
Fontes
- RFC 9000, QUIC: A UDP-Based Multiplexed and Secure Transport, maio de 2021: seções 2.2 (entrega ordenada dentro de um stream), 5.1 (connection IDs), 8.2 (path validation), 9 (migração de conexão, iniciada pelo cliente, depois da confirmação do handshake, disable_active_migration) e 13 (head-of-line blocking entre streams e pacotes com vários streams) (lida em 2026-10-26)
- RFC 9002, QUIC Loss Detection and Congestion Control, maio de 2021: seção 4.2 (números de pacote sempre crescentes, sem ambiguidade de retransmissão) e seção 7 (um controlador de congestionamento parecido com o NewReno, por caminho) (lida em 2026-10-26)
- RFC 9114, HTTP/3, junho de 2022: seção 3.1, os clientes SHOULD voltar ao HTTP sobre TCP quando o QUIC não pode ser estabelecido, por exemplo com UDP bloqueado (lida em 2026-10-26)