Armadilloフォーラム

LAN通信でメッセージ送信時にプログラムが終了する

atom

2015年3月10日 14時18分

お世話になります。
佐藤です。

原因はプログラムによるものと思われますが対処方がわからないので対処法のご教授をよろしくお願いします。

現象
現在のプログラムは以下のような処理を想定しています。

(1) ソケット作成
sock = socket(AF_INET, SOCK_STREAM, 0);
(2) connect実施
connect(sock, (struct sockaddr *)&server, sizeof server);
(3) fdの追加
FD_SET(sock, &readfds);
(4) select()を使用して受信データを待つ(タイムアウトあり)
memcpy(&fds, &readfds, sizeof(fd_set);
select(max_fd+1, &fds, NULL, NULL, &tv);
(5) 受信データがある場合は受信データの処理し、処理終了後(6)へ
(6) 受信データとは関係なく行う処理(定期的に送信するメッセージなど)後(4) へ戻る
※(5)で回線切断と判断された場合はsocketを破棄して(1)から再度実行するようにしたい

現時点での問題点
(6) の"write"実行時にプログラムが終了してしまうことがある。
条件としては、相手側(サーバー)が切断されたあとに起こることがあるため、(4)、(5)による相手側の切断
の判定処理後に回線切断となり、(6)でwriteコマンドによる送信が生じているためと考えられます。

以下のいずれかの方法をとることを考えていますが対処可能でしょうか。また、どのように処理をすればい
いのでしょうか?
(1) write実行前にwrite可能かを識別する
(2) 相手側の回線切断後にwriteを実行してもプログラムが終了されないようにする

以上、対処方法などありましたらご教授ください。

コメント

y.nakamura

2015年3月10日 14時53分

中村です。

> (6) の"write"実行時にプログラムが終了してしまうことがある。
> 条件としては、相手側(サーバー)が切断されたあとに起こることがあるため、(4)、(5)による相手側の切断
> の判定処理後に回線切断となり、(6)でwriteコマンドによる送信が生じているためと考えられます。

ソケットの切断で生じるのであれば、write時の
SIGPIPEによる強制終了だと思います。

> (1) write実行前にwrite可能かを識別する

これをやったとして(調べることができたとして)も、
write可能かを調べたあと、writeするまでの間に
ソケットが切れれば同じことです。

> (2) 相手側の回線切断後にwriteを実行してもプログラムが終了されないようにする

SIGPIPEをトラップしてみてください。

--
なかむら

atom

2015年3月10日 16時00分

回答ありがとうございます。
SIGPIPEのトラップについていまひとつわからなかったのですが、send()を使用することによりSIGPIPEを無効かすることができる
といことがわかったので以下のように修正しました。

変更前:write(sock, data, lng) ;
変更後:send(sock, data, lng, MSG_NOSIGNAL) ;

現時点で問題が起こらなくなることを確認できました。
回答ありがとうございました。

y.nakamura

2015年3月10日 17時10分

中村です。

> 変更後:send(sock, data, lng, MSG_NOSIGNAL) ;

これでもいいですね。

> SIGPIPEのトラップについていまひとつわからなかったのですが

せっかくなので説明しておきます。

「トラップ」と書いたのがわかりにくかったかもしれません。
シグナルハンドラを設定するか無視するか、なのですけど、
無視するようにするだけでもいいと思います。
signal(SIGPIPE, SIG_IGN);
です。

SIGPIPEを無視するようにしてからwrite()するのと、
send(x,x,x,MSG_NOSIGNAL)するとので、
効果(動作)は同じだと思います。
どちらもソケットが切れたときにデータ送信しようとすると
-1でreturnしてerrno=EPIPEです。

--
なかむら