2015年9月4日金曜日

艦これ+航海日誌環境における猫事件についての調査

ブログ書くの相当久しぶりになりました。最近趣味では新しいことがあまりできてなかったので書くネタがなかったのですね。もっと時間を作らないと。

さて今回のネタはもともと技術的な話ではなかったのですが、結局は技術的な話になったのでここに書くことにしました。内容は艦隊これくしょん(艦これ)の通信についての話なので、興味のある方だけ読んでみてください。

私が艦これを始めたのは相当前になります。最初はただの興味本位で始めたのですが、基本運ゲーでも結構いろいろ考えることが必要なことがわかり、最初はアナログな試行錯誤をしていたところ@sanae_hirotakaさん作の航海日誌という大変便利なソフトに出会い、より楽しくプレイさせていただけるようになりました。作者さんがソースを公開してくださっているので、私の専門のJavaで書かれていることもあり、ほんの少しですがpull request等で開発に協力させていただいたりもしていました。

ところが事件が起きた(起き始めた)のは2015年の夏イベント(通称夏イベ)が開始されてからです。今まではほとんど猫った(エラーが起きた)ことのない平和な我がタウイタウイ泊地だったのですが、夏イベ開始のサーバーメンテナンス明けからちらほら猫るようになりました。最初はサーバーが混んでるのかなと思っていましたが、空いてそうな時間帯でもなることがあり、変だなと思い始めました。しかしネット上では特に騒がれている風もなく・・・。

すると艦これをやってる知り合いから「どうも航海日誌を使ってると猫るって話になってるぞ」との知らせが。そんな馬鹿なと思いツイッターで「航海日誌 猫」とかで調べてみると確かにそういう事象にあっている方もいるようで、ただ実行環境によるのか、サーバーによるのかは不明にせよ、全く起きない方もいるのも事実。 しかし調べていたら、こんなに便利に使わせてもらってるソフトをしかもタダで使わせてもらってるのに、「使えねー」とか「ふざけんな」とか言われてるのは納得いかないので調査してみようと思ったのがこの記事を書いている理由です。

するとその知り合いからさらに「特にテザリングでやってるとしょっちゅう猫る」との知らせが。それをヒントに、まずどういう環境で出やすいのかを考えました。家ではあまり出ない、テザリングで出やすい、と考えると、やはり遅い回線だと出やすいのではないかと考えました。そこで私はiijmioのSIMでiPhone4Sを運用しているのですが、このiijmioのSIMはアプリで通信速度を低くすることができます(正確には高速通信は月3Gまでとなっているので、高速通信をオンオフできます)。なので高速通信をオフにした、たしか200kbps程度の通信速度でテザリングして艦これしてみました。

すると


でました、猫。ログインしてすぐというわけではないですが、任務をクリックしたり母港に戻ったりとか通信を何度もしていると、私の環境では数分で起きます。これは何かありそうですね。そう思って航海日誌を通さずに艦これしてみると、不思議なことに全く猫は起きません。なぜでしょう。

悩んでても解決できないので、Wireshark を使ってパケットキャプチャをしてみました。そして猫が出るまで通信を繰り返します。そして猫が出た時と出なかった時を比べます。すると
うまく行った時はこんな感じです。これは任務一覧を表示した時ですね。POSTのメッセージに対して ACK が返り、その後 200 の応答が返ってきます。普通です。ところが猫が出た時は(キャプチャミスってボケてます。すみません。)

のように、[FIN, ACK] が返ってきています。FIN は通信終了なので切断するという意味なので、航海日誌が使っている jetty の ProxyServlet は EOFException を投げます。結果ブラウザから見れば通信エラーとなり、猫が出るという仕組みでした。

しかしこれだったら普通に航海日誌抜きでやっても同じなのでは?と思って試してみたところ・・・
おお、これは・・・。どういうからくりかはわかりませんが、上のようにAPIの呼び出しが切断された時はどうもリトライしにいくようですね。これは任務の受諾をやめた時に呼ばれるAPIなので、連続で来るはずはないリクエストになります。通常は stop が来た後 questlist が来るはずのところを2回 stop が来ているので明らかにリトライしているようです。

なので ProxyServlet 経由でも全く同じ状況、FIN を返すことができれば勝手にリトライしに来ると考えられます。しかしそれを ProxyServlet を使ってやるのは難しそうに思います。試しに ReverseProxyServlet を無理やり extends して EOFException が出た時にリトライするサーブレットを書いてみたところ、私の環境では猫は出なくなりました、があまりに無理やり過ぎるので pull request はしないほうがよいかなと思って保留しています。

なぜ通信が切断されるようになったか、という点については、どうもこのメンテナンスでサーバーによっては HTTP 1.1/ keep alive が使われるようになったそうです。とはいえメンテナンス前が keep alive じゃなかったかどうかは全く知らないですけどね。その実装の影響でこうなったのかもしれません。想像でしかないですが。

結論としては、
- なぜか環境によっては艦これサーバーからAPIリクエストの通信が切断されることがある
- ブラウザから直結している場合は自動的にリトライする
- 航海日誌やその他プロキシの実装によってはその切断が別のエラーとして処理されると猫る
ということのようです。なので実装系が違う他のソフトでも同様な挙動をしていれば、航海日誌と同じように猫りやすくなったソフトもあると考えられます。というわけで、この猫事件(?)の真相は「艦これの微妙な(怪しい)挙動」が根本原因であって、航海日誌その他プロキシの実装のバグと呼ぶのはフェアじゃないと思いますね。