Armadilloフォーラム

ソケット接続が出来なくなる

n.yamamoto

2016年12月20日 13時35分

Yamamotoです。

Armadillo-460とPCをLAN接続してソケット通信を行っていますが、2ヶ月位運用するとソケットが接続しなくなります。

ソケットはArmadillo側がサーバーで、同じポート番号で1本は常時接続、1本は多分5分周期位でサーバー側は接続->受信->送信->切断(クライアントから)を繰り返しています。

2ヶ月位すると、まず5分周期位で接続->通信->切断を繰り返しているほうが接続できなくなって、次に常時接続しているほうが繋がらなくなります。(多分、常時接続側は何処かでエラーになり再接続で接続できなくなっているのではないかと思っています)
クライアントからは接続して、送信に対する応答が無い状態。
サーバー側は接続要求が無い状態ですれ違い状態に見えます。

netstatを見ると、正常時はESTABLISHEDになっていますが、繋がらなくなるとSYN_RECVになっています。

ネットで調べるとbacklogとかtcp_max_syn_backlogがフル(Acceptキューフルというのでしょうか)になると上記のような状態になるとありましたが、ソケット定期的に最短5分程度で接続切断を繰り返しているとこのような状態になったりするのでしょうか?

また、このような状態になりえるのであれば、回避策としてどのようなことを行えばよいでしょうか?

よろしくお願いいたします。

コメント

Yamamotoです。

現状、繋がらなくなっている状態のパケットキャプチャーを入手できましたのでアップしておきます。

発生もとの繋がらない状態のパケットキャプチャーで、前投稿内容は再現していただいたところでの確認ないようです。

何か気づいた点がありましたら教えてください!

ファイル ファイルの説明
TCPキャプチャー.png

Yamamotoです

もう一点気になる事がありました。

messagesに以下の内容がありました。
何か問題あるのでしょうか?

<28>Dec 23 17:45:28 avahi-daemon[1237]: socket() failed: Address family not supported by protocol
<29>Dec 23 17:45:28 avahi-daemon[1237]: Failed to create IPv6 socket, proceeding in IPv4 only mode
<30>Dec 23 17:45:28 avahi-daemon[1237]: New relevant interface eth0.IPv4 for mDNS.
<30>Dec 23 17:45:28 avahi-daemon[1237]: Joining mDNS multicast group on interface eth0.IPv4 with address 192.168.0.40.
<30>Dec 23 17:45:28 avahi-daemon[1237]: Network interface enumeration completed.
<30>Dec 23 17:45:28 avahi-daemon[1237]: Registering new address record for 192.168.0.40 on eth0.

wiresharkの画面からデコードするのは辛いので、もう少し教えてください。

- armadillo は192.168.0.150ですよね?
- armadillo は、 complex-main port で listen 中?
- client の aterm? は、192.168.0.1
- client は、53286 と50943 ポートを使っているのが見える
- retransmission が多発している?
- なぜか、ARPが発生している?
- 問題になっているのは、53286 のポートだけ?

どれが接続できなくなったものですか?

Yamamotoです。

情報不足で申し訳ありません。

処理前提として
Armadillo<--PC端末間は本来は消しこみした1本のポートで、定周期用のつなぎっぱなしソケットとPC端末操作時に
必要とする情報を送受信するために接続->通信->切断を繰り返す2つをつないでいます。
Armadillo-->PC端末間は異常発生時当に、PC端末側に専用ポートで接続->通信->切断を実施しています。

> wiresharkの画面からデコードするのは辛いので、もう少し教えてください。
>
> - armadillo は192.168.0.150ですよね?
Yes!

> - armadillo は、 complex-main port で listen 中?
2つの接続があるのですが、恐らく50943 接続はキャプチャー外でListen中と思われます。
53286 は接続できなくなっている状態ですので、PC端末はlisten中になっているのですが、Armadillo側で
Acceptが発生しない状態で接続できなくなっています。(約2ヶ月連続動作して異常発生状態でのログです)

