web-dev-qa-db-ja.com

TCPオプションSO_LINGER(ゼロ)-必要な場合

私はオプションの正式な意味を理解していると思います。現在処理している一部のレガシーコードでは、このオプションが使用されています。顧客は、その側から近い接続でのその側からのFINへの応答としてRSTについて不満を言います。

いつ使用すべきかわからないため、安全に削除できるかどうかはわかりません。

オプションが必要になる時期の例を教えてください。

83
dimba

SO_LINGERタイムアウトを0に設定する一般的な理由は、TIME_WAIT状態にある多数の接続を回避し、サーバー上の使用可能なすべてのリソースを占有することです。

TCP接続が完全に閉じられると、閉じを開始した側(「アクティブな閉じる」)が数分間TIME_WAITに接続された状態になります。そのため、プロトコルがserverで接続のクローズを開始し、非常に多数の短命の接続が含まれるプロトコルの場合、この問題の影響を受けやすい可能性があります。

ただし、これは良い考えではありません-TIME_WAITが理由で存在します(古い接続からの浮遊パケットが新しい接続に干渉しないようにするため)。可能であれば、クライアントが接続を開始するプロトコルにプロトコルを再設計することをお勧めします。

71
caf

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

その前に、次のことについて少し講義します。

  • 正常なTCP終了
  • TIME_WAIT
  • FINACK、およびRST

正常な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はACKをBに送信します
    • AはTIME_WAIT状態になります
  6. BはACK を受け取ります
    • BはCLOSED状態になります。つまり、ソケットテーブルから削除されます。

TIME_WAIT

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

TIME_WAIT状態が私たちの友人である理由を理解するには、スティーブンス他による「UNIXネットワークプログラミング」第3版のセクション2.7(43ページ)をお読みください。

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

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

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

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

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

繰り返しますが、「UNIX Network Programming」第3版202-203によれば、close()を呼び出す前にSO_LINGERをタイムアウト0に設定すると、通常の終了シーケンスnot開始されます。

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

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

ただし、特定の状況では、とにかくそうする必要があります。

  • サーバーアプリケーションのクライアントが誤動作する(タイムアウト、無効なデータを返す、など)場合は、CLOSE_WAIT状態に陥ったり、TIME_WAIT状態になることを避けるために、強制終了を意味します。
  • 現在数千のクライアント接続があるサーバーアプリケーションを再起動する必要がある場合は、TIME_WAIT(サーバーエンドからclose()を呼び出すとき)で数千のサーバーソケットを回避するために、このソケットオプションを設定することを検討してくださいサーバーは、再起動後に新しいクライアント接続に使用可能なポートを取得できません。
  • 前述の本の202ページには、「この機能を使用して強制終了を送信する必要がある特定の状況があります。1つの例はRS-232ターミナルサーバーであり、CLOSE_WAITスタックされた端末ポートにデータを送信しますが、保留中のデータを破棄するRSTを取得した場合、スタックポートを適切にリセットします。」

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

168
mgd

リンガーがオンでタイムアウトがゼロの場合、TCPスタックは、接続を閉じる前に保留中のデータが送信されるのを待機しません。これによりデータが失われる可能性がありますが、このようにリンガーを設定することにより、これを受け入れ、接続を正常に閉じるのではなく、すぐにリセットするように求めます。これにより、通常のFINではなくRSTが送信されます。

EJPのコメントに感謝します。詳細については、 here を参照してください。

16
Len Holgate

コード内のリンガーを安全に削除できるかどうかは、アプリケーションのタイプによって異なります。それは「クライアント」(TCP接続を開いて最初にアクティブに閉じる)か「サーバー」( TCPをリッスンし、反対側がクローズを開始した後にオープンしてクローズしますか?

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

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

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

5
Grandswiss