async_socketクラス
2010年4月27日
少し思うところあって、非同期のソケットクラスをPHPで書いてみた。
たぶん、こういった用途のライブラリは色々とあるのだろうけど、スクラッチから書くのが好きなので…。
ソースコード
実行結果
async_socketクラスは、GETメソッドで呼び出し、返事を受け取るとコールバック関数を実行する単純な仕様。POSTメソッドを利用したり拡張ヘッダ情報の送信
、あるいはKeep-Aliveな接続などを行いたい時は、スクリプトを修正すべし。
ソースコードの半ばあたり、"if ($continue) usleep(100000);"の行をコメントアウトすると、スクリプトの実行中はCPUの占有率が100%になる。このあたりの設定は、スピードとリソース消費の兼ね合いで、調整が必要なところ。今のような用途だと、100000マイクロ秒(0.1秒)のスリープで、リソース消費も最低限だし実行速度も問題なさそうだ。
たぶん、こういった用途のライブラリは色々とあるのだろうけど、スクラッチから書くのが好きなので…。
ソースコード
<?php echo microtime()."\n"; $kandk=new async_socket; $kandk->register_callback('cb',array($kandk)); $kandk->request('kandk.cafe.coocan.jp','/'); $kandk2=new async_socket; $kandk2->register_callback('cb',array($kandk2)); $kandk2->request('kandk.cafe.coocan.jp','/nucleus/'); $google=new async_socket; $google->register_callback('cb',array($google)); $google->request('www.google.com','/'); $sockets=array($kandk,$kandk2,$google); $continue=true; while($continue){ $continue=false; foreach($sockets as $obj) { if ($obj->check()) $continue=true; } if ($continue) usleep(100000); } echo "all done"; function cb($obj){ $len=strlen($obj->reply); echo microtime()." Got {$len} bytes from {$obj->hostname}\n"; } class async_socket { private $hostname,$port,$uri; private $res=false,$busy=false,$errno=false,$errstr=false; private $reply=''; private $callback=false,$args=array(); public function __get($name){ // Return private property as "read only" value. if (isset($this->$name)) return $this->$name; return null; } public function register_callback($callback,$args=array()){ $this->callback=$callback; $this->args=$args; } public function request($hostname,$uri='/',$port=80,$timeout=20){ if ($this->res) return false; // Connect $this->hostname=$hostname; $this->port=$port; $this->uri=$uri; $this->res=fsockopen($hostname,$port,$errno,$errstr,$timeout); if (!$this->res) return false; // Request stream_set_blocking($this->res,0); $request="GET $uri HTTP/1.1\r\nHost: $hostname\r\nConnection: Close\r\n\r\n"; if (!fwrite($this->res,$request)) return false; $this->busy=true; return true; } public function check(){ // Return true if still busy. // If connection closed, call callback-function (if registered). if (!$this->busy) return false; if (feof($this->res)) { $this->res=false; $this->busy=false; if ($this->callback) call_user_func_array($this->callback,$this->args); return false; } while (strlen($got=fread($this->res,1024))) $this->reply.=$got; return true; } }
実行結果
0.49500300 1272399245 0.03582900 1272399246 Got 9048 bytes from www.google.com 0.25463900 1272399246 Got 3585 bytes from kandk.cafe.coocan.jp 0.00156700 1272399248 Got 58650 bytes from kandk.cafe.coocan.jp all done
async_socketクラスは、GETメソッドで呼び出し、返事を受け取るとコールバック関数を実行する単純な仕様。POSTメソッドを利用したり拡張ヘッダ情報の送信
、あるいはKeep-Aliveな接続などを行いたい時は、スクリプトを修正すべし。
ソースコードの半ばあたり、"if ($continue) usleep(100000);"の行をコメントアウトすると、スクリプトの実行中はCPUの占有率が100%になる。このあたりの設定は、スピードとリソース消費の兼ね合いで、調整が必要なところ。今のような用途だと、100000マイクロ秒(0.1秒)のスリープで、リソース消費も最低限だし実行速度も問題なさそうだ。