[sockets] TCPオプションSO_LINGER(ゼロ) - 必要なとき


Answers

私の提案では、最後のセクション、 「タイムアウト0でSO_LINGERを使用するタイミング」をお読みください。

私たちがそれに来る前に、少しの講義について:

  • 通常のTCP終了
  • TIME_WAIT
  • FINACKRST

通常のTCP終了

通常のTCP終了シーケンスは次のようになります(簡略化)。

我々は2つのピアを持っています:AとB

  1. A close()呼び出すと、
    • AはFINをBに送る
    • AはFIN_WAIT_1状態になります
  2. BはFINを受け取る
    • BがACKをAに送信する
    • BがCLOSE_WAIT状態に入る
  3. AはACKを受信する
    • AはFIN_WAIT_2状態になります
  4. Bはclose()呼び出します。
    • BがFINをAに送信する
    • BがLAST_ACK状態に入る
  5. AはFINを受け取る
    • AはBにACKを送る
    • AがTIME_WAIT状態に入る
  6. BがACKを受信する
    • BがCLOSED状態になります。つまり、ソケットテーブルから削除されます。

TIME_WAIT

したがって、終了を開始するピア、つまりclose()最初に呼び出すピアは、 TIME_WAIT状態になります。

TIME_WAIT状態が私たちの友人である理由を理解するには、Stevensら(43ページ)の第3版「UNIXネットワークプログラミング」の2.7節を読んでください。

しかし、最終的に新しい接続が受け入れられないようにするために、サーバー上のTIME_WAIT状態のソケットが多く問題になる可能性があります。

この問題を回避するために、 close()呼び出す前に、タイムアウト0のSO_LINGERソケットオプションを設定するように提案してきました。 ただし、TCP接続がエラーで終了するため、これは悪い解決策です。

代わりに、クライアント側から常に接続終了が開始されるようにアプリケーションプロトコルを設計します。 クライアントがいつ残りのすべてのデータを読み取ったかを常に知っている場合、クライアントは終了シーケンスを開始することができます。 例として、ブラウザーはContent-Length HTTPヘッダーから、すべてのデータを読み取ってクローズを開始できることを知っています。 (私は、HTTP 1.1では可能な再利用のためにしばらく開いたままにしておき、それを閉じることを知っています。)

サーバーが接続を閉じる必要がある場合は、サーバーがクライアントにclose()を呼び出すようにアプリケーションプロトコルを設計します。

タイムアウト0でSO_LINGERを使用する場合

ここでも、 close()を呼び出す前にSO_LINGERをタイムアウト0に設定すると、通常の終了シーケンスが開始されません

代わりに、このオプションを設定してclose()を呼び出すピアは、エラー状態を示すRST (接続リセット)を送信し、これは相手側でどのように認識されるかです。 通常、「ピアによる接続のリセット」のようなエラーが表示されます。

したがって、通常の状況では、 close()を呼び出す前にSO_LINGERをタイムアウト0に設定することは本当に悪い考えです。これからサーバーアプリケーションではSO_LINGERと呼ばれます。

しかし、とにかくそうすることを保証する特定の状況:

  • サーバーアプリケーションのクライアントが誤動作(タイムアウト、無効なデータを返すなど)したCLOSE_WAITCLOSE_WAITにスタックされたり、 TIME_WAIT状態になるのを避けるために、不完全な終了が意味を持ちます。
  • 現在数千のクライアント接続を持つサーバーアプリケーションを再起動する必要がある場合は、 TIME_WAIT (サーバー側からclose()を呼び出すときclose()何千ものサーバーソケットが存在しないようにこのソケットオプションを設定することを検討してください。再起動後の新しいクライアント接続用。
  • 「この機能を使用して強制終了を送信することを保証する特定の状況があります。たとえば、 CLOSE_WAIT永続的にハングアップしてスタックにデータを配信しようとするRS-232ターミナルサーバーがあります保留中のデータを破棄するRSTがあれば、スタックポートを適切にリセットします。

私はあなたの質問に非常に良い答えを与えると信じてthis長い記事をお勧めします。

Question

私はオプションの正式な意味を理解していると思います。 私が今取り扱っているいくつかのレガシーコードでは、オプションが使用されています。 顧客は、FSTへの応答として、その側から接続が近い側のRSTについて苦情を言います。

私はそれがいつ使用されるべきか理解していないので、私はそれを安全に取り外すことができないのか分かりません。

オプションが必要な場合の例を挙げてください。




あなたのコードの中で安全に削除することができるかどうかは、アプリケーションのタイプに依存します:それは "クライアント"(TCP接続をオープンし、最初にそれを閉じる)かそれとも "サーバー"反対側がクローズを開始した後にクローズする)?

あなたのアプリケーションが "クライアント"のフレーバーを持っていて(最初に閉じる)、別のサーバーへの膨大な数の接続を開始したり閉じたりする場合(例えば、あなたのアプリケーションが膨大な数の異なるサーバーの到達可能性を監督する監視アプリケーションの場合)すべてのクライアント接続がTIME_WAIT状態になっているという問題があります。 次に、タイムアウトをデフォルトよりも小さくして、正常にシャットダウンしても、クライアント接続リソースを解放しておくことをお勧めします。 0はFINで正常にシャットダウンしませんが、RSTでは中断するため、タイムアウトを0に設定しません。

アプリケーションが "クライアント"の風味を持ち、同じサーバーから大量の小さなファイルをフェッチする必要がある場合は、ファイルごとに新しいTCP接続を開始し、TIME_WAITで大量のクライアント接続を終了するべきではありませんが、接続を開いたままにして、すべてのデータを同じ接続でフェッチします。 リンガーオプションは削除できます。

あなたのアプリケーションが "サーバー"(ピアのクローズの反応として2番目に近い)であれば、close()で接続は正常にシャットダウンされ、TIME_WAIT状態に入らないのでリソースは解放されます。 リンガーは使用しないでください。 しかし、あなたのサーバーアプリケーションが、アクティブでないアイドル状態の接続を長時間アイドル状態にしていることを監視するプロセスがある場合(「長い」が定義される)、この非アクティブな接続をあなた側からシャットダウンすることができます。 これは、lingerタイムアウトを0に設定することによって行われます。close()はクライアントにRSTを送信し、あなたに怒っていることを伝えます:-)




Links