php fsockopen解决办法 php实现多线程


Posted in PHP onJanuary 20, 2014

回答:
当有人想要实现并发功能时,他们通常会想到用fork或者spawn
threads,但是当他们发现php不支持多线程的时候,大概会转换思路去用一些不够好的语言,比如perl。
其实的是大多数情况下,你大可不必使用fork或者线程,并且你会得到比用fork或thread更好的性能。
假设你要建立一个服务来检查正在运行的n台服务器,以确定他们还在正常运转。你可能会写下面这样的代码:

<?php 
$hosts = array("host1.sample.com", "host2.sample.com", "host3.sample.com"); 
$timeout = 15; $status = array(); 
foreach ($hosts as $host) { 
$errno = 0; 
$errstr = ""; 
$s = fsockopen($host, 80, $errno, $errstr, $timeout); 
if ($s) { 
$status[$host] = "Connectedn"; 
fwrite($s, "HEAD / HTTP/1.0rnHost: $hostrnrn"); 
do { 
$data = fread($s, 8192); 
if (strlen($data) == 0) { break; }
$status[$host] .= $data; 
} while (true); fclose($s); 
} else { 
$status[$host] = "Connection failed: $errno $errstrn"; 
} 
} 
print_r($status); 
?>

它运行的很好,但是在fsockopen()分析完hostname并且建立一个成功的连接(或者延时$timeout秒)之前,扩充这段代码来管理大量服务器将耗费很长时间。
因此我们必须放弃这段代码;我们可以建立异步连接-不需要等待fsockopen返回连接状态。PHP仍然需要解析hostname(所以直接使用ip更加明智),不过将在打开一个连接之后立刻返回,继而我们就可以连接下一台服务器。
有两种方法可以实现;PHP5中可以使用新增的stream_socket_client()函数直接替换掉fsocketopen()。PHP5之前的版本,你需要自己动手,用sockets扩展解决问题。
下面是PHP5中的解决方法:

<?php 
$hosts = array("host1.sample.com", "host2.sample.com", "host3.sample.com"); 
$timeout = 15; 
$status = array(); 
$sockets = array(); 
foreach ($hosts as $id => $host) { 
$s = stream_socket_client("$host:80", $errno, $errstr, $timeout,STREAM_CLIENT_ASYNC_CONNECT|STREAM_CLIENT_CONNECT); 
if ($s) { 
$sockets[$id] = $s; 
$status[$id] = "in progress"; 
} else { 
$status[$id] = "failed, $errno $errstr"; 
} 
} 
while (count($sockets)) { 
$read = $write = $sockets; 
$n = stream_select($read, $write, $e = null, $timeout); 
if ($n > 0) { 
foreach ($read as $r) { 
$id = array_search($r, $sockets); 
$data = fread($r, 8192); 
if (strlen($data) == 0) { 
if ($status[$id] == "in progress") { 
$status[$id] = "failed to connect"; 
} 
fclose($r); 
unset($sockets[$id]); 
} else { 
$status[$id] .= $data; 
} 
} 
foreach ($write as $w) { 
$id = array_search($w, $sockets); 
fwrite($w, "HEAD / HTTP/1.0rnHost: " . $hosts[$id] . "rnrn"); 
$status[$id] = "waiting for response"; 
} 
} else { 
foreach ($sockets as $id => $s) { 
$status[$id] = "timed out " . $status[$id]; 
} 
break; 
} 
} 
foreach ($hosts as $id => $host) { 
echo "Host: $hostn"; echo "Status: " . $status[$id] . "nn"; 
} 
?>

我们用stream_select()等待sockets打开的连接事件。stream_select()调用系统的select(2)函数来工作:前面三个参数是你要使用的streams的数组;你可以对其读取,写入和获取异常(分别针对三个参数)。stream_select()可以通过设置$timeout(秒)参数来等待事件发生-事件发生时,相应的sockets数据将写入你传入的参数。
下面是PHP4.1.0之后版本的实现,如果你已经在编译PHP时包含了sockets(ext/sockets)支持,你可以使用根上面类似的代码,只是需要将上面的streams/filesystem函数的功能用ext/sockets函数实现。主要的不同在于我们用下面的函数代替
stream_socket_client()来建立连接:

<?php 
// This value is correct for Linux, other systems have other values 
define('EINPROGRESS', 115); 
function non_blocking_connect($host, $port, &$errno, &$errstr, $timeout) { 
$ip = gethostbyname($host); 
$s = socket_create(AF_INET, SOCK_STREAM, 0); 
if (socket_set_nonblock($s)) { 
$r = @socket_connect($s, $ip, $port); 
if ($r || socket_last_error() == EINPROGRESS) { 
$errno = EINPROGRESS; return $s; 
} 
} 
$errno = socket_last_error($s); 
$errstr = socket_strerror($errno); 
socket_close($s); 
return false; 
} 
?>

