ソケットを閉じるかシャットダウンするか?


214

Cでは、ソケットを閉じると、ソケットが破棄され、後で再利用できることを理解しました。

シャットダウンはどうですか?説明は、そのソケットへの二重接続の半分を閉じると述べています。しかし、そのソケットはcloseシステムコールのように破棄されますか?


後で再利用することはできません。閉まっています。終了しました。できました。
ローン侯爵

回答:


191

これは、Beejのネットワーキングガイドで説明されています。 shutdown一方向または双方向の通信をブロックする柔軟な方法です。2番目のパラメータがの場合、SHUT_RDWR送信と受信の両方をブロックします(などclose)。しかしながら、close実際にソケットを破棄する方法です。

を使用shutdownすると、ピアがすでに送信した保留中のデータを引き続き受信できます(これを指摘してくれたJoey Adamsに感謝します)。


23
TCPソケットをclose()した場合でも、OSは新しい情報として混乱する可能性のある未解決のパケットがないことを確認しながら、TIME_WAIT状態になるため、TCPソケットをすぐに再利用できるとは限りません。そのソケットをすぐに別のものに再利用することでした。
alesplin 2010

91
ソケットのシャットダウンとクローズの大きな違いは、ソケットが他のプロセスによって共有されている場合の動作です。shutdown()はソケットのすべてのコピーに影響しますが、close()は1つのプロセスのファイル記述子にのみ影響します。
Zan Lynx

5
shutdown両方向を使用したいが、を使用してソケットへの参照をclose作成したかどうかが理由の1つです。あなたの場合はソケット、新しくオープンしたファイルは、同じFDを割り当てることができ、そしてその後の使用が非常に悪い可能性が間違った場所を書き込み/読み込みます。だけの場合、後続のを使用すると、が呼び出されるまでエラーが発生します。FILEfdopencloseFILEshutdownFILEfclose
R .. GitHub ICE HELPING ICEの停止

25
TIME_WAITに関するコメントは正しくありません。これは、ソケットではなくポートに適用されます。ソケットを再利用することはできません。
ローン侯爵

48
-1。この投稿とリンクの両方で、使用したい重要な概念上の理由が省略されていますshutdown。ピアにEOFを通知し、ピアが送信した保留中のデータを引き続き受信できるようにするためです。
ジョーイアダムス

144

既存の回答はどれshutdownclose、TCPプロトコルレベルでどのように機能するかをユーザーに伝えないため、これを追加する価値があります。

標準のTCP接続は、4方向のファイナライズによって終了します。

  1. 送信するデータがなくなった参加者は、FINパケットを他の参加者に送信します
  2. 相手はFINのACKを返します。
  3. 相手もデータ転送を終了すると、別のFINパケットを送信します
  4. 最初の参加者はACKを返し、転送を完了します。

ただし、TCP接続を閉じるには別の「緊急」の方法があります。

  1. 参加者はRSTパケットを送信し、接続を破棄します
  2. 反対側はRSTを受信し、接続を放棄します

Wiresharkを使用した私のテストでは、デフォルトのソケットオプションを使用して、shutdownFINパケットをもう一方の端に送信しますが、それだけです。相手がFINパケットを送信するまで、データを受信できます。これが発生するReceiveと、サイズは0になります。したがって、「送信」を最初にシャットダウンする場合は、データの受信が終了したらソケットを閉じる必要があります。

一方、 close一方、接続がアクティブな間に(反対側はまだアクティブであり、システムバッファーに未送信のデータがある場合もあります)、RSTパケットが反対側に送信されます。これはエラーに適しています。たとえば、相手が間違ったデータを提供した、またはデータの提供を拒否した(DOS攻撃?)と思われる場合は、すぐにソケットを閉じることができます。

