12.5. Confiabilidade – sequências, ACKs, retransmissões#
A camada de enquadramento detecta corrupção com seus CRCs. A camada de confiabilidade transforma “corrupção detectada” em “a aplicação nunca vê dados quebrados” ao negociar retransmissões sempre que um pacote não chega intacto.
12.5.1. Números de sequência#
Cada cabeçalho de pacote carrega um número de sequência de um byte, separado para cada direção de tráfego. O emissor incrementa o contador antes de transmitir; o receptor verifica se a sequência de cada pacote recebido é a anterior mais um (módulo 256).
Três coisas podem aparecer no receptor em vez de um pacote limpo e em ordem:
O número de sequência esperado, com um CRC válido. O pacote é entregue à camada seguinte.
O número de sequência esperado, com um CRC inválido. O receptor descarta o pacote e (se os ACKs forem negociados) envia um NAK pedindo uma retransmissão.
Um número de sequência que está um acima do esperado, com um CRC válido. O receptor sabe que o pacote anterior se perdeu; ele envia um NAK referenciando a sequência perdida e guarda o novo.
O caso de duplicata (uma retransmissão chegando depois que o original finalmente passou) é tratado verificando o contador esperado: se a sequência está atrás da esperada, o pacote é uma duplicata e o receptor o descarta depois de enviar o ACK que o emissor claramente não recebeu da primeira vez.
12.5.2. ACK e NAK#
Dois bits de flag no cabeçalho do pacote carregam o próprio tráfego de confiabilidade:
ACK_REQativado em um pacote de saída significa “quero uma confirmação de volta.” Pacotes de dados normalmente ativam isso; pings de status e eventos pontuais podem não ativar.ACKativado em um pacote significa “este pacote é a confirmação do número de sequência indicado no cabeçalho.” Ele não carrega payload próprio.NAKativado significa “este pacote rejeita um anterior” – geralmente por causa de um CRC inválido ou uma lacuna no número de sequência. O cabeçalho indica ao emissor qual sequência retransmitir.
O emissor executa um laço stop-and-wait: ele transmite um pacote que requer confirmação, depois espera pelo ACK (ou NAK) correspondente antes de enviar o próximo. O modelo de um único pacote em trânsito mantém o estado do emissor limitado – algumas centenas de bytes nas câmeras menores – e corresponde ao papel do protocolo como um canal de controle entre dois endpoints, em vez de um canal otimizado para throughput. Em um NAK, o emissor retransmite o mesmo pacote com a flag RTX ativada para que o receptor saiba que é uma nova tentativa.
12.5.3. Tempo de retransmissão#
Se nem o ACK nem o NAK chegarem dentro do tempo limite de retransmissão, o emissor retransmite por conta própria o pacote em trânsito. O tempo limite tem o valor padrão de 500 ms e dobra a cada nova tentativa consecutiva (1 s, 2 s, …). Após o número configurado de tentativas – três por padrão – o emissor desiste e reporta um erro de transporte à aplicação.
Dobrar o tempo limite é o padrão clássico de backoff exponencial. Um primeiro tempo limite curto captura pacotes perdidos rapidamente; o fato de dobrar significa que um host que fica ocupado por algumas centenas de milissegundos não dispara uma tempestade de duplicatas que agravem a carga.
12.5.4. Configurando a confiabilidade#
Ambas as pontas podem desligar partes da camada de confiabilidade, mediante acordo, quando a aplicação pode se dar ao luxo de perder dados:
protocol.init(ack=False)desabilita os ACKs por pacote. O emissor dispara e esquece; o receptor entrega o que quer que chegue. Bom para o streaming de dados de sensor em que uma amostra desatualizada é aceitável.protocol.init(seq=False)desativa o rastreamento de números de sequência, o que implica também desligar os ACKs. Útil apenas em transportes perfeitamente confiáveis.protocol.init(crc=False)desativa a validação de CRC, mas deixa o resto do enquadramento intacto. Vale a pena fazer apenas quando o próprio transporte é robusto o suficiente para que erros de CRC não aconteçam.
Os padrões – tudo ligado – são o ponto de partida certo para qualquer sessão de depuração entre host e câmera. Uma vez que a aplicação está em produção, os compromissos se tornam específicos aos seus dados e ao seu transporte.
12.5.5. Os códigos de status#
Quando um erro de transporte se propaga até o código da aplicação, ele chega como um código de status. A biblioteca do protocolo define dez:
SUCCESS– operação concluída.FAILED– o comando falhou por um motivo não especificado.INVALID– o receptor rejeitou o comando ou um de seus argumentos.TIMEOUT– um temporizador de nova tentativa esgotou.BUSY– a câmera está ocupada (tipicamente um canal bloqueado).CHECKSUM– o CRC do cabeçalho ou do payload não correspondeu.SEQUENCE– o número de sequência estava fora de ordem além do que a camada consegue recuperar.OVERFLOW– um payload excedeu o máximo negociado.FRAGMENT– uma mensagem de múltiplos fragmentos chegou com partes faltando.UNKNOWN– um capturador defensivo para condições genuinamente inesperadas.
O código do host que chama channel_read() vê esses códigos como exceções Python; o código de aplicação no lado da câmera que optou por tratamento de erros personalizado os vê como valores de retorno dos callbacks do backend. A maioria dos apps de câmera não precisa olhar para os códigos de status – a biblioteca trata da nova tentativa, e apenas falhas genuinamente irrecuperáveis (por exemplo, o próprio transporte sumiu) chegam à aplicação.
Com o enquadramento no lugar para detectar corrupção e a confiabilidade no lugar para se recuperar dela, o trabalho a nível de fio está pronto. O código da aplicação vê pacotes enquadrados, ordenados e intactos; os bytes dentro deles são livres para significar o que quer que a camada acima queira que signifiquem.