PHP でサーバソケットプログラミング (1) [PHP]
低レベルのサーバサイドソケットプログラミングってあんまり知らなかったのでちょこっと調べてまとめてみた。
対象言語: PHP5
この記事のゴール: クライアントから入力されたデータをそのまま標準出力に表示する
PHP5 でソケットを使うための準備
ソケットを使うには php.ini の以下の行のコメントアウトを外す必要があります。
変更前:
;extension=php_sockets.dll
変更後:
extension=php_sockets.dll
この設定をしないとソケット関数を使用したときに関数が未定義であるというエラーが発生します。
PHP Fatal error: Call to undefined function socket_create() in ...
ソケットを作る
サーバ側ソケットを作るには socket_create_listen を使います。
$port = 9999;
$sock = socket_create_listen($port);
このコードでは9999番ポートを待ち受けとするサーバソケットを作成します。ポート番号は1024より上であれば任意です。
作成されたソケットは $sock に入ります。
socket_create_listen は実は若干高レベルなAPIで、細かい制御をするには socket_create, socket_bind, socket_listen を組み合わせて使います。socket_create_listen はこの3つをまとめて実施しています。
クライアントの接続を待つ
作成されたソケットを引数に socket_accept を呼び出すとクライアントからの接続待ちが開始します。
$clientsock = socket_accept($sock);
この行が実行されるとプログラムは接続が来るまで待ち続けてここで停止しています。
接続があるとそのクライアントとの通信用の新しいソケットが $clientsock に入り、次の行に進みます。
クライアントからのデータはこのソケットを通じて取得することが出来ます。
クライアントからのデータを読み込む
上記で得られた $clientsock からデータを読みこむには socket_read 関数を使います。
$buf = socket_read($clientsock, 1024);
第2引数の意味は最大で1024バイトのデータを取得するということです。
ソケットを閉じる
ソケットの後始末をするには socket_close 関数を使います。
socket_close($clientsock);
socket_close($sock);
ここまでのまとめと実験
ここまでのコードをプログラムにまとめて実行してみます。
<?php // sock_try1.php
$port = 9999;
$sock = socket_create_listen($port);
$clientsock = socket_accept($sock);
$buf = socket_read($clientsock, 1024);
echo $buf;
socket_close($clientsock);
socket_close($sock);
?>
クライアントはなんでもいいのですが、Tcl のインタラクティブシェルを使うことにします。
% set s [socket localhost 9999] sock1808 % puts $s "Hello World!" % flush $s %
サーバの方を見ると…
F:\>php sock_try1.php Hello World! F:\>
おお!表示されてますね!(←「JavaでHello World」の人風に)
もっとサーバらしくする
しかしクライアントが1度フラッシュしただけで読み込みを終えたり、そもそも1回の接続だけでサーバが終了するというのもおかしな話です。
<?php // sock_try2.php
$port = 9999;
$sock = socket_create_listen($port);
while (true) {
$clientsock = socket_accept($sock);
while (true) {
$buf = socket_read($clientsock, 1024);
if ($buf == "") { break; }
echo $buf;
}
socket_close($clientsock);
}
socket_close($sock);
?>
ここでは2つのループを追加しました。
内側のループは1つの接続からの全入力を終えるまで繰り返し socket_read を実行します。socket_read は読み込むものが無くなると空文字列を返すのでそれをループから抜ける判断に使います。
外側のループは内側のループの終了後に再び接続待ち状態に戻るためのものです。
サーバを止めるには Ctrl+C を打ちます。
外側のループは終了条件がないので実は最後の socket_close($sock); にはたどり着きません。本当はシグナルハンドラなどで終了処理をすべきなのですが Windows 版 PHP では使えないようなので差し当たりあきらめました。
これを起動して再びクライアントから接続を行います。
% set s [socket localhost 9999] sock1760 % puts $s "Hello World!" % flush $s % puts $s "Hello Again!" % flush $s % close $s % set s [socket localhost 9999] sock1760 % puts $s "Howdy!" % flush $s % close $s
サーバ側の実行結果はこちら。
F:\>php sock_try2.php Hello World! Hello Again! Howdy!
クライアントがクローズするまで読み込み続けること、クローズをした後に再接続できることがわかります。
次回は複数の接続を同時に受け入れられるようにする方法を(調べて)書きます。
コメント 0