Ir para o conteúdo principal

Um equívoco comum é achar que o TCP garante a entrega dos dados. O que o TCP realmente garante é que entregará os dados à pilha TCP do host receptor ou reportará um erro ao aplicativo remetente. Infelizmente, o relatório de erro não indica a quantidade de dados que foi efetivamente entregue. Há também uma diferença significativa entre a pilha TCP receptora e o aplicativo receptor.

Existem dois cenários básicos de falha. No primeiro, a pilha TCP de envio não está recebendo confirmações TCP para os dados que está transmitindo. Nesse cenário, o aplicativo de envio pode continuar a chamar a função `send` para inserir mais dados no buffer de envio da pilha TCP. Quando a pilha TCP atingir o tempo limite da transmissão, a próxima chamada de `send` (ou `receive`) feita pelo aplicativo de envio indicará o erro `ETIMEDOUT`. O aplicativo agora sabe que houve um problema, mas não tem ideia de quantos dados foram transmitidos com sucesso. Sucesso significa que a pilha TCP de envio recebeu uma confirmação dos dados da pilha TCP de recepção.

No segundo cenário, a pilha TCP de envio transmite dados e recebe um reset em vez de um reconhecimento. O reset pode indicar que a pilha TCP de recepção fechou o soquete ou que algum dispositivo de rede, talvez um firewall, atingiu o tempo limite. Na próxima vez que o aplicativo de envio chamar a função send (ou receive), o erro ECONNRESET será indicado. Você pode pensar que, nesse cenário, o aplicativo remetente poderia inferir que apenas os dados da última chamada de envio falharam, mas estaria errado. É possível que a pilha TCP remetente tenha armazenado em buffer os dados de várias chamadas de envio em um único segmento TCP e que esse segmento tenha acionado a reinicialização, ou que uma série de segmentos TCP tenha sido enviada antes que a reinicialização, acionada pelo primeiro segmento da série, chegasse de volta ao remetente. Tudo o que o aplicativo remetente pode inferir é que nem todos os dados foram transmitidos com sucesso.

Existe um terceiro cenário de falha que não tem nada a ver com a pilha TCP (de envio ou recepção) nem com a rede. Suponha que o aplicativo receptor tenha um bug que o impeça de ler os dados. A pilha TCP de recepção continuará a receber os dados e a enviar confirmações TCP até o momento em que o buffer de recepção TCP ficar cheio. No entanto, isso pode chegar a 64 KB de dados (ou mais, se o dimensionamento de janela TCP for suportado). Se o aplicativo receptor precisar ser reiniciado, todos os dados no buffer de recepção TCP serão perdidos. A pilha TCP receptora deve (mas pode não) enviar um reset para a pilha TCP emissora quando o aplicativo receptor for encerrado. Ela enviará um reset na próxima vez que receber um segmento para a conexão agora fechada. Do ponto de vista do aplicativo remetente, isso é semelhante ao segundo cenário. No entanto, isso ressalta que, mesmo que a pilha TCP receptora confirme o recebimento dos dados, em caso de erro, não é seguro para o remetente presumir que o aplicativo receptor tenha lido os dados.

A lição a ser tirada desses cenários de falha é que, sem um aviso de recebimento na camada de aplicação, um tempo limite de transmissão ou um erro de reinicialização indica que parte — ou possivelmente todos — os dados transmitidos podem não ter sido lidos pelo aplicativo receptor. Por esse motivo, recomendo que todos os aplicativos incluam avisos de recebimento na camada de aplicação, estejam preparados para restabelecer uma conexão e retransmitir dados não confirmados, e sejam capazes de lidar com dados duplicados, já que o que pode ter sido perdido é justamente o aviso de recebimento.

© 2024 Stratus Technologies.