SocketChannelが閉じれないことがある話 と 暫定対策

おもいっきりハマったので自メモ)

結論的にはこういう話)
http://twitter.com/kimukou2628/status/268656976647577601:twitter:detail:right

http://twitter.com/kimukou2628/status/268833153349722112:twitter:detail:right
一度で閉じなければ再帰で閉じろって感じで・・。*1
そもそもソケット切断をトリガーにするサービスっていやん><。

NIOあたりの資料)
櫻庭さんの資料




追記)
上記で上手く言ったと思ってたんだけど、ダメやった。。。

  • 普通に接続するだけなら
InetSocketAddress isa = new InetSocketAddress(”XXX.XXX.XXX.XXX”, 8000);
java.lang.System.setProperty("java.net.preferIPv4Stack", "true");
java.lang.System.setProperty("java.net.preferIPv6Addresses", "false");
channel = SocketChannel.open();
//channel.configureBlocking(false);
channel.socket().setSoTimeout(3000);

if(!channel.connect(isa))return false;
while(!channel.finishConnect());
  • 切断は
if(channel==null)return
channel.close() 
channel=null

をExceptionが出なくなるまで繰り返せば
切断接続の繰り返しでも再接続OKでしたが

一度でも読み書きの通信するとダメで、読込、書込バッファをフラッシュしてからじゃないとダメらしい。*2
close()自体はExceptionも出ずに通過してるんだけどな・・*3

if (channel == null || !channel.isOpen())return;
Socket sc = channel.socket();
sc.shutdownInput(); //★
sc.shutdownOutput();//★
sc.close();

でもまあ
id:sakura_bird1sakura_bird1さん興味をもつ幅がすごいよな(汗

参考にしたサイト1)



追記2)
更に補足コメントいただきました。ありがとうございます。http://twitter.com/kimukou2628/status/269121235231846400:twitter:detail:right

channel = SocketChannel.open();
Socket sc = channel.socket();
sc.setSoTimeout(3000);
sc.setSoLinger(true, 0);      //◯
sc.setReuseAddress(true);  //◯

if(!channel.connect(isa))return false;

channel.configureBlocking(false);

while(!channel.finishConnect());

修正にあたっての参考サイト1ー2)

configureBlocking(false) に関しては connect後にするまで落ちてた><。
呼ぶタイミングもやはり大事。。



追記3)
で、殆どの機種では上記の対応でいくのだが。。
いつも苦しめられる ARROWS X LTE F-05D 様に苦しめられることに・・・。
まず

  • timeoutが3秒ではなく30秒ぐらい設定しないとすぐtimeoutで自然に切れる><。
channel = SocketChannel.open();
Socket sc = channel.socket();
sc.setSoTimeout(30000); //△
sc.setSoLinger(true, 0); 
sc.setReuseAddress(true);

if(!channel.connect(isa))return false;

channel.configureBlocking(false);

while(!channel.finishConnect());
  • ソケットclose関数時 に★をtry-catchで囲まないと盛大に落ちる>< *4
if (channel == null || !channel.isOpen())return;
Socket sc = channel.socket();
try{
sc.shutdownInput(); //★
}catch(Exception ex){}
try{
sc.shutdownOutput();//★
}catch(Exception ex){}
sc.close();
  • channel.read(buffer); なバッファで取得した文字列を判定処理するところに時間がかかりすぎるとtimeoutで自然に切れる。たまに落ちる
    • 大量にデータ受信しまくると発生。。。
    • とりあえず別スレッド(ExecutorService)に判定処理自体を逃した。。。

IS03やGNとかは全然問題ないので、なんだまたこの機種かよ。。な感じ
なん何でしょうかね・・・。
しかもこのtimeout起きた時に ソケットclose処理読んでますが 、
shutdownInput()
shutdownOutput()
がエラーでて通過なので、
ソケットは切れてるけど鯖側はクライアント切断認識してない
というよくわからん現象。。
まあ上には相談したので後は判断待ちかな・・*5

あと今回一定期間経過後に回線切断する処理を組んでみたんだけど
onPause,onStopに入れた処理

  • 2系だと
    • 電源ボタン押して画面暗くする
      • =>指定時間後に切断通知
  • 4系だと
    • ロック画面でも回線切れない
    • Homeで最小化
      • =>指定時間後に切断通知

イマイチ挙動遷移がわからない感じなのでしたorz

http://twitter.com/kimukou2628/status/269421280153972737:twitter:detail:right

http://twitter.com/kimukou2628/status/269427276066742272:twitter:detail:right
http://twitter.com/kimukou2628/status/269428180559994881:twitter:detail:right
http://twitter.com/kimukou2628/status/269433094220677120:twitter:detail:right
http://twitter.com/kimukou2628/status/269436915508449281:twitter:detail:right


やっぱり結論としてはこうなっちゃうんだよな。。

よくググるとグローバルチャット系の実装の話は出てくるんですけど(node.jsとか
どちらかと言うとプライベートチャットのほうが人気あるよねみたいな。
なかでも登録するデータができるだけ少ないやつ・・*6

まあ情報凄く抜こうとする割には情報管理がずさんだからな。。。(汗


やり取り的なメモ)
検証コードさらっとかける @alterakey さん凄い!
[http://twitter.com/alterakey/status/268703967486279680:twitter:tree]

まあ本来 普通は繋げっぱで、やり取りメッセージで退室とかって実装になってればよかったんだけど
後半多分実装面倒になったのか、クライアント切断処理と退室処理を兼ねて横着みたいな鯖実装。
即興で作るとこんな感じになっちゃうんだよな(汗

参考にしたサイト2



最終的にはTLの詳しい人に聴きまくり。
いつもお世話になってます(汗

なんか変な仕様は治したいんだけど、運用中のサービスだと
騙し騙しでクライアント側でなんとかゴニョる話になるんだよな・・・。
正直ありえない実装なんだけど・・・

*1:超泥臭くてイヤンですが

*2:★の箇所

*3:まあどっかのブログで挫折したみたいな記事も読んだし。。。

*4:再帰で呼んでるからStackOverFlowで・・

*5:既知事項でリリース予定かも。。。

*6:匿名性が高いやつ