现在用socket_select()替换掉stream_select(),用socket_read()替换掉fread(),用
socket_write()替换掉fwrite(),用socket_close()替换掉fclose()就可以执行脚本了!
PHP5的先进之处在于,你可以用stream_select()处理几乎所有的stream-例如你可以通过include
STDIN用它接收键盘输入并保存进数组,你还可以接收通过proc_open()打开的管道中的数据。
如果你想让PHP4.3.x自身拥有处理streams的功能,我已经为你准备了一个让fsockopen可以异步工作的patch。不赞成使用该补丁,该补丁不会出现在官方发布的PHP版本中,我在补丁中附带了stream_socket_client()函数的实现,通过它,你可以让你的脚本兼容
PHP5。

PHP 相关文章推荐
rephactor 优秀的PHP的重构工具
Jun 09 PHP
php源代码安装常见错误与解决办法分享
May 28 PHP
数组与类使用PHP的可变变量名需要的注意的问题
Jun 20 PHP
探讨GDFONTPATH能否被winxp下的php支持
Jun 21 PHP
php实现下载限制速度示例分享
Feb 13 PHP
php使用Image Magick将PDF文件转换为JPG文件的方法
Apr 01 PHP
[原创]ThinkPHP让../Public在模板不解析(直接输出)的方法
Oct 09 PHP
详解WordPress中给链接添加查询字符串的方法
Dec 18 PHP
简单谈谈php延迟静态绑定
Jan 26 PHP
实现PHP框架系列文章(6)mysql数据库方法
Mar 04 PHP
PHP实现的同步推荐操作API接口案例分析
Nov 30 PHP
php base64 编码与解码实例代码
Mar 21 PHP
linux系统下php安装mbstring扩展的二种方法
Jan 20 #PHP
php共享内存段示例分享
Jan 20 #PHP
php使用base64加密解密图片示例分享
Jan 20 #PHP
用Zend Studio+PHPnow+Zend Debugger搭建PHP服务器调试环境步骤
Jan 19 #PHP
php实现可以设置中奖概率的抽奖程序代码分享
Jan 19 #PHP
php根据身份证号码计算年龄的实例代码
Jan 18 #PHP
php 启动报错如何解决
Jan 17 #PHP
You might like
Linux Apache PHP Oracle 安装配置(具体操作步骤)
2013/06/17 PHP
PHP生成不重复随机数的方法汇总
2014/11/19 PHP
PHP中配置IIS7实现基本身份验证的方法
2015/09/24 PHP
php array_keys 返回数组的键名
2016/10/25 PHP
获取Javscript执行函数名称的方法
2006/12/22 Javascript
页面中body onload 和 window.onload 冲突的问题的解决
2009/07/01 Javascript
javascript对象的使用和属性操作示例详解
2014/03/02 Javascript
jquery $.trim()去除字符串空格的实现方法【附图例】
2016/03/30 Javascript
利用Angular.js限制textarea输入的字数
2016/10/20 Javascript
jQuery实现的省市联动菜单功能示例【测试可用】
2017/01/13 Javascript
JavaScript中数组Array方法详解
2017/02/27 Javascript
javascript闭包功能与用法实例分析
2017/04/06 Javascript
web前端页面生成exe可执行文件的方法
2018/02/08 Javascript
关于Vue的路由权限管理的示例代码
2018/03/06 Javascript
基于datepicker定义自己的angular时间组件的示例
2018/03/14 Javascript
JS 自执行函数原理及用法
2019/08/05 Javascript
详解vue父子组件状态同步的最佳方式
2020/09/10 Javascript
python快速排序代码实例
2013/11/21 Python
python实现电子词典
2020/04/23 Python
Python 创建空的list,以及append用法讲解
2018/05/04 Python
基于DataFrame改变列类型的方法
2018/07/25 Python
python 保存float类型的小数的位数方法
2018/10/17 Python
对Python3 * 和 ** 运算符详解
2019/02/16 Python
python binascii 进制转换实例
2019/06/12 Python
24式加速你的Python(小结)
2019/06/13 Python
50行Python代码实现视频中物体颜色识别和跟踪(必须以红色为例)
2019/11/20 Python
PyQt5+Pycharm安装和配置图文教程详解
2020/03/24 Python
python图片指定区域替换img.paste函数的使用
2020/04/09 Python
CSS3实现全景图特效示例代码
2018/03/26 HTML / CSS
MIRTA官网:手工包,100%意大利制造
2020/02/11 全球购物
求职推荐信范文
2013/12/01 职场文书
小学生打架检讨书
2014/01/26 职场文书
退休感言
2014/01/28 职场文书
车间质检员岗位职责
2015/04/08 职场文书
2015年英语教研组工作总结
2015/05/23 职场文书
无婚姻登记记录证明
2015/06/18 职场文书