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 相关文章推荐
通过对php一些服务器端特性的配置加强php的安全
Oct 09 PHP
php 远程关机操作的代码
Dec 05 PHP
php完全过滤HTML,JS,CSS等标签
Jan 16 PHP
7个超级实用的PHP代码片段
Jul 11 PHP
Pain 全世界最小最简单的PHP模板引擎 (普通版)
Oct 23 PHP
set_include_path和get_include_path使用及注意事项
Feb 02 PHP
解析PHP可变函数的经典用法
Jun 20 PHP
php记录代码执行时间(实现代码)
Jul 05 PHP
codeigniter上传图片不能正确识别图片类型问题解决方法
Jul 25 PHP
php采用ajax数据提交post与post常见方法总结
Nov 10 PHP
php常用字符函数实例小结
Dec 29 PHP
Laravel如何同时连接多个数据库详解
Aug 13 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
PHP的分页功能
2007/03/21 PHP
php+Ajax处理xml与json格式数据的方法示例
2019/03/04 PHP
php常用的工具开发整理
2019/09/26 PHP
jquery ajax提交表单数据的两种方式
2009/11/24 Javascript
基于jquery的图片的切换(以数字的形式)
2011/02/14 Javascript
Javascript 鼠标移动上去小三角形滑块缓慢跟随效果
2013/04/26 Javascript
javascript ready和load事件的区别示例介绍
2013/08/30 Javascript
javascript打印输出json实例
2013/11/11 Javascript
JavaScript—window对象使用示例
2013/12/09 Javascript
javascript与有限状态机详解
2014/05/08 Javascript
js中new一个对象的过程
2017/02/20 Javascript
jquery中绑定事件的异同
2017/02/28 Javascript
Nodejs+angularjs结合multiparty实现多图片上传的示例代码
2017/09/29 NodeJs
nodejs基于WS模块实现WebSocket聊天功能的方法
2018/01/12 NodeJs
原生JS实现循环Nodelist Dom列表的4种方式示例
2018/02/11 Javascript
利用npm 安装删除模块的方法
2018/05/15 Javascript
使用javascript做时间倒数读秒功能的实例
2019/01/23 Javascript
vue使用keep-alive保持滚动条位置的实现方法
2019/04/09 Javascript
解决包含在label标签下的checkbox在ie8及以下版本点击事件无效果兼容的问题
2019/10/27 Javascript
js实现3D旋转相册
2020/08/02 Javascript
VUE UPLOAD 通过ACTION返回上传结果操作
2020/09/07 Javascript
python脚本替换指定行实现步骤
2017/07/11 Python
PyQt5每天必学之带有标签的复选框
2018/04/19 Python
Python迭代器iterator生成器generator使用解析
2019/10/24 Python
python制作微博图片爬取工具
2021/01/16 Python
英国泰坦旅游网站:全球陪同游览,邮轮和铁路旅行
2016/11/29 全球购物
澳大利亚礼品卡商店:Gift Card Store
2019/06/24 全球购物
光电信息专业应届生求职信
2013/10/07 职场文书
集体生日活动方案
2014/08/18 职场文书
证券公司客户经理岗位职责
2015/04/09 职场文书
讲座通知范文
2015/04/23 职场文书
驳回起诉民事裁定书
2015/05/19 职场文书
家长对学校的意见和建议
2015/06/03 职场文书
酒店宣传语大全
2015/07/13 职场文书
民事纠纷协议书
2016/03/23 职场文书
python自然语言处理之字典树知识总结
2021/04/25 Python