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开发好。)
建立非阻塞连接后,下一步就是自定义通信协议了。后台开发同学说给我发个心跳,我再给你返还这个心跳就表明我是在线的。所以这里就是用select来进行一次写加一次读,部分代码如下
(0813更新----关于socket_select函数,一开始还以为是监听所有的连接,然后返回在规定时间内有状态变化的连接。然而后半句出错了,师兄说是一旦某个连接状态有变化就会返回,不是我想的在timeout时间内判断所有连接的状态。也就是说,在调用select的时候,有几个连接的状态已经改变,则立即返回这几个连接,否则才会等待timeout,等待过程中如果有一个连接状态发生变化,则立即返回这个连接,不会去管其他连接的。所以最后监测大量服务器在线的问题,最终采用了同步非阻塞的方式,即每个连接是非阻塞的,然后同步操作,对每个连接使用select来进行读写和超时判断。)
最后就是根据读取到的信息来判断在不在线的问题了,这部分很简单就不说了。
总结一下,本文就是考虑socket的阻塞连接比较耗时间,而且在数量稍大的情况下尤其费时,所以根据这一点写了点非阻塞连接socket的记录,这之后在页面上异步调用或者php文件里调用再或者作为后台服务运行都是可以的。
先说说搜索结果,出来最多的都是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文件里调用再或者作为后台服务运行都是可以的。
相关文章推荐
- 一个关于if else容易迷惑的问题
- java-模拟tomcat服务器
- Linux socket 初步
- PHP5.2.*防止Hash冲突拒绝服务攻击的Patch
- 深入理解PHP之匿名函数
- JSP/PHP基于Ajax的分页功能实现
- 关于PHP通过PDO用中文条件查询MySQL的问题。
- 什么是设计模式
- PHP数据库长连接mysql_pconnect的细节
- Php Installing An Expansion
- rem : web app适配的秘密武器
- jquery高级应用之Deferred对象
- 关于浮动与清除浮动,你应该知道的
- 数组reduce方法的高级技巧
- java socket 注意的地方
- java socket 注意的地方
- php7 读取php.ini[4]
- C#基于socket模拟http请求的方法