> - client の aterm? は、192.168.0.1
Yesだと思います。(お客様のところで再現試験をしていただいたので、恐らくPC端末のIPがこれになっていると思われます)

> - client は、53286 と50943 ポートを使っているのが見える
処理前提に記載の同一ポートに2つ接続しています。
よってArmadillo側はAcceptでforkして子プロセス個別に処理をするようにしています。

> - retransmission が多発している?
TCPパケットに関して知識が乏しく、TCP内でリトライが行われていることはわかりましたが、
Armadillo側でAccept検出できないために発生しているのかとおもっているのですが。

> - なぜか、ARPが発生している?
TCPパケットに関して知識が乏しいのですが、接続相手を見失って探しているということでしょうか?
これはネットワーク異常になってしまっているということなのでしょうか?

> - 問題になっているのは、53286 のポートだけ?
現状はそうですが、50943 もリトライ等が発生して切断、再接続すると繋がらなくなると聞いています。

>
> どれが接続できなくなったものですか?
現状では53286 です。

よろしくお願いいたします。

Yamamotoです。

追加でポートの使用状況が入手できましたのでアップしておきます。

消しこんだポートでローカルIPが0.0.0.0のlitenが残っていますが、これが問題になるのでしょうか?

おそらくSYN_RECVの接続がつながらないソケットではと思いますが

よろしくお願いいたします。

Yamamotoです。

追加で解ったことがあります。

接続先クライアントがソケットをcloseしてFIN,ACKが送信されて、ACKを返信しているのは確認したのですが

クライアントのcloseを受信(EOF受信)して、接続しているソケットをArmadillo側のサーバーソケットもclose
しているのですが、サーバー側のFIN,ACKパケットが送信されていません。
何か、close前に必要なのでしょうか?
サーバー側も単純に接続しているソケットのファイルディスクリプタでcloseすれば、ソケット切断のFIN,ACKが
送信されると思っていたのですが。

よろしくお願いいたします。

> 接続先クライアントがソケットをcloseしてFIN,ACKが送信されて、ACKを返信しているのは確認したのですが
>
> クライアントのcloseを受信(EOF受信)して、接続しているソケットをArmadillo側のサーバーソケットもclose
> しているのですが、サーバー側のFIN,ACKパケットが送信されていません。

それは変ですね。キャプチャーできてますか?

> 何か、close前に必要なのでしょうか?

なにも必要ないと思います。

Yamamotoです。

再現試験で、直近の接続->通信->切断では、クライアント側のFIN,ACK送信はありましたが、サーバー側のclose時のFIN,ACK送信は有りませんでした。
RST,ACKで接続が切れているパケットになっていました。

これも、再度確認してキャプチャーをアップさせて頂きます。

Yamamotoです。

現状の再現試験中のキャプチャーが取れましたので添付します。

やはりArmadillo側のサーバー側からFIN,ACKが送信されていないように見えます。
クライアントからのFIN,ACKへのACK送信後、KeepaliveでRST,ACKが送信されて切断されているように見えます。

よろしくお願いいたします。

ファイル ファイルの説明
001.png
002.png
003.png
004.png

Yamamotoです。

ちょと気になったのでコメントします。

プログラムは添付ファイルのような感じで作成しています。
acceptがあると、forkで受信用の子プロセスを起動して、子プロセス側で切断(recv()=0)検出でcloseして子プロセスを終了しています。

現状受信できているので問題ないと思っていたのですが、forkで子プロセス化した側でcloseしても問題ないのでしょうか?

forkした子プロセスには、子プロセス起動前の情報は受け渡せないとあったように思うので、気になりますので意見をお聞かせください。

添付ファイルは、要所のみにしてあります。

ファイル ファイルの説明
temp1.c

Yamamotoです。

もう一つ分かった事があります。

