私はオプションの正式な意味を理解していると思います。現在処理している一部のレガシーコードでは、このオプションが使用されています。顧客は、その側から近い接続でのその側からのFINへの応答としてRSTについて不満を言います。
いつ使用すべきかわからないため、安全に削除できるかどうかはわかりません。
オプションが必要になる時期の例を教えてください。
SO_LINGER
タイムアウトを0に設定する一般的な理由は、TIME_WAIT
状態にある多数の接続を回避し、サーバー上の使用可能なすべてのリソースを占有することです。
TCP接続が完全に閉じられると、閉じを開始した側(「アクティブな閉じる」)が数分間TIME_WAIT
に接続された状態になります。そのため、プロトコルがserverで接続のクローズを開始し、非常に多数の短命の接続が含まれるプロトコルの場合、この問題の影響を受けやすい可能性があります。
ただし、これは良い考えではありません-TIME_WAIT
が理由で存在します(古い接続からの浮遊パケットが新しい接続に干渉しないようにするため)。可能であれば、クライアントが接続を開始するプロトコルにプロトコルを再設計することをお勧めします。
私の提案については、最後のセクションをお読みください:「タイムアウト0でSO_LINGERを使用する場合」。
その前に、次のことについて少し講義します。
TIME_WAIT
FIN
、ACK
、およびRST
通常のTCP終了シーケンスは次のようになります(簡略化)。
2つのピア:AとBがあります
close()
を呼び出しますFIN
をBに送信しますFIN_WAIT_1
状態になりますFIN
を受け取りますACK
をAに送信しますCLOSE_WAIT
状態になりますACK
を受け取ります。FIN_WAIT_2
状態になりますclose()
[.____を呼び出します。]FIN
をAに送信しますLAST_ACK
状態になりますFIN
を受け取ります。ACK
をBに送信しますTIME_WAIT
状態になりますACK
を受け取りますCLOSED
状態になります。つまり、ソケットテーブルから削除されます。したがって、終了を開始するピア(つまり、最初にclose()
を呼び出す)はTIME_WAIT
状態になります。
TIME_WAIT
状態が私たちの友人である理由を理解するには、スティーブンス他による「UNIXネットワークプログラミング」第3版のセクション2.7(43ページ)をお読みください。
ただし、サーバー上のTIME_WAIT
状態の多くのソケットでは、新しい接続が受け入れられなくなる可能性があるため、問題になる可能性があります。
この問題を回避するために、close()
を呼び出す前に、タイムアウトが0のSO_LINGERソケットオプションを設定することを提案する人がたくさんいます。ただし、TCP接続がエラーで終了するため、これは悪い解決策です。
代わりに、接続終了が常にクライアント側から開始されるようにアプリケーションプロトコルを設計します。クライアントは、残りのすべてのデータをいつ読み取ったかを常に把握している場合、終了シーケンスを開始できます。例として、ブラウザはContent-Length
HTTPヘッダーからすべてのデータを読み取り、クローズを開始できることを認識します。 (HTTP 1.1では、再利用の可能性のためにしばらく開いたままにしてから閉じます。)
サーバーが接続を閉じる必要がある場合、サーバーがクライアントにclose()
を呼び出すように要求するようにアプリケーションプロトコルを設計します。
繰り返しますが、「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()
を呼び出すとき)で数千のサーバーソケットを回避するために、このソケットオプションを設定することを検討してくださいサーバーは、再起動後に新しいクライアント接続に使用可能なポートを取得できません。CLOSE_WAIT
スタックされた端末ポートにデータを送信しますが、保留中のデータを破棄するRST
を取得した場合、スタックポートを適切にリセットします。」this 長い記事をお勧めします。これはあなたの質問に非常に良い答えを与えると信じています。
リンガーがオンでタイムアウトがゼロの場合、TCPスタックは、接続を閉じる前に保留中のデータが送信されるのを待機しません。これによりデータが失われる可能性がありますが、このようにリンガーを設定することにより、これを受け入れ、接続を正常に閉じるのではなく、すぐにリセットするように求めます。これにより、通常のFINではなくRSTが送信されます。
EJPのコメントに感謝します。詳細については、 here を参照してください。
コード内のリンガーを安全に削除できるかどうかは、アプリケーションのタイプによって異なります。それは「クライアント」(TCP接続を開いて最初にアクティブに閉じる)か「サーバー」( TCPをリッスンし、反対側がクローズを開始した後にオープンしてクローズしますか?
アプリケーションに「クライアント」のフレーバー(最初に閉じる)があり、異なるサーバーへの膨大な数の接続を開始および閉じた場合(たとえば、アプリが膨大な数の異なるサーバーの到達可能性を監視する監視アプリである場合)すべてのクライアント接続がTIME_WAIT状態のままになる問題があります。その後、タイムアウトをデフォルトよりも小さい値に短縮して、正常にシャットダウンしますが、クライアント接続リソースをより早く解放することをお勧めします。 0はFINでは正常にシャットダウンせず、RSTでは異常終了するため、タイムアウトを0に設定しません。
アプリケーションに「クライアント」のフレーバーがあり、同じサーバーから大量の小さなファイルを取得する必要がある場合、ファイルごとに新しいTCP接続を開始して、大量のTIME_WAITのクライアント接続。ただし、接続を開いたままにして、同じ接続を介してすべてのデータをフェッチします。リンガーオプションは削除できますし、削除する必要があります。
アプリケーションが「サーバー」(ピアのクローズに対する反応として2番目にクローズ)の場合、close()で接続が正常にシャットダウンされ、TIME_WAIT状態に入らないためリソースが解放されます。リンガーは使用しないでください。ただし、サーバーアプリケーションに、非アクティブな開いている接続が長時間アイドル状態になっていることを検出する監視プロセスがある場合(「長い」を定義する必要があります)、この非アクティブな接続を側からシャットダウンすることができます。これは、リンガータイムアウトを0に設定することで行われます。close()は、クライアントにRSTを送信し、怒っていることを伝えます:-)