281

TCPまたはローカルUNIXソケットで接続を受け入れ、単純なコマンドを読み取り、(コマンドに応じて)応答を送信する小さなサーバープログラムがあります。

問題は、クライアントが答えに興味がなく、場合によっては早期に終了する可能性があることです。そのため、そのソケットに書き込むとSIGPIPE、サーバーがクラッシュします。

ここでのクラッシュを防ぐためのベストプラクティスは何ですか?行の反対側がまだ読んでいるかどうかを確認する方法はありますか?(select()ソケットは書き込み可能であると常に表示されるため、ここでは機能しないようです)。SIGPIPEまたは、ハンドラーでをキャッチして無視する必要がありますか?

4

10 に答える 10

281

通常、を無視してSIGPIPE、コードで直接エラーを処理する必要があります。これは、Cのシグナルハンドラーには、実行できることに対して多くの制限があるためです。

これを行う最も移植性の高い方法は、SIGPIPEハンドラーをに設定することSIG_IGNです。これにより、ソケットまたはパイプの書き込みによってSIGPIPE信号が発生するのを防ぐことができます。

信号を無視するSIGPIPEには、次のコードを使用します。

signal(SIGPIPE, SIG_IGN);

呼び出しを使用している場合send()、別のオプションは、呼び出しごとに動作をオフにするMSG_NOSIGNALオプションを使用することです。SIGPIPEすべてのオペレーティングシステムがMSG_NOSIGNALフラグをサポートしているわけではないことに注意してください。

最後に、一部のオペレーティングシステムSO_SIGNOPIPEで設定できるソケットフラグについても検討することをお勧めします。これにより、設定されているソケットへの書き込みだけが原因にsetsockopt()なるのを防ぐことができます。SIGPIPE

于 2008-09-20T13:46:21.210 に答える
159

もう 1 つの方法は、write() で SIGPIPE を生成しないようにソケットを変更することです。これは、SIGPIPE のグローバル シグナル ハンドラーが必要ないライブラリでより便利です。

ほとんどの BSD ベース (MacOS、FreeBSD...) システム (C/C++ を使用していると仮定) では、次のようにしてこれを行うことができます。

int set = 1;
setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int));

これにより、SIGPIPE シグナルが生成される代わりに EPIPE が返されます。

于 2009-01-16T11:25:00.997 に答える
119

私はパーティーに非常に遅れていSO_NOSIGPIPEますが、移植性がなく、システムで動作しない可能性があります (BSD のようです)。

たとえば、Linux システムを使用している場合の良い代替方法は、send(2) 呼び出しでフラグSO_NOSIGPIPEを設定することです。MSG_NOSIGNAL

write(...)置換の例send(...,MSG_NOSIGNAL)( nobarのコメントを参照)

char buf[888];
//write( sockfd, buf, sizeof(buf) );
send(    sockfd, buf, sizeof(buf), MSG_NOSIGNAL );
于 2009-11-10T04:58:36.217 に答える
29

この投稿では、SO_NOSIGPIPE も MSG_NOSIGNAL も使用できない場合の Solaris のケースで考えられる解決策について説明しました。

代わりに、ライブラリ コードを実行する現在のスレッドで SIGPIPE を一時的に抑制する必要があります。これを行う方法は次のとおりです。 SIGPIPE を抑制するには、まず保留中かどうかを確認します。存在する場合、これはこのスレッドでブロックされていることを意味し、何もする必要はありません。ライブラリが追加の SIGPIPE を生成する場合、それは保留中のものとマージされます。これはノーオペレーションです。SIGPIPE が保留中でない場合は、このスレッドでブロックし、既にブロックされているかどうかも確認します。その後、自由に書き込みを実行できます。SIGPIPE を元の状態に戻すときは、次のことを行います。SIGPIPE が最初に保留中だった場合は、何もしません。それ以外の場合は、現在保留中かどうかを確認します。そうである場合 (これは、out アクションが 1 つ以上の SIGPIPE を生成したことを意味します)、このスレッドでそれを待ちます。したがって、保留中のステータスをクリアします (これを行うには、タイムアウトなしで sigtimedwait() を使用します。これは、悪意のあるユーザーが SIGPIPE をプロセス全体に手動で送信したシナリオでのブロックを回避するためです。この場合、保留中と表示されますが、他のスレッドが変更を待つ前に処理してください)。保留中のステータスをクリアした後、このスレッドで SIGPIPE のブロックを解除しますが、最初にブロックされていない場合に限ります。

