ソケット通信をあーだこーだしてrcssserver3dにkick offさせる

GAを使ってrobocup 3D agentの動作を生成したい。評価のために、勝手にサーバを起動してクライアントを接続してエージェント走らせて評価値を書き出して死ぬクライアントを作る必要が生じている(もう長いことこの要求は放置されている)。

で、クライアント(C++)でとりあえずパラメータをテキストから読み込むようにして、GA部分はPerlのAI::Geneticに丸投げする。あとは、Perlからサーバを上げてクライアントを走らせて評価値をとってくるだけ(言うのは簡単だ)。

で、rcssserver3dにkick offの命令を出さないといけないのだが、rcssmonitor3dから出す以外に方法がよくわからなかった。サーバ側にコマンドからキックオフする機能は特に見あたらない(例によって調べるのがめんどい)のだが、いちいちモニタまで上げてたのではただでさえ遅いGAが耐え難い遅さになってしまう。

というわけで、rcssmonitor3dにキックオフ命令を出させた時に流れてるパケットをキャプチャして、同じように送ってやれば良いのではないかと思ったのでやってみた。

ソケットで送ってることはまぁわかっているので、tcpdumpでloをdumpする。この辺のがわからんってtwitterに言ってみたら何人かが教えてくれた。持つべきものはすごい友人達ですね。ありがとう。

tcpdump -i lo -w hoge -vvv -s0

とすると、loを通るパケットがhogeに吐き出される。いけてる。

これをwiresharkで開くといい感じにパケットの中身が見られるので、kickoffとかで検索をかけて、クライアント→サーバのパケットから目的のものを探す。

00 00 00 0e 28 6b  69 63 6b 4f 66 66 20 4c   ....(k ickOff L
65 66 74 29                                        eft)

目的のパケットのdata部はこんな感じだった。とりあえず (kickOff Left)という文字列を送ればいいものかと思い、適当にぐぐりつつPerlでport 3200に送りつけてみた。が、うまく動かない。

やはりdata部をそのままコピーするしかないと悟り、16進数で送るようにしてみた。

use IO::Socket::INET;

my $c = IO::Socket::INET->new(
    PeerAddr => "localhost",
    PeerPort => 3200,
    Proto    => "tcp"
) or die $!;

my $msg = pack("H*", "0000000e286b69636b4f6666204c65667429");
$c->print($msg);
$c->getline;
$c->close;

serverを立ち上げて、このスクリプトを走らせるとkickoffされるではないか。なんかひたすら蛇足でしかない気がするが、思うように動いたのでとてもうれしい。

0000  00 00 00 00 00 00 00 00  00 00 00 00 08 00 45 00   …….. ……E.
0010  00 46 7e 18 40 00 40 06  be 97 7f 00 00 01 7f 00   .F~.@.@. ……..
0020  00 01 de b0 0c 80 04 61  75 28 05 1b 9a ff 80 18   …….a u(……
0030  03 02 fe 3a 00 00 01 01  08 0a 00 1d 09 fd 00 1d   …:…. ……..
0040  09 f5 00 00 00 0e 28 6b  69 63 6b 4f 66 66 20 4c   ……(k ickOff L
0050  65 66 74 29 begin_of_the_skype_highlighting              0050  65 66 74 29      end_of_the_skype_highlighting                                        eft)