ソケット切断でcloseしているのですが、ファイルディスクリプタが開放されていないように見えます。
(処理の間違いでclose出来ていないのかもしれませんが・・・)

close後にacceptで新しいファイルディスクリプタを取得しますが、closeで解放されていないのかファイル番号か加算されて
1024に到達したところで、ファイル番号が使用できなくなりaccept出来なくなっているようです。

何が問題かご意見を頂ければ助かります。

中村です。

たくさんあったYamamotoさんの投稿の中の最後の2本しか読んでませんが・・・・

> ソケット切断でcloseしているのですが、ファイルディスクリプタが開放されていないように見えます。
> (処理の間違いでclose出来ていないのかもしれませんが・・・)

forkした後の親プロセスがacceptの戻り値のソケット(ファイルディスクリプタ)を
クローズしていないからだと思います。

それから、その1本前の投稿:
> 現状受信できているので問題ないと思っていたのですが、forkで子プロセス化した側でcloseしても問題ないのでしょうか?

クローズしても問題ないですし、クローズしないままexitしても大丈夫です。
exitすれば自動的にクローズされますので。

--
なかむら

Yamamotoです。

> たくさんあったYamamotoさんの投稿の中の最後の2本しか読んでませんが・・・・
済みません。
年末で、参考意見がもらえず判明した内容から順次投稿していたので大量になりました。

> > ソケット切断でcloseしているのですが、ファイルディスクリプタが開放されていないように見えます。
> > (処理の間違いでclose出来ていないのかもしれませんが・・・)
>
> forkした後の親プロセスがacceptの戻り値のソケット(ファイルディスクリプタ)を
> クローズしていないからだと思います。
昨日、再確認して上記の事象ではないかと推測しました。
実際には、accept処理が子プロセスで、送受信処理が孫プロセスに成っています。

実処理として、送受信処理の孫プロセス(複数接続で複数孫プロセスが存在する可能性あり)が終了した事を
accept処理の子プロセスで検知してcloseさせるの処理的に難しいと思われますのでスレッド化であれば全て
子プロセス内で実行されるの問題ないのではないか思い、変更確認しようと思っています。

スレッドであればcloseは出来ると思っていますが合っておりますでしょうか?

> それから、その1本前の投稿:
> > 現状受信できているので問題ないと思っていたのですが、forkで子プロセス化した側でcloseしても問題ないのでしょうか?
>
> クローズしても問題ないですし、クローズしないままexitしても大丈夫です。
> exitすれば自動的にクローズされますので。

問題は発生していませんが、孫プロセスでcloseしても実質closeになっていないので処理的には問題ありと思われます。

>
> --
> なかむら
>

> 実処理として、送受信処理の孫プロセス(複数接続で複数孫プロセスが存在する可能性あり)が終了した事を
> accept処理の子プロセスで検知してcloseさせるの処理的に難しいと思われますのでスレッド化であれば全て
> 子プロセス内で実行されるの問題ないのではないか思い、変更確認しようと思っています。

fork直後に親プロセス側で close() する方法では、だめですか?

fork() すると、親側で開いていた file descriptor の *コピー* は、すべて子に引き継がれます。
たとえ、子が close() しても、親は開いたまま持っているので、 親側の fd は close されません。
逆も真で、親が close() しても、子の file descriptor が勝手に close されることはありません。

sock = accept(...);
pid = fork();
if(0 == pid) {
        /* this is a newly created child */
        /* マルチ監視モニターから接続の場合 */
        :
}
else if (pid > 0) {
        /* this is the parent */
        close(sock);
}
else {
        /* error */
}

> スレッドであればcloseは出来ると思っていますが合っておりますでしょうか?

ごめんなさい、意味がわからないです。