ルールについての私の意見は:

  1. 可能な場合はshutdown事前closeに検討する
  2. シャットダウンする前に受信を終了した場合(0サイズのデータ​​を受信)、最後の送信(存在する場合)が終了した後で接続を閉じます。
  3. 接続を正常に閉じる場合は、接続をシャットダウンし(SHUT_WRを使用し、この時点以降にデータを受信する必要がない場合は、SHUT_RDも使用します)、サイズ0のデータを受信するまで待ってから、ソケット。
  4. いずれの場合も、他のエラー(タイムアウトなど)が発生した場合は、ソケットを閉じてください。

SHUT_RDおよびSHUT_WRの理想的な実装

以下はテストされていません。自己責任で信頼してください。ただし、これは合理的で実用的な方法です。

TCPスタックがSHUT_RDのみでシャットダウンを受信した場合、TCPスタックは、この接続に予期されるデータがないとマークします。保留中のreadリクエストと後続のリクエストは(どのスレッドにあるかに関係なく)、サイズがゼロの結果で返されます。ただし、接続はまだアクティブで使用可能です-たとえば、OOBデータを引き続き受信できます。また、OSはこの接続で受信したデータをすべてドロップします。しかし、それだけです。パッケージは反対側に送信されません。

TCPスタックがSHUT_WRのみでシャットダウンを受信した場合、これ以上データを送信できないため、この接続にマークを付けます。保留中の書き込み要求はすべて終了しますが、後続の書き込み要求は失敗します。さらに、FINパケットが別の側に送信され、送信するデータがないことを通知します。


2
「接続がまだ有効であるときにcloseを呼び出した場合」はトートロジーであり、RSTが送信されることはありません。(1)必要ありません。(4)では、タイムアウトは必ずしも接続にとって致命的ではなく、接続を閉じることができることを常に示しています。
ローンの侯爵2014年

4
@EJPいいえ、トートロジーではありません。あなたshutdown()は接続をすることができ、それからそれはもはや生きていません。まだファイル記述子があります。まだrecv()受信バッファーからできます。またclose()、ファイル記述子を破棄するために呼び出す必要があります。
PavelŠimerda16年

これは、ワイヤー形式に関する最良の答えです。SHUT_RDによるシャットダウンの詳細について興味があります。より多くのデータを期待しないためのTCPシグナリングはありませんよね?これ以上データを送信しないためのシグナリング用のFINだけがないのですか?
PavelŠimerda2016年

3
@PavelŠimerdaはいTCPは、これ以上のデータを期待しないことを通知しません。これは高水準プロトコルで考慮されるべきです。私の意見では、これは一般に必要ではありません。ドアを閉めることはできますが、ドアの前にプレゼントを置く人を止めることはできません。これはあなたの決定ではなく、彼らの決定です。
アースエンジン

1
@EJP実際の議論はありますか?そうでなければ私は興味がありません。
PavelŠimerda2016年

35

代わりにclose()使用すれば回避できるいくつかの制限がありますshutdown()

close()TCP接続で双方向を終了します。他のエンドポイントにデータの送信が終了したことを伝えたいが、それでもデータを受信したい場合があります。

close()記述子参照カウント(ファイルテーブルエントリに保持され、ファイル/ソケットを参照している現在開いている記述子の数をカウントします)をデクリメントし、記述子が0でない場合はソケット/ファイルを閉じません。これは、フォークしている場合、クリーンアップは、参照カウントが0になった後にのみ行われshutdown()ます。参照カウントを無視して、通常のTCPクローズシーケンスを開始できます。

パラメータは次のとおりです。

int shutdown(int s, int how); // s is socket descriptor

int how 次のいずれかになります。

SHUT_RDまたはそれ0 以上の受信は許可されていません

SHUT_WRまたはそれ1 以上の送信は許可されていません

SHUT_RDWRまたはそれ2 以上の送受信は許可されていません


