您的位置:首页 > 编程语言 > PHP开发

php的非阻塞套接字socket

2016-07-24 22:39 706 查看
        本文的需求是检测数量不确定的服务器在不在线的问题。一开始是准备网页异步调用php去检测socket连接,所以没弄过这块内容的我就上网搜了类似php异步调用socket等内容。真是~。~

        先说说搜索结果,出来最多的都是ajax,curl,fsockopen和popen等等。偶然间发现了一篇文章中提到了socket中的select函数,然后仔细看了看,才发现自己把非阻塞和异步搞混了。这个问题本可以继续深入,这里暂且不说。现在来说说非阻塞套接字在本文需求下的应用。

         socket_select函数相关的代码不多,网上多得是c语言的select函数的用法,基本流程是建立一个套接字,设为非阻塞的,然后再connect,这个时候就用select在timeout时间内选择建立好的连接来进行读写,最后用完关掉。一般人或许就照搬c语言代码就差不多了,把相应的函数改成php里的形式。老实说,一开始我也这么做的,结果失败了。网上有一份类似的php写的socket代码,里面的错误有不少,我都给改了,但结果还是报错10035,无法立即建立一个非阻塞套接字连接。于是找到socket_connect的manual,就让我在user
contributed notes发现了这么一段话。

        If you're using non-blocking, be sure not to turn it on until after you connect, otherwise you will get the mesasge: PHP Warning:  socket_connect() unable to connect [115]: Operation now in progress in file.php on line 123 and socket_connect() will
return false (even though it will connect).

        虽然这段话是13年前的,但我觉得还是有道理的,因为我把设为非阻塞的操作和connect的操作换了下顺序,真的就成功连接上了。然而问题在于每次连接都是先阻塞得连接好再设为非阻塞的,每次connect的时间真是个问题,一旦对方没响应,等个5秒都是小意思,然后我就重新去看了下c语言的代码。这个时候我才发现,我被上述这段话迷惑了,c语言的基本流程顺序是没有问题的,那就是针对非阻塞的套接字。每个套接字先设为非阻塞的然后再connect,虽然这样会报错,但还是会继续连接的,仅仅只是不能立马完成而已,正如上述那段英文的最后
= =。

        然而一个致命的问题让我困扰了好久,上述connect后根据报出的错误numbe来判断是不是我们想要的连接。简单来说,就是报出的错误如果是10035,那么就可以进行下一步任务,如果是其他错误那就不行了。所以一开始还以为是115又浪费了不少时间。

        (0813更新----然而,在不同的操作系统中,error number好像是不一样的,windows下是10035,Linux下就是115了,果然还是少用windows开发好。)

define('EINPROGRESS', 10035);
function noblockingsocket($ip, $port, &$errstr, &$errno){
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if (!$socket ) {
$errno = socket_last_error($socket);
$errstr = socket_strerror($errno);
return false;
}
if (socket_set_nonblock($socket)) {
$rr = @socket_connect($socket, $ip, $port);
if ($rr || socket_last_error() == EINPROGRESS) {
$errno = EINPROGRESS;
$errstr = socket_strerror($errno);
return $socket;
}
}
$errno = socket_last_error($socket);
$errstr = socket_strerror($errno);
socket_close($socket);
return false;
}


        建立非阻塞连接后,下一步就是自定义通信协议了。后台开发同学说给我发个心跳,我再给你返还这个心跳就表明我是在线的。所以这里就是用select来进行一次写加一次读,部分代码如下

function writesocket($sockets, &$status, $timeout, &$errstr, &$errno, $in){
if (count($sockets)){
$write = $sockets;
$n = socket_select($read = null, $write , $e = null
c5fc
, $timeout);
 
if ($n > 0) {
foreach ($write as $w) {
$id = array_search($w, $sockets);
socket_write($w, $in, strlen($in));
$status[$id] = 2;
echo "message is sent";
}
}else{
foreach ($sockets as $id => $s){
$status[$id] = -31;        //timeout
}
}
}
}


        (0813更新----关于socket_select函数,一开始还以为是监听所有的连接,然后返回在规定时间内有状态变化的连接。然而后半句出错了,师兄说是一旦某个连接状态有变化就会返回,不是我想的在timeout时间内判断所有连接的状态。也就是说,在调用select的时候,有几个连接的状态已经改变,则立即返回这几个连接,否则才会等待timeout,等待过程中如果有一个连接状态发生变化,则立即返回这个连接,不会去管其他连接的。所以最后监测大量服务器在线的问题,最终采用了同步非阻塞的方式,即每个连接是非阻塞的,然后同步操作,对每个连接使用select来进行读写和超时判断。)
        最后就是根据读取到的信息来判断在不在线的问题了,这部分很简单就不说了。

        总结一下,本文就是考虑socket的阻塞连接比较耗时间,而且在数量稍大的情况下尤其费时,所以根据这一点写了点非阻塞连接socket的记录,这之后在页面上异步调用或者php文件里调用再或者作为后台服务运行都是可以的。

        
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  php select socket 非阻塞