以前写过一个multi_http()的函数,就是异步地做http请求,后来再次看那段代码的时候,发现不少问题:
1. 我只在发送、接受数据的时候使用了异步,connect的时候还是同步的
2. 由于我发送数据的时候使用的是http1.0,所以接受数据的时候靠feof()判断结束就很方便了;如果使用http1.1,而且connection:keep-alive; 那么就不是那么简单了
如果需要异步并发,建议使用curl,今天看了一下,curl在异步并发请求的时候,connect、send、recv都是异步的。测试代码:
-
<?php
-
// 创建一对cURL资源
-
$ch1 = curl_init();
-
$ch2 = curl_init();
-
-
// 设置URL和相应的选项
-
curl_setopt($ch1, CURLOPT_URL, "http://phpor.net/tools/whoami.php");
-
curl_setopt($ch1, CURLOPT_HEADER, 0);
-
curl_setopt($ch2, CURLOPT_URL, "http://phpor.net/tools/whoami.php");
-
curl_setopt($ch2, CURLOPT_HEADER, 0);
-
-
// 创建批处理cURL句柄
-
$mh = curl_multi_init();
-
-
// 增加2个句柄
-
curl_multi_add_handle($mh,$ch1);
-
curl_multi_add_handle($mh,$ch2);
-
-
$active = null;
-
// 执行批处理句柄
-
do {
-
$mrc = curl_multi_exec($mh, $active);
-
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
-
-
while ($active && $mrc == CURLM_OK) {
-
if (curl_multi_select($mh) != –1) {
-
do {
-
$mrc = curl_multi_exec($mh, $active);
-
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
-
}
-
}
-
-
// 关闭全部句柄
-
curl_multi_remove_handle($mh, $ch1);
-
curl_multi_remove_handle($mh, $ch2);
-
curl_multi_close($mh);
使用strace观察一下:
strace -tt php multi_curl.php
connect(3,{sa_family=AF_INET,sin_port=htons(80), sin_addr=inet_addr("66.147.244.189")}, 16) = -1 EINPROGRESS (Operation now in progress)
poll([{fd=3, events=POLLOUT}], 1, 0) = 0
虽然是异步,这里还是立即检查了一下是否已经连接成功;但是这种检查也是非阻塞的(看poll的第三个参数)。如果connect连接的是本地端口,poll检查的时候连接就已经是成功的了。 如(10.55.38.14是本机):
connect(4, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("10.55.38.14")}, 16) = -1 EINPROGRESS (Operation now in progress)
poll([{fd=4, events=POLLOUT, revents=POLLOUT}], 1, 0) = 1
——————————————
下面这篇文章写的并不好,仅作学习之用:
这里关键是socket_select的用法了;
另:
1. 当阻塞方式connect的时候,设置连接超时时间是通过设置SO_SNDTIMEO来实现的,如:
socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, array(‘sec’ => 3, ‘usec’ => 0));
设置超时时间为3s;
2. 对于非阻塞方式的connect,连接的超时时间不是在socket上设置的,而是自己控制循环的时间
-
<?php
-
-
$arrTarget = array(
-
array("host"=>"10.55.38.61", "port"=>80),
-
array("host"=>"66.147.244.18", "port"=>80),
-
array("host"=>"10.55.38.63", "port"=>82),
-
);
-
$arrResult = multi_connect($arrTarget);
-
-
print_r($arrResult);
-
exit;
-
-
function multi_connect($arrTarget) {
-
$arrSocket = array();
-
foreach($arrTarget as $key=>$pair) {
-
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
-
socket_set_nonblock($socket);
-
@socket_connect($socket, $pair["host"], $pair["port"]);
-
$arrSocket[$key] = $socket;
-
}
-
$arrLeft = $arrSocket;
-
$arrSocketWrite = $arrSocket;
-
$arrSocketRead = null;
-
$arrExcept = array();
-
$all = count($arrTarget);
-
$arrOk = array();
-
-
$timeout = 5;
-
$oritimeout = $timeout;
-
-
while($all > 0 && $timeout > 0) {
-
$time_start = time();
-
$done = socket_select($arrSocketRead, $arrSocketWrite,$arrExcept,$timeout);
-
$timeout -= (time() – $time_start);
-
if ($done <= 0) {
-
//error or timeout
-
echo "Timeout $oritimeout(s)n";
-
break;
-
}
-
$all -= $done;
-
-
foreach($arrSocketWrite as $key=>$val) {
-
$arrOk[$key] = $val;
-
}
-
$arrLeft = array_diff($arrLeft, $arrSocketWrite);
-
-
echo "n—-connect ok:——————–n";
-
print_r($arrSocketWrite);
-
echo "n—-except:——————–n";
-
print_r($arrExcept);
-
echo "n–left:———————-n";
-
print_r($arrSocketWrite);
-
-
echo "n====================================n";
-
-
$arrSocketWrite = $arrLeft;
-
-
}
-
return $arrOk;
-
}