よくある誤解は、TCP がデータの配送を保証しているということです。TCPが実際に保証しているのは、受信側ホストのTCPスタックにデータを配信するか、送信側アプリケーションにエラーを報告することです。残念ながら、エラーレポートでは実際にどのくらいのデータが配信されたかはわかりません。また、受信側のTCPスタックと受信側のアプリケーションの間にも大きな違いがあります。
基本的な障害シナリオは2つあります。1 つ目は、送信側の TCP スタックが送信中のデータに対する TCP 確認応答を受信していない場合です。このシナリオでは、送信アプリケーションは TCP スタックの送信バッファにさらに多くのデータを入れるために送信を呼び続けることができます。TCP スタックが送信をタイムアウトすると、送信アプリケーションによって呼び出された次の送信(または受信)は ETIMEDOUT エラーを示します。アプリケーションは問題があったことを知っていますが、どのくらいのデータが正常に送信されたかはわかりません。成功とは、送信側の TCP スタックが受信側の TCP スタックからデータの確認応答を受け取ったことを意味します。
2 番目のシナリオでは、送信側の TCP スタックがデータを送信し、確認応答ではなくリセットを受信します。リセットは、受信側の TCP スタックがソケットを閉じたか、ファイアウォールなどのネットワークデバイスがタイムアウトしたことを示しています。次に送信側のアプリケーションが送信(または受信)を呼び出すと、ECONNNRESETエラーが表示されます。このシナリオでは、送信アプリケーションは最後の送信コールで失敗したデータだけが原因であると考えるかもしれませんが、それは間違いです。送信側のTCPスタックが複数の送信コールからのデータを1つのTCPセグメントにバッファリングし、そのセグメントがリセットの引き金となった可能性もありますし、一連のTCPセグメントが送信側に戻ってくる前に、一連のTCPセグメントが送信された可能性もあります。送信アプリケーションが推測できるのは、すべてのデータが正常に送信されたわけではないということだけです。
TCP(送信・受信)スタックやネットワークとは関係のない第三の障害シナリオがあります。受信側のアプリケーションにバグがあり、データの読み込みができない状態になっているとします。受信側の TCP スタックは、TCP 受信バッファが一杯になるまでデータを受信し、TCP 確認応答を送信し続けます。しかし、これは最大 64K バイトのデータになります(TCP ウィンドウのスケーリングがサポートされている場合はそれ以上になります)。受信アプリケーションを再起動する必要がある場合、TCP 受信バッファ内のすべてのデータは失われます。受信側の TCP スタックは、受信アプリケーションが終了したときに、送信側の TCP スタックにリセットを送信すべきです(してはいけません)。受信側 TCP スタックは、現在閉じられている接続のセグメントを次に受信したときにリセットを送信します。送信側のアプリケーションの観点からは、これは 2 番目のシナリオに似ています。しかし、受信 TCP スタックがデータを認識したとしても、エラーが発生した場合には、送信側が受信アプリケーションがデータを読み込んだと仮定することは安全ではないことが指摘されています。
これらの障害シナリオから得られる考え方は、アプリケーション層の確認応答がなければ、送信タイムアウトやリセットエラーは、送信されたデータの一部またはすべてが受信側のアプリケーションによって読み取られていない可能性があることを示しているということです。この理由から、すべてのアプリケーションにアプリケーション層の確認応答を含めること、接続を再確立して未認識のデータを再送信する準備をしておくこと、そして重複したデータを処理できるようにしておくことをお勧めします。