2006年11月03日

PHPでのソケットサーバー、第一歩での泥沼

昨日はそこいらにあるPerlのコードを手本にして
Flash用のものをでっちあげた「借り物コード」での実験だが
PHPでの実装を目指したいと思った。

しかし現状「サーバー開始リクエストを閉じる方法がみつからない」
という理由で第一歩を踏み出せないでいる。
まず今日は使う関数について学んだ。
目的のための選択肢としてのソケットサーバーであり必須ではない
結果としてソケットサーバーが必然でなければ切り捨てることもありうる。
やりたいのはツールとしてのサーバーではなくて結果としてのアミューズメントなのだ。

ところでなぜ昨日唐突にPerlを使ったのか。
それには理由がふたつあった。

ひとつめだがPerlの場合ソケットの生成にはポート番号だけ指定すればいい。
しかしPHPの場合はIPアドレスも指定しないといけないという理由だ。
果たしてどのIPなのか不安があった。
もう1つはサーバーをlistenしたときにサーバー開始リクエストを閉じる命令が見つからなかったということ。

その二つはいまだ解決していない。
とりあえずPHPでのソケットに必要な関数について調べて見た。
PHPでソケット操作をするときに一般的には

socket_create

socket_bind

socket_listen

ときてあとはsocket_acceptのループを作るというのが基本形とされているが
このsocket_bindでIPアドレスが必要になる。
自分用にソースを書く場合はいいがソースを改変しないで配布するコンテンツには使えない。
そういってしまうとポート番号もそうだ。
クライアントであればアクセス側のIPを書くわけだが
いわゆるソース公開して再配布するものの場合コアの部分を「改変」しなくてはいけないというのはよくない。
逃げてとしてはクラス型にしてその部分をメンバー定数にする手もある。

まだ勉強不足でわからないだけなのだろうが、
空きポートをスキャンしたり自分のIPを取得すればいいのだろうが
取敢えずの策としてサーバートリガーに対してgetのパラメータとしてポートをつけてやることにした。

そしてソケットの基本命令だが
一般的には上のものが使われているが


resource stream_socket_server ( string local_socket [, int &errno [, string &errstr [, int flags [, resource context]]]] )

であれば1発でlistenまでやってくれる。
たいていの場合

if (!$socket) {
echo "$errstr ($errno)\n";
}

を命令ごとにかますことになるので「呪文」のような部分の簡略化という事でこれは使える。

またこの場合acceptも

resource stream_socket_accept ( resource server_socket [, float timeout [, string &peername]] )

を使う。

ほんとは

onAccept

みたいなイベントハンドラを使いたいのだが
そもそもPerlとかPHPというのはそういう言語で無いらしく
「ぐるぐるチェック」しかてが無いようだ。
無論ぐるぐるチェックの基本クラスを作っておけば上位クラスはまるでイベントのように扱える。

stream_socket_server で作ったソケットは読み書きは
socket_readやsocket_write、socket_closeは使わず
fead、fwrite、fcloseを使う。コードは要するにfopenと似た形になる。


int fwrite ( resource handle, string string [, int length] )//バイナリ・モードによるファイル書き込み


string fread ( resource handle, int length )//バイナリ・モードでファイルを読み込む


今回は使わないと思うがクライアントの場合も

resource stream_socket_client ( string remote_socket [, int &errno [, string &errstr [, float timeout [, int flags [, resource context]]]]] )


なのだが

resource fsockopen ( string target, int port [, int &errno [, string &errstr [, float timeout]]] )//ソケット接続をオープンする


int pfsockopen ( string hostname, int port [, int errno [, string errstr [, int timeout]]] )//ソケットは持続される。

との差別化がいまひとつわからない。

継続的サーバーソケットを閉じる方法だが
昨日のPerl版は30分で勝手に閉じたからいいものの
意図的に閉じる方法を用意しておかなくてはいけないと思う。
プロセス通信等も考えたのだが
サーバーが生きているのだからクライアントのコマンドで閉じる方向にしようと思った。

「ぐるぐるチェック」で「何もアクションが無い」ときむしろ過負荷にならないか心配だが
とりあえずはこの方向でモジュール版で作って
制限が多いならばtimeoutなどを設定してCGI版で試してみようと思った。

「サーバー起動リクエスト」を終了させる方法だが
「バックグラウンド処理」にするためには必須だ。

Perlの場合

close(STDIN); close(STDOUT);

でリクエストを切ってサーバーだけ残せるのだが
PHPの場合の記述法がわからなかった。

PHPのサンプルではサーバーはリクエストした接続を継続したまま動いているようになっていて
それではどう考えてもタイムアウトを食らうはずなのだ。

探索したところPHP 4.3.0以降であれば

fclose(STDOUT);fclose(STDIN);

でよいはずなのだがphpversion()だと5.14なんだけれどどうもうまくいかない。

さらに探索するとどうもこのネタは定番の問題点らしいことが判明
ようするにSTDOUT定数は標準出力のコピーであって元は閉じられないということらしい。
結論に至ったものはなく最悪シェルを操作するとか無駄にめんどくさい事をしないといけないようだ。

かなり細やかな事をしないといけなさそうだ。
とりあえず今考えていることとしては

  1. -q オプションをつけてみる。多分.htaccessも必要

  2. 別プロセス起動を検討する。


でこれをどうにかしないと話にならない気がする。
あるいはPHPをCGIモードで起動するようにした場合は上手くいくのかもしれない。
とりあえず明日はサーバー以前にここを何とかしよう。
そしてだめならさっくりとPerl版をつめることにする。


posted by Xo_ox at 23:40| Comment(0) | サーバーサイド手習い | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
×

この広告は180日以上新しい記事の投稿がないブログに表示されております。