(PHP Advent Calendar 23日目) PHPでIPMessanger

@aods1004さんの「[資格試験][PHP] PHP5技術者認定初級試験を受けてきました PHP Advent Calendar jp 2011 」からのバトンをうけて、
PHP Advent Calendar 2011 23日目を書かせていただくbornknow108です。

今年のクリスマスは連休と重なっているので、世間的にはかなりピンクなことになるのではと思っています。
多分私は、ひきこもりです。
で、PHP Advent Calendar ですが、 23日目にもなると投稿されているいろいろな記事に目を通してしまうため、自分の中のハードルがけっこう高くなっています。
でも高いハードルを飛び越えるのはムリなので、高いハードルにぶつかるつもりで書かせてもらいます。

みなさんは「IP Messanger」ってご存知ですか?
IP Messanger は、TCP/IPを利用したLAN内のメッセンジャーツールで、メッセージのやりとりや、ファイルの送付ができるので勤め先で重宝して使っております。

このIP Messangerで送ったテキストをコマンド替わりにして、サーバーの処理と連携すれば、ちょっとしたツールになるんじゃないカナと思ったので、PHP版を作ってみることにしました。
###先人の知恵を借りる
とりあえずしたい事は、PHPを使ってサーバーサイドでIP Messangerのメッセージのやりとりができればいいので、Google大先生に聞いてみました。(ここで、見つかれば作らないで、作業は終了です。)
IPMessanger PHP」で聞いてみたところ
PHPからIP Messengerにメッセージを送る
PHPからIPメッセンジャーにメッセージ送信(BOT用)
などなど、メッセージを送信するためのお知恵を見つけることができました。
しかし、今回は、メッセージをサーバー側で受信がしたかったのですが、該当する記事は見つかりませんでした。
当初の予定通り自分で作ることにしました。
ちなみに、Perl版やらJava版など他の言語に移植されたIP Messangerはすでに存在していました。

###IP Messanger の仕様を調べてみる
兎にも角にも、IPMessangerがどういう動きをしているのかがわからないと始まりません。
本家サイトより、ソースコード一式をダウンロードし、解析、さらに仕様もググってなんとなく雰囲気はつかめました。
IP Messenger 通信プロトコル仕様(ドラフト10版)
IP Messenger プロトコルメモ.
IP Messenger library for UNIX - 仕様
わかったこと

  • IP Messangerは、メッセージの送信に TCP/UDP の2425番ポートを使っている
  • 決まったフォーマットのメッセージをやり取りしている。
    Ver(1) : Packet番号 : 自User名 : 自Host名 : Command番号 : 追加部
  • コマンド番号は、32bit
  • コマンド番号の下位8ビットがコマンドで、上位24ビットがオプション
  • 受け取ったコマンドに合わせて、処理をする

###メッセージの受信してみる
まずは、メッセージを受信です。
IPMessangerはTCP/UDPの2425番ポートを利用して通信しているので
PHPでTCP/UDPを使った通信方法を調べたところ

  • ソケット関数
  • ストリーム関数
    などを利用することがわかりました。
    それぞれの実装方法を確認して、今回はストリーム関数を使うことにしました。
    まずは、ソケットサーバーを立てます。
    立て方は簡単で、以下のようなソースになります。
    1
    2
    3
    4
    5
    6
    7
    $socket = stream_socket_server("udp://[ホストのアドレス]:[ポート番号]"
    , $errno
    , $errmessage
    , STREAM_SERVER_BIND);
    if (!$socket) {
    throw new Exception("{$errno}{$errmessage}");
    }

これで指定したポートで待ち受けるソケットが作成されます。
$errno, $errmessage にはそれぞれ、エラー時に、エラー番号とメッセージが返されます。
続いてメッセージの受信処理です。
先ほど作成したソケットを使って、メッセージを受信するための処理を書きます。
以下のようなソースになります。

1
2
3
do {
$packet = stream_socket_recvfrom($socket, 1024, 0, $address_port);
} while ($packet !== false)

第2引数は、一度に受け取るパケットのバイト数です。
第4引数に指定した変数にパケットを送ってきたクライアントのIPアドレスとポートが返されます。
あとは、受信したパケットをIPMessangerのフォーマットにしたがって分解することで
送られてきた情報を読み取ることができます。
読み取った情報をもとに、サーバー側で処理をさせればいいので、これで目的達成です。
###ついでに、メッセージの送信してみる
先人たちのお知恵を借りれば、OKなのですがせっかくなのでストリーム関数で実装してみました。
以下のようなソースになります。

1
stream_socket_sendto($socket, $send_packet, 0, $address_port);

第2引数には、送信するパケットを指定します。
第4引数には、送信先のアドレスとポートを指定します。
びっくりするぐらい簡単です。

###最後に
もうちょっとIPMessanger的な処理を埋め込んだソースファイルは、github にアップしてますのでご自由にお使いください。
(ただ簡易版なので、テキストの送受信ぐらいしか作れてません。)
明日は、@hirakuさんです。
それでは、みなさんメリークリスマス!!