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.