VOS 17.1より前のバージョンでは、サーバアプリケーションがaccept関数を呼び出さない限り、STCPはTCP接続要求を受け付けませんでした。接続要求はバックログキューに配置されていましたが、アクセ スコールによってバックログキューから要求が削除されるまで、応答は送信されませんでした。バックログキューが一杯になると、STCPは接続要求に応答してリセットを送信します。
リリース 17.1 から、TCP 接続のダイナミクスが変更されました。接続要求が来ると、バックログキューが満たされていないと仮定して、STCP スタックは直ちに接続を受け入れる応答を送信します。その後、接続はバックログキューに置かれます。バックログキューが一旦満たされると、それ以降の接続要求は応答を送信しません。サーバアプリケーションが accept を呼び出すと、バックログキューの先頭にある接続は削除されます。この動作は、他のほとんどのオペレーティングシステム上の TCP スタックの動作に似ています。
通常の状況では、このダイナミクスの変化は、サーバやクライアントアプリケーションの動作に目立った変化をもたらすことはありません。しかし、サーバアプリケーションの動作が遅くなったり、接続が予想以上に速くなったりすると、接続が長時間バックログキューに残ってしまうことがあります。このような状況下では、いくつかの興味深い挙動が見られるかもしれません。
以下の表は、クライアントのアクションに基づいて接続要求がバックログキューに置かれたときに何が起こるかをまとめたものです。クライアントは何もせず、サーバアプリケーションがデータを送信するのを待ち、データを送信し、FIN(最終)フラグ(正常終了)またはRST(リセット)フラグ(異常終了)のいずれかで接続を閉じることができます。最初の4つのカラムは期待通りに動作します。サーバアプリケーションは、バックログキューからソケットを取得した後、recvを呼び出し、データがない場合はハングアップするか、ソケットの内容に応じてデータを取得するか、ファイルの終了を表示します。最後の4つのカラムをより詳しく見る必要があります。
ソケットには何もない | ソケット内のデータ | ソケットのFIN | ソケット内のデータ
ソケットのFIN |
ソケット内のデータ
ソケット内のFIN クライアントソケットがなくなった |
ソケット内のFIN
クライアントソケットがなくなった |
ソケット内のデータ
送信されたリセット |
リセット送信 | ||
受け入れて下さい | ソケットを返します。 | ソケットを返します。 | ソケットを返します。 | ソケットを返します。 | ソケットを返します。 | ソケットを返します。 | 吊るす | 吊るす | |
最初の recv | 吊るす | データを返します。 | EOFを返します。 | データを返します。 | リセットエラーを返します。 | EOFを返します。 | |||
セカンドリブ | 吊るす | EOFを返します。 |
ソケット内のデータ / ソケット内のFIN / クライアントソケットがなくなった
このシナリオでは、クライアントはいくつかのデータを送信し、接続をシャットダウンしました。STCPの17.1リリースでは、送信されたすべてのデータを認識しますが、受信キューのデータがアプリケーションに渡されるまではFINを認識しません。その結果、クライアントの再送タイマーが切れるまでクライアントのFINは再送されます。うまく書かれたクライアントアプリケーションは、FINがタイムアウトしたときにクライアントにエラーが通知されるように、サーバーアプリケーションが接続をシャットダウンするのを待ちます。しかし、クライアントアプリケーションがサーバからの通知を待たずにソケットを閉じてしまうと、送信したデータが失われたことを知ることができません。
サーバアプリケーションが重複確認応答パケットを受け入れると、データは確認してもFINは送信されません。クライアントはソケットを破棄したため、リセットで応答します。リセットにより、サーバ側のソケットはクリアされます。サーバアプリケーションは、最初のrecvコールでリセットエラーを取得しますが、その時間は、 acceptコールから最初のrecvコールまでの時間に依存しています。その時間がクライアントのリセットパケットを取得して処理するのにかかる時間よりも速い場合、サーバアプリケーションはデータを見ることになります。いずれの場合も、サーバアプリケーションは接続が行われたことと、接続が中断されたことを知ることができます。
ソケット内のFIN / クライアントソケットが消えた
これは、ソケット内にデータがないという点で、前回のシナリオとは異なる。データがない場合は、FINを確認します。acceptはソケットを返し、recvはFINを受信したことを示すEOFを返す。ソケットを閉じるなどしてソケットへの書き込みを試みると、クライアントは対応するソケットを持っていないため、リセットが返されます。
ソケット内のデータ / リセット送信
ここで、クライアントはいくつかのデータを送信してからリセットを送信します。一般的には、アプリケーションがソケットをシャットダウンし、TCPスタックがすぐにリセットを送信するか、FINパケットの確認応答に失敗した後にリセットを送信するかのどちらかである。これは最初のシナリオと似ていますが、クライアントのTCPスタックはソケットを切断する代わりに、ソケットを切断する前にリセットを送信します。この場合も、よく書かれたクライアントアプリケーションはこれをエラーとして認識しますが、よく書かれていないアプリケーションはエラーを認識しません。
サーバーソケットが引き裂かれ、リセットを受信するとすべてのデータが破棄されるため、サーバーアプリケーションは接続が確立されたことを示す表示すら見ておらず、ただ別の接続を待っているだけです。
送信されたリセット
この場合も、リセットはバックログキューからソケットを削除するので、Acceptはソケットを見ずに次の接続要求を待つことになります。
では、これは何を意味しているのでしょうか?まず、クライアントアプリケーションはより高速な接続応答を得ることができますが、アプリケーションレベルのバナーやメッセージの種類によっては、より長く待たなければならないかもしれません。このバナーやメッセージのタイムアウトは、上向きに調整する必要があるかもしれません。第二に、サーバアプリケーションは accept した直後に recv コールからのエラーを処理できるように準備しておかなければなりません。これは常に可能性がありましたが、現在では可能性が高くなっています。最後に、サーバにデータを送信していて、何らかのメッセージの返答を期待していないクライアントアプリケーションは、サーバアプリケーションが正しく接続をシャットダウンしたことを確認するように書かれなければなりません。繰り返しになりますが、この可能性は常に存在しており、STCP に特有のものではありませんが、現在ではその可能性はわずかに高くなっています。