> > それから、その1本前の投稿:
> > > 現状受信できているので問題ないと思っていたのですが、forkで子プロセス化した側でcloseしても問題ないのでしょうか?
> >
> > クローズしても問題ないですし、クローズしないままexitしても大丈夫です。
> > exitすれば自動的にクローズされますので。
>
> 問題は発生していませんが、孫プロセスでcloseしても実質closeになっていないので処理的には問題ありと思われます。

forkしたプロセスで正常に処理(close)していれば、forkしたプロセス側も問題ないですし、
forkで子プロセス化した側でcloseしても親の fdを勝手にクローズすることはないので問題ないですし、
クローズしないままexitしても(どうせ exitする *子プロセス側の fd* は closeされるので)大丈夫です。

「孫プロセスでcloseしても実質closeになっていない」は、正確には、
「孫プロセスでcloseしても、子プロセスではソケットを close していないので、処理に問題あり」
ですね。

一般的には上記のコードのように、 accept() した socket は child 用として、親側では即 close します。

中村です。

どう返事(説明)を書こうかな・・・と考えていたら、
at_yashiさんがすべて書いてくれてました。

Yamamotoさんは難しく考えすぎているみたいですけど、
> 一般的には上記のコードのように、 accept() した socket は child 用として、親側では即 close します。
です。

--
なかむら

Yamamotoです。

at_yashi さん、中村さんありがとうございます。

> Yamamotoさんは難しく考えすぎているみたいですけど、
> > 一般的には上記のコードのように、 accept() した socket は child 用として、親側では即 close します。
> です。
ここが良く解らずに難しく考えていました。
親側で即closeしたら子プロセス側で使用できなくなってしまうだろうと、勝手に考えて難しくしてしまいました。

fork時に親プロセス側処理にcloseを追加するだけで正常に動作するようになりました。

念のため、連続試験を実施してみます。

Yamamotoです。

下記、ご指摘の内容で再確認しスレッド化で対応しようと思いましたが、現状運用している事もあり最小限の変更
にすべく接続状態を管理している共有メモリテーブルに接続状態を追加して、終了した接続を親プロセス(実際は子プロセス)
で検出してcloseするようしました。
acceptは念のため5分程度タイムアウト設定しましたが、実質頻繁に-1で抜けてきているので、抜けたところで終了した接続を
確認するようにして確認中です。
短時間ではFIH,ACKも送信されるようになりファイルディスクリプタの増加もなくなりました。

> > ソケット切断でcloseしているのですが、ファイルディスクリプタが開放されていないように見えます。
> > (処理の間違いでclose出来ていないのかもしれませんが・・・)
>
> forkした後の親プロセスがacceptの戻り値のソケット(ファイルディスクリプタ)を
> クローズしていないからだと思います。
>
> それから、その1本前の投稿:
> > 現状受信できているので問題ないと思っていたのですが、forkで子プロセス化した側でcloseしても問題ないのでしょうか?
>
> クローズしても問題ないですし、クローズしないままexitしても大丈夫です。
> exitすれば自動的にクローズされますので。
>
> --
> なかむら
>

> 2ヶ月位すると、まず5分周期位で接続->通信->切断を繰り返しているほうが接続できなくなって、

この時、client側ではどのようなエラーになりますか?
サーバー側のエラーやシステムの状態は?
また、サーバー側のログになにか出てませんか?

5分周期を早めると、早く再現しますか?

Yamamotoです。

済みません。
5分周期は間違いで、1時間周期でした。
試験的に5分以下にして短期間で起きるかこれから確認予定です。

サーバー側のエラーとはmessgeを見てということでしょうか?
現状では、再現させてからかなり経過しているので発生時の状態が残っているかは不明です。

一応、現在のmessgeファイルを取得してもらいます。
取得できたらアップさせていただきます。

Yamamotoです。

> この時、client側ではどのようなエラーになりますか?
> サーバー側のエラーやシステムの状態は?
> また、サーバー側のログになにか出てませんか?

現在のmessage内容が取得できました。

特に異常は見当たらないようにおもいます。

ファイル ファイルの説明
messages.txt