12.5. Fiabilidade – sequências, ACKs, retransmissões#
A camada de enquadramento deteta corrupção através dos seus CRCs. A camada de fiabilidade transforma «corrupção detetada» em «a aplicação nunca vê dados corrompidos», negociando retransmissões sempre que um pacote não chega intacto.
12.5.1. Números de sequência#
Cada cabeçalho de pacote contém um número de sequência de um byte, separado para cada direção de transmissão. O emissor incrementa o contador antes de transmitir; o recetor verifica que a sequência de cada pacote recebido é a anterior mais um (módulo 256).
Três situações podem surgir no recetor em vez de um pacote limpo e ordenado:
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 recetor descarta o pacote e (se os ACKs estiverem negociados) envia um NAK a solicitar uma retransmissão.
Um número de sequência superior em um ao esperado, com um CRC válido. O recetor sabe que o pacote anterior se perdeu; envia um NAK referenciando a sequência em falta e guarda o novo.
O caso duplicado (uma retransmissão que chega depois do original ter finalmente sido entregue) é tratado comparando com o contador esperado: se a sequência estiver atrás do esperado, o pacote é um duplicado e o recetor descarta-o após enviar o ACK que o emissor claramente não recebeu na primeira vez.
12.5.2. ACK e NAK#
Dois bits de flag no cabeçalho do pacote transportam o próprio tráfego de fiabilidade:
ACK_REQdefinido num pacote de saída significa «quero um reconhecimento de volta.» Os pacotes de dados normalmente definem este bit; os pings de estado e eventos pontuais podem não o fazer.ACKdefinido num pacote significa «este pacote é o reconhecimento do número de sequência no cabeçalho.» Não transporta payload próprio.NAKdefinido significa «este pacote rejeita um anterior» – normalmente por causa de um CRC inválido ou de uma falha no número de sequência. O cabeçalho indica ao emissor qual a sequência a retransmitir.
O emissor executa um ciclo de espera e avanço: transmite um pacote que requer reconhecimento e aguarda o ACK (ou NAK) correspondente antes de enviar o seguinte. O modelo de um único pacote em voo mantém o estado do emissor limitado – algumas centenas de bytes nas câmaras mais pequenas – e corresponde ao papel do protocolo como canal de controlo entre dois endpoints, em vez de um pipeline otimizado para débito. Em caso de NAK, o emissor retransmite o mesmo pacote com o flag RTX definido, para que o recetor saiba que é uma nova tentativa.
12.5.3. Temporização de retransmissão#
Se nem ACK nem NAK chegarem dentro do tempo limite de retransmissão, o emissor retransmite o pacote em voo por sua iniciativa. O tempo limite é predefinido para 500 ms e duplica em cada nova tentativa consecutiva (1 s, 2 s, …). Após o número de tentativas configurado – predefinição de três – o emissor desiste e comunica um erro de transporte à aplicação.
A duplicação do tempo limite é o padrão padrão de backoff exponencial. Um primeiro tempo limite curto apanha pacotes perdidos rapidamente; a duplicação significa que um host ocupado durante algumas centenas de milissegundos não desencadeia uma enxurrada de duplicados que aumenta a carga.
12.5.4. Configurar a fiabilidade#
Ambas as extremidades podem desativar partes da camada de fiabilidade, por acordo, quando a aplicação pode permitir-se perder dados:
protocol.init(ack=False)desativa os ACKs por pacote. O emissor envia e esquece; o recetor entrega o que chegar. Adequado para transmissão contínua de dados de sensor onde uma amostra desatualizada é aceitável.protocol.init(seq=False)desativa o rastreamento do número de sequência, o que implica também desativar os ACKs. Útil apenas em transportes perfeitamente fiáveis.protocol.init(crc=False)desativa a validação CRC mas mantém o resto do enquadramento intacto. Vale a pena fazer apenas quando o próprio transporte é suficientemente robusto para que erros de CRC não ocorram.
As predefinições – tudo ativo – são o ponto de partida correto para qualquer sessão de depuração entre host e câmara. Quando a aplicação entra em produção, as trocas tornam-se específicas aos seus dados e ao seu transporte.
12.5.5. Os códigos de estado#
Quando um erro de transporte se propaga até ao código de aplicação, chega como um código de estado. A biblioteca de protocolo define dez:
SUCCESS– operação concluída.FAILED– o comando falhou por um motivo não especificado.INVALID– o recetor rejeitou o comando ou um dos seus argumentos.TIMEOUT– um temporizador de nova tentativa expirou.BUSY– a câmara 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 peças em falta.UNKNOWN– um apanhador defensivo para condições genuinamente inesperadas.
O código do host que chama channel_read() recebe estes como exceções Python; o código de aplicação do lado da câmara que optou pelo tratamento de erros personalizado recebe-os como valores de retorno dos callbacks do backend. A maioria das aplicações de câmara não precisa de examinar os códigos de estado – a biblioteca trata da nova tentativa, e apenas falhas genuinamente irrecuperáveis (p.ex., o próprio transporte desapareceu) chegam à aplicação.
Com o enquadramento em vigor para detetar corrupção e a fiabilidade em vigor para recuperar dela, o trabalho ao nível do fio está concluído. O código de aplicação vê pacotes enquadrados, ordenados e intactos; os bytes dentro deles são livres de significar o que o canal acima quiser.