JavaWebSockets(本家のサイト!)とかWebsockets in Play 2.0 - Blueprint Forge.とかのサンプルコードがスッキリ動いてくれなくて激しくハマったのでメモ。
public static WebSocket<String> index() {
return new WebSocket<String>() {
// WebSocket のハンドシェイクが完了すると呼ばれます。
public void onReady(WebSocket.In<String> in, WebSocket.Out<String> out) {
// ソケットで受け取ったイベント毎に
in.onMessage(new Callback<String>() {
public void invoke(String event) {
// コンソールにイベントのログを出力する
println(event);
}
});
// ソケットが閉じた時
in.onClose(new Callback0() {
public void invoke() {
println("Disconnected")
}
});
// 単一の `Hello!` というメッセージを送る
out.write("Hello!");
}
}
}
WebSocket のハンドシェイクが完了すると呼ばれるらしいonReady
の中で、WebSocketの入力チャンネルにonMessage
とonClose
のハンドラを設定し、出力チャンネルのwrite
でクライアントにWebSocket通信を試みているコード。
これがHerokuだと思うように動かない。localhostでは動く。
'動かない'というのは、
out.write("Hello!");
でWebSocketの出力チャンネルに書き込んだハズなのに、クライアント側のJSのWebSocketオブジェクトのonmessageイベントがfireされないということ。
localhostでは、クライアントのonmessageはすぐに反応するが、Herokuに配置したアプリケーションでは、onmessageはいつまでたっても反応せず、サーバとの通信がないまま55秒経ってしまうと、Herokuのタイムアウトに引っ掛かってWebSocketが断絶してしまう。さらに不思議なことに、その際、上記out.write("Hello!");
で書き込んでいた内容が、突然onmessageでキャッチされる。out.writeは決して書き込み失敗したのではなく、何故か待ち状態になってしまっているように思われる。
##解決策
onReadyの中でout.writeしない
onReadyはハンドシェイクが完了したら呼ばれる、と書いてあるが、これがイマイチ釈然としない。out.writeをするということは、できたばっかりのWebSocketのoutを使って通信をするということであるが、これがうまくいっていない模様。ハンドシェイクが'本当に'完了する前にoutを使ってしまい、そこで処理が止まってしまっているのではないだろうか。試しにout.writeを取り除いてみると、思った通りの、定義通りの挙動をするようになった。onReadyの中で無理にクライアントに通信を返す必要は特段ないので、今後はこのような対応を取ることにする。しかしAPIリファレンスが貧弱で困る。