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 相关文章推荐
解决了Ajax、MySQL 和 Zend Framework 的乱码问题
Mar 03 PHP
使用php清除bom示例
Mar 03 PHP
php调用c接口无错版介绍
Mar 11 PHP
PHP实现多图片上传类实例
Jul 26 PHP
PHP多个文件上传到服务器实例
Oct 29 PHP
php页面缓存方法小结
Jan 10 PHP
DOM基础及php读取xml内容操作的方法
Jan 23 PHP
PHP如何将log信息写入服务器中的log文件
Jul 29 PHP
Linux下编译redis和phpredis的方法
Apr 07 PHP
CakePHP框架Session设置方法分析
Feb 23 PHP
PHP实现十进制、二进制、八进制和十六进制转换相关函数用法分析
Apr 25 PHP
PHP设计模式之数据访问对象模式(DAO)原理与用法实例分析
Dec 12 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两种去掉数组重复值的方法比较
2014/06/19 PHP
PHP使用PHPexcel导入导出数据的方法
2015/11/14 PHP
php有效防止同一用户多次登录
2015/11/19 PHP
深入解析PHP中foreach语句控制数组循环的用法
2015/11/30 PHP
PHP文件系统管理(实例讲解)
2017/09/19 PHP
网页开发中的容易忽略的问题 javascript HTML中的table
2009/04/15 Javascript
浅谈JavaScript编程语言的编码规范
2011/10/21 Javascript
精心挑选的12款优秀的基于jQuery的手风琴效果插件和教程
2012/08/22 Javascript
JavaScript将取代AppleScript?
2014/09/18 Javascript
前端必备神器 Snap.svg 弹动效果
2014/11/10 Javascript
jQuery实现Flash效果上下翻动的中英文导航菜单代码
2015/09/22 Javascript
使用jQuery的toggle()方法对HTML标签进行显示、隐藏的方法(示例)
2016/09/01 Javascript
Javascript农历与公历相互转换的简单实例
2016/10/09 Javascript
wap手机端解决返回上一页的js实例
2016/12/08 Javascript
vue实现类似淘宝商品评价页面星级评价及上传多张图片功能
2018/10/29 Javascript
bootstrap与pagehelper实现分页效果
2018/12/29 Javascript
nodejs中各种加密算法的实现详解
2019/07/11 NodeJs
Python实现SSH远程登陆,并执行命令的方法(分享)
2017/05/08 Python
详解Python import方法引入模块的实例
2017/08/02 Python
Python迭代器定义与简单用法分析
2018/04/30 Python
python抓取网站的图片并下载到本地的方法
2018/05/22 Python
python3 常见解密加密算法实例分析【base64、MD5等】
2019/12/19 Python
Python列表操作方法详解
2020/02/09 Python
Python+Kepler.gl实现时间轮播地图过程解析
2020/07/20 Python
Python非单向递归函数如何返回全部结果
2020/12/18 Python
python Pexpect模块的使用
2020/12/25 Python
CSS3 实现童年的纸飞机
2019/05/05 HTML / CSS
全球最大的网上自行车商店:Chain Reaction Cycles
2016/12/02 全球购物
澳大利亚游乐场设备品牌:Lifespan Kids
2019/05/24 全球购物
英语专业学子个人的自我评价
2013/10/02 职场文书
毕业生求职简历中的自我评价
2013/10/18 职场文书
历史系毕业生自荐信
2013/10/28 职场文书
幼儿园爱国卫生月活动总结
2014/06/30 职场文书
购房意向书
2014/08/30 职场文书
新手,如何业余时间安排好写作、提高写作能力?
2019/10/21 职场文书
全新239军机修复记
2022/04/05 无线电