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_REQ ativado 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.

  • ACK ativado 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.

  • NAK ativado 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.