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&amp;mysql(二)
Oct 09 PHP
require(),include(),require_once()和include_once()的异同
Jan 02 PHP
php站内搜索并高亮显示关键字的实现代码
Dec 29 PHP
屏蔽机器人从你的网站搜取email地址的php代码
Nov 14 PHP
Zend Studio 实用快捷键一览表(精心整理)
Aug 10 PHP
PHP中大于2038年时间戳的问题处理方案
Mar 03 PHP
ThinkPHP里用U方法调用js文件实例
Jun 18 PHP
深入浅析yii2-gii自定义模板的方法
Apr 26 PHP
Yii2组件之多图上传插件FileInput的详细使用教程
Jun 20 PHP
CI框架无限级分类+递归的实现代码
Nov 01 PHP
PHP中Notice错误常见解决方法
Apr 28 PHP
thinkphp5 redis缓存新增方法实例讲解
Mar 24 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 字符串编码截取函数(兼容utf-8和gb2312)
2009/05/02 PHP
PHP数组内存耗用太多问题的解决方法
2010/04/05 PHP
php中转义mysql语句的实现代码
2011/06/24 PHP
PhpDocumentor 2安装以及生成API文档的方法
2014/05/21 PHP
laravel 4安装及入门图文教程
2014/10/29 PHP
初识PHP中的Swoole
2016/04/05 PHP
thinkPHP5项目中实现QQ第三方登录功能
2017/10/20 PHP
jquery 锁定弹出层实现代码
2010/02/23 Javascript
Javascript 闭包引起的IE内存泄露分析
2012/05/23 Javascript
jquery 绑定回车动作扑捉回车键触发的事件
2014/03/26 Javascript
jquery马赛克拼接翻转效果代码分享
2015/08/24 Javascript
jQuery基于ajax()使用serialize()提交form数据的方法
2015/12/08 Javascript
ES6实现的遍历目录函数示例
2017/04/07 Javascript
小程序点赞收藏功能的实现代码示例
2018/09/07 Javascript
详解如何探测小程序返回到webview页面
2019/05/14 Javascript
vue 导航内容设置选中状态样式的例子
2019/11/01 Javascript
如何基于layui的laytpl实现数据绑定的示例代码
2020/04/10 Javascript
Element MessageBox弹框的具体使用
2020/07/27 Javascript
微信小程序实现拼图小游戏
2020/10/22 Javascript
详解uniapp的全局变量实现方式
2021/01/11 Javascript
arcgis.js控制地图地体的显示范围超出区域自动弹回(实现思路)
2021/01/28 Javascript
vue如何使用rem适配
2021/02/06 Vue.js
python解析xml文件实例分享
2013/12/04 Python
删除python pandas.DataFrame 的多重index实例
2018/06/08 Python
python Qt5实现窗体跟踪鼠标移动
2019/12/13 Python
python给视频添加背景音乐并改变音量的具体方法
2020/07/19 Python
HTML5 通过Vedio标签实现视频循环播放的示例代码
2020/08/05 HTML / CSS
意大利网上药房:Farmacia 33
2020/01/27 全球购物
十佳美德少年事迹材料
2014/02/05 职场文书
信息专业毕业生五年职业规划参考
2014/02/06 职场文书
总经理的岗位职责
2014/02/23 职场文书
文明美德伴我成长演讲稿
2014/05/12 职场文书
实习护士自荐信
2014/06/21 职场文书
租房协议书范文
2014/08/20 职场文书
信息简报范文
2015/07/21 职场文书
HTML+JS实现在线朗读器
2022/02/15 Javascript