JavaとKeep-Aliveの話

Javaに対する愚痴みたいなエントリー

グダグダと無為に長い文章書いたけど、結論だけ先に書くと、
・JavaではIPパケットヘッダを操作出来ないので、Keep-Aliveを自力で実装することは不可能。
・どうしてもKeep-AliveしたかったらC/C++でDLLとか作ってJNIで呼び出すか、OSの設定(レジストリとか)を変えるしかない。
 Jpcapを使ったJava用のライブラリがありましたが、今は公開されてないっぽい。結局JNI使うし。
・Javaはうんこ
ということです。

数年前に仕事で、Javaでサーバ・・・というか、電文変換等を行う中継サーバ(ゲートウェイ)を作ったんですよ。
Java使った理由は稼働環境がWindowsだったりLinuxだったりするから、どっちでも動くようにっていう事だったんだけど。
実際はWindowsだけだったっぽいけど。

で、似たような中継サーバをまた作ることになったんですね。
で、前のやつのソース流用してコスト下げようとかってことで、やっぱりJavaで作ることになったんだけど、前回は1電文毎にコネクション張って、応答返したら切断って方式だったから考慮しなかったけど、
今回のは常時接続方式なのでKeep-Aliveで通信状況を監視する事になったんですよ。

ここから本題。

Javaの通信はjava.net.Socketを使います。
このSocketクラスでKeep-Aliveしたい場合setKeepAlive(boolean)ってメソッドがあるんだけど、引数から分かる通りこれはKeep-Aliveの有効/無効の設定しかできない。
無通信監視時間(無通信の状態がどれだけ続いたらKeep-Aliveパケットを投げるか)とかKeep-Alive送信間隔とか送信回数とかを設定する方法は無く、それらは実行環境(OS)に依存します。
無通信監視時間は大抵、2時間とか長めの時間になってます。正直長すぎて使いものにならない。

私が作ろうとした中継サーバは無通信監視時間を数十秒程度で設定できるようにしたかったので、電文が数十秒流れなかったら1バイトのゴミデータをKeep-Aliveパケットとして送信するように自作しました。
socket.getOutputStream().write(new byte[1]);
みたいな感じで。
ホントは0バイトにしたかったんだけど、引数に要素数0のbyte配列渡しても送信してくれないんだよね。
一応RFCではKeep-Aliveパケットに1バイトのゴミデータをつけても良いって書いてあるし、コレでイケルと思ったんだよね。

で、実装終わって稼働試験が始まったんだけど、そこで問題が起こった。
中継先のサーバが数十秒毎にPSHフラグのついたゴミデータが飛んで来るのは異常だから切断するね、と言われた。

PSHフラグ?なにそれ?

PSHフラグとは

PSHフラグとは、TCPパケットのヘッダに設定される情報の一つで、パケットを受信したらすぐに上位のプロトコルやアプリケーションに渡すよう指示するためのもの。

要はKeep-Aliveみたいな制御用パケットにはつけちゃいけないフラグっぽい。

ではPSHフラグをOFFにして送信する方法は・・・というと、Javaには無い。無いのですよ。
PSHフラグどころかIPパケットヘッダが触れない。
そもそもKeep-AliveパケットはPSHフラグだけじゃなく、シーケンス番号も弄る必要があるので、IPヘッダを触れないJavaではどうしようもない。
さて困った。

解決方法としては、
1.IPパケットをいじれる別の言語(Cとか)でKeep-Alive作って、JNI経由で呼び出して使う方法。
2.動作環境(OS)の設定を変える。
この2パターンくらいしか無い。

要件が許してくれれば2の方法が一番楽でいい。Windowsだとレジストリをいじることになるけど。
1の方法はそもそもJavaで作った意味がなくなるんだよね。

実はこの話、現在進行形で解決してなくて、どうしようか検討中の案件だったりする。
最悪別言語で作り直しか、プロジェクト自体消滅することに。

最近Javaの仕事多くやってたけど、やればやるほどC#との差が際立って萎えるわ。

もしまた似たような仕事が来たら、次はC#でやらせて貰えるように交渉しよう。うん、そうしよう。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

認証のために問題を解いて下さい * Time limit is exhausted. Please reload CAPTCHA.