"アプリケーションは何年も正常に動作していましたが、先週ネットワークがアップグレードされ、100 mbpsからギガビットに移行しました。今では、いくつかのメッセージのデータの最後の半分がゴミになっています。ネットワークの人たちはネットワークのせいではないと言っていますが、変わったのはそれだけです。"
高価なネットワークのアップグレードをしても、アプリケーションが壊れなかったのは朗報です。
悪いニュースは、アプリケーションが壊れていて、常に壊れているということです。TCP アプリケーションを書いている*多くの人が気付いていないことは、TCP はメッセージではなくバイトのストリームであるということです。アプリケーションが2つの1000バイトのメッセージを送信するという事実は、送信側のTCPスタックが2つの1000バイトのTCPセグメントを送信することを意味しません。アプリケーションのメッセージは、レシーバのアドバタイズされた最大セグメントサイズ、または送信者の設定制限に基づいて、より小さな断片にセグメント化することができます。再送はまた、アプリケーション・メッセージを組み合わせたり、断片化したりすることもできます。送信側の TCP スタックが実際に 2 つの 1000 バイトの TCP セグメントを送信したとしても、受信側の TCP スタックが受信側のアプリケーションに 2 つの 1000 バイトのメッセージを与えることを保証するものではありません。例えば、アプリケーションのバッファが 500 バイトしかない場合、TCP スタックが返すのはそれだけです。一方、バッファが 1500 バイトで、両方の 1000 バイトのセグメントが到着した場合、TCP スタックは最初の呼び出しでは 1500 バイト、2 番目では 500 バイトを返します。バイトストリームを受け取り、それを正しく解析してメッセージに戻すのはアプリケーション次第です。
これはネットワークのアップグレードと何の関係があるのでしょうか?さて、OpenVOS サーバとLinux クライアントは異なるサブネット上にあるので、OpenVos システムは最大セグメントサイズを 536 バイトと宣伝していました。クライアントから送信されるアプリケーションメッセージは1000バイトなので、メッセージは2つに分割されていました。アップグレード前は、アプリケーションが受信を投稿する前に両方のセグメントが OpenVOS サーバに到着していたようです。アップグレード後、セグメントのタイミングが変更され、アプリケーションがレシーブを投稿したときに最初のセグメントだけが利用できるようになりました。アプリケーションバッファの最後の 464 (1000 - 536) バイトは TCP スタックによって埋められず、受信が投稿される前に存在していたすべてのゴミが含まれていました。
このケースでは、OpenVOS のアドバタイズドされた MSS 値を 1460 に増加させるという簡単な修正方法がありました (サブネット間の TCP スループットを改善する簡単な方法を参照)。しかし、これは実際にはストップギャップに過ぎません。本当の解決策は、1 回の受信コールが 1 つのメッセージを返すと仮定するのではなく、TCP のバイトストリームを正しく解析してアプリケーションメッセージに戻すコードを書き換えることです。