https://github.com/kroki/XProbes/blob/1447f3d93b6dbf273919af15e59f35cca58fcc23/src/libxprobes.c#L156のコード例

于 2010-02-27T15:30:57.683 に答える
22

SIGPIPE をローカルで処理する

通常は、グローバル シグナル イベント ハンドラーではなく、ローカルでエラーを処理することをお勧めします。これは、何が起こっているのか、どの手段を取るべきかについて、ローカルでより多くのコンテキストを把握できるためです。

アプリの 1 つに、アプリが外部アクセサリと通信できるようにする通信レイヤーがあります。書き込みエラーが発生すると、通信レイヤーで例外をスローし、それを try catch ブロックにバブルアップさせてそこで処理します。

コード:

ローカルで処理できるように SIGPIPE シグナルを無視するコードは次のとおりです。

// We expect write failures to occur but we want to handle them where 
// the error occurs rather than in a SIGPIPE handler.
signal(SIGPIPE, SIG_IGN);

このコードは SIGPIPE シグナルの発生を防ぎますが、ソケットを使用しようとすると読み取り/書き込みエラーが発生するため、それを確認する必要があります。

于 2012-01-27T16:04:09.180 に答える
14

パイプの遠端のプロセスが終了するのを防ぐことはできません。書き込みが完了する前にプロセスが終了すると、SIGPIPEシグナルが発生します。シグナルをSIG_IGNすると、書き込みはエラーで返されます。そのエラーに注意して対応する必要があります。ハンドラーでシグナルをキャッチして無視することはお勧めできません。パイプが機能しなくなったことに注意し、プログラムの動作を変更して、パイプに再度書き込まれないようにする必要があります(シグナルが再度生成され、無視されるため)もう一度やり直してください。プロセス全体が長時間続き、多くのCPUパワーを浪費する可能性があります)。

于 2008-09-20T14:45:34.733 に答える
6

または、ハンドラーでSIGPIPEをキャッチして、無視する必要がありますか?

私はそれが正しいと信じています。もう一方の端が記述子を閉じたときを知りたいのですが、それがSIGPIPEからの指示です。

サム

于 2008-09-20T13:45:23.893 に答える
3

ここでクラッシュを防ぐためのベストプラクティスは何ですか?

みんなのようにシグパイプを無効にするか、エラーをキャッチして無視します。

回線の反対側がまだ読んでいるかどうかを確認する方法はありますか?

はい、select() を使用します。

select() は、ソケットが書き込み可能であると常に言っているため、ここでは機能していないようです。

読み取りビットを選択する必要があります。書き込みビットはおそらく無視できます。

遠端がファイル ハンドルを閉じると、select は、読み取る準備ができているデータがあることを通知します。これを読み込んでみると、0 バイトが返されます。これは、ファイル ハンドルが閉じられたことを OS が通知する方法です。

書き込みビットを無視できない唯一のケースは、大量のボリュームを送信している場合であり、相手側でバックログが発生するリスクがあり、バッファーがいっぱいになる可能性があります。その場合、ファイル ハンドルに書き込もうとすると、プログラム/スレッドがブロックされるか失敗する可能性があります。書き込みの前に選択をテストすると、それから保護されますが、相手が正常であることや、データが到着することを保証するものではありません。

書き込み時と同様に、close() から sigpipe を取得できることに注意してください。

Close は、バッファリングされたデータをすべてフラッシュします。もう一方の端が既に閉じられている場合、close は失敗し、sigpipe を受け取ります。

バッファリングされた TCPIP を使用している場合、書き込みが成功したということは、データが送信のためにキューに入れられたことを意味するだけであり、送信されたことを意味するわけではありません。close を正常に呼び出すまで、データが送信されたことはわかりません。

Sigpipe は、何か問題が発生したことを通知しますが、何をすべきか、何をすべきかについては通知しません。

于 2015-09-15T23:57:28.827 に答える