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秒)のスリープで、リソース消費も最低限だし実行速度も問題なさそうだ。