8
2つの機能は完全に異なる目的のためのものです。ソケットの最後のクローズがまだ開始されていない場合にシャットダウンシーケンスを開始するという事実は、クローズがソケットデータ構造のクリーンアップのためであり、シャットダウンがtcpレベルのシャットダウンシーケンスを開始するためであるという事実を変更しません。
Len Holgate、2010年

25
「代わりにシャットダウンを使用する」ことはできません。あなたもそれ使うことができますただし、しばらくしてソケットを閉じる必要があります。
ローン侯爵、

15

これはプラットフォーム固有の可能性がありますが、どういうわけか疑いますが、とにかく、私が見た最良の説明は、このmsdnページにあり、シャットダウン、残存オプション、ソケットクロージャ、および一般的な接続終了シーケンスについて説明しています。

要約すると、shutdownを使用してTCPレベルでシャットダウンシーケンスを送信し、closeを使用してプロセスのソケットデータ構造で使用されているリソースを解放します。closeを呼び出すまでに明示的なシャットダウンシーケンスを発行していない場合は、シャットダウンシーケンスが開始されます。


9

Linuxではshutdown()、あるpthreadを使用して、現在ブロックされconnect()ている別のpthreadを強制的に早期に中止することもできました。

他のOS(少なくともOSX)では、呼び出しclose()connect()失敗するのに十分であることがわかりました。


7

「shutdown()は実際にはファイル記述子を閉じません。それは、その使いやすさを変更するだけです。ソケット記述子を解放するには、close()を使用する必要があります。」1


3

閉じる

ソケットの使用が終了したら、closeを使用してファイル記述子を閉じるだけです。接続を介して送信されるのを待っているデータがまだある場合、通常、closeはこの送信を完了しようとします。SO_LINGERソケットオプションを使用してこの動作を制御し、タイムアウト期間を指定できます。ソケットオプションを参照してください。

シャットダウン

また、shutdownを呼び出して、接続の受信または送信のみをシャットダウンすることもできます。

シャットダウン機能は、ソケットの接続をシャットダウンします。その引数howは、実行するアクションを指定します。0このソケットのデータの受信を停止します。さらにデータが到着した場合は、それを拒否します。1このソケットからのデータ送信の試行を停止します。送信を待機しているデータを破棄します。すでに送信されたデータの確認を探すのをやめます。紛失した場合は再送信しないでください。2受信と送信の両方を停止します。

戻り値は、成功した場合は0、失敗した場合は-1です。


2

私のテストでは。

close ソケットが他のプロセスと共有されていない場合、finパケットを送信し、fdを即座に破棄します

shutdown SHUT_RD、プロセスは引き続きソケットからデータを受信できますが、recvTCPバッファーが空の場合は0 recvを返します。ピアがさらにデータを送信した後、再びデータを返します。

shutdown SHUT_WRは、finパケットを送信して、それ以上の送信が許可されていないことを示します。ピアはデータを受信できますが、TCPバッファが空の場合は0を受信します

shutdown SHUT_RDWRSHUT_RDSHUT_WRの両方を使用するのと同じ)は、ピアがさらにデータを送信する場合、最初のパケットを送信します。


私はそれを試しclose()、FINの代わりにLinuxでRST を送信しました。
PavelŠimerda2016年

また、読み取り側をシャットダウンした後、プログラムがより多くのデータを受信することを本当に述べたいと思いましたか?
PavelŠimerda2016年

@PavelŠimerdaRSTまたはFINを送信するのはtcpの状態に依存していると思いますか?よく分かりません。
simpx 2016年

1.「ピアがさらにデータを送信した後、recv()再びデータを返します」は正しくありません。2.ピアが後にさらにデータを送信する場合の動作SHUT_RDは、プラットフォームに依存します。
ローンの侯爵

@EJP自分で試してみました。申し訳ありませんが、centosまたはMacのどちらのプラットフォームでこのテストを行ったかを忘れてしまいました。そして、これは私が手に入れたものです
simpx '25 / 11/16
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.