- ベストアンサー
SocketのSend関数でのCLOSEの検知 [Linux]
Linux環境でSocket(dm:PF_INET,type:SOCK_STREAM)を使用しての、 Client&ServerプログラムをCで作成しているのですが、 そこでのSend関数の使い方についてご助力ください。 Client&Serverプログラムは下記のような動きをします。 [Client] ServerへConnectした後、複数のDataを数秒間隔でServerへ 送信(send関数使用)します。受信(recvやread関数等)は、 一切行いません。 [Server] ClientからのConnectを受け付けた後、Clientから受信(recv関数 使用)したDataを標準出力へ表示する。送信(sendやwrite関数 等)は、一切行いません。 さて、ここでもしClientプログラムがCloseを発行したり、マシン DOWN等の理由でConnectionが切断され、Server側のSocketが CLOSE_WAIT状態になった場合、Bufferに溜まっていたDataを すべて受けきった後、recv関数が0を返してくれるので 相手が終了したことがわかります。 ここからが質問のMainです。 では、もしServerプログラムがCloseを発行したり、マシン DOWN等の理由でConnectionが切断され、Client側のSocketが CLOSE_WAIT状態になっても、CLOSE_WAIT直後のsend関数が なぜか正常に処理されてしまいます。無論このDataは、 Server側は受け取りません。この次のsend関数実行時に EPIPEが返ってくるので、ここでようやくSocketが切断された ことが判ります。 これを何とかCLOSE_WAIT状態になった直後から、send関数で 切断を検知できるようにできないでしょうか。 よろしくお願いします。 以上
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
#3です。 >完全な保証ではないのですが、”送れた/送れない”をきちんと ログとして残す必要があるのです。 なるほど、そういう事情でしたか。それだと、それなりの信憑性が要求されますね。 >それと最初にご提案いただいたrecvをNonBlockモードで呼び出す 方法ですが、これだとrecvのリターン値は0が返ってこないでしょうか? Linuxで以下のような、プログラムを組んで確認しました。 //送信前に、ノンブロッキングでrecvする。 ret = recv(sock,rbuf,sizeof(rbuf),MSG_DONTWAIT); if (ret == -1 && errno == EAGAIN){ //正常なので送信可能 ret = send(sock,msg[i],sendlen,0); if (ret != sendlen){ //送信エラーの処理 } }else{ //切断検知時の処理 // CLOSE_WAITになると ret=0が返る } サーバー側で切断時、直ちにrecvで戻り値=0となり、エラーの検知ができました。ret==-1 かつ errno==EAGAIN であれば、回線は正常状態です。
その他の回答 (3)
- tatsu99
- ベストアンサー率52% (391/751)
すみません。全然回答になっていないんですが、 >これから作ろうとしている本プログラムでは、実はもっと送信量は 増えるんです。このプログラムは常時接続型を考えていまして、 めったに切断は起こりません。 ということであれば、 CLOSE_WAIT状態になった直後のsend関数で切断を検知できなくても、次のsendで、切断が検知できますので、運用上は特に、問題ないと思うのですが、いかがでしょうか。 CLOSE_WAIT状態になった直後のsend関数で、必ずエラーを検知することに、こだわる必要性がわかりません。できましたら、その理由を教えていただけませんでしょうか。(何故、次のsendでのエラー検知ではいけないのでしょうか)
補足
幾度も返信いただきありがとうございます。 >CLOSE_WAIT状態になった直後のsend関数で切断を検知できなくても、 >次のsendで、切断が検知できますので、運用上は特に、問題ないと >思うのですが、いかがでしょうか。 実は、データの保証をしないといけないという事情があります。 完全な保証ではないのですが、”送れた/送れない”をきちんと ログとして残す必要があるのです。 それと最初にご提案いただいたrecvをNonBlockモードで呼び出す 方法ですが、これだとrecvのリターン値は0が返ってこないでしょうか? それにしても、Winsockだとただしくエラーが検知できてWSAESHUTDOWNの エラーコードが取得できるのに、なぜUnixだとだめなんでしょうか。 って愚痴ってもしょうがないですね。 何かよい方法があればお願いします。 よろしくお願いいたします。
- tatsu99
- ベストアンサー率52% (391/751)
>この次のsend関数実行時に EPIPEが返ってくるので、ここでようやくSocketが切断されたことが判ります。 この次のsend関数実行迄に、相当の時間間隔が発生する為、もっと素早く、障害を検知したいということであれば、以下の方法で対処できるはずです。 まず、ソケットをノンブロッキングモードにします。 次に、一定間隔でrecv関数を呼び出し、サーバーからのデータを受信します。もちろん、サーバはデータを送ってないので、「データなし」の状態が、続きます。しかしながら、Connectionが切断された場合は、recv関数からエラーが返ります。
補足
返信ありがとうございます。 recv関数をノンブロッキングモードで呼び出すことは、一応考えました。 でも、質問にある 『複数のDataを数秒間隔で・・・・』 というのは、実はテストプログラムなんです。 これから作ろうとしている本プログラムでは、実はもっと送信量は 増えるんです。このプログラムは常時接続型を考えていまして、 めったに切断は起こりません。めったにおきない現象のために、 余計な処理を増やしたくないということもあります。 なんとかないでしょうか、send関数で判定する方法。 それともあきらめるしかないのでしょうか。 わがままで申し訳ありませんが、よろしくお願いします。
- notnot
- ベストアンサー率47% (4900/10358)
>CLOSE_WAIT直後のsend関数が >なぜか正常に処理されてしまいます。無論このDataは、 >Server側は受け取りません。この次のsend関数実行時に >EPIPEが返ってくるので、 このあたりの表現が気になるのですが、send のリターン値をちゃんと見ていますか?非負で引数で与えたデータ長より小さい値が返ったら残りデータを再度sendしなければなりません。そのとき-1が返るのでは?
補足
返信ありがとうございます。 sendのリターン値は、ちゃんと判定しています。 >CLOSE_WAIT直後のsend関数が >なぜか正常に処理されてしまいます。 このときも、ちゃんとリターン値を判定しています。 このときsendの引数に渡したLengthSizeと同じ 値が返ってきてしまいます。 よろしくお願いします。
お礼
プログラムを組んでまで、確認していたいたのですね。 多謝多謝です。 やはりSendだけでは、厳しそうですね。 tatsu99さんの案も候補にして、今後の方針を探って いこうとおもいます。 ありがとうございました。