首页 > 编程 > PHP > 正文

PHP网络编程 之Accept 阻塞模型的介绍

2020-03-22 19:15:07
字体:
来源:转载
供稿:网友
本篇文章给大家分享的内容是关于PHP网络编程 之Accept 阻塞模型的介绍,内容很详细,有需要的朋友可以参考一下,希望可以帮助到大家。

Accept 阻塞模型是一种相对古老的模型,不过里面蕴含了许多有趣的知识,比如阻塞/非阻塞、锁、超时重传...

服务端程序 acceptSever.php
 ?php
$socket = stream_socket_server( tcp://0.0.0.0:{$port} , $errno, $errstr); if (!$socket) die( $errstr ($errno)
while ($conn = stream_socket_accept($socket, -1)) { // 这样设置不超时才有用 html' target='_blank'>static $id = 0; # 进程 id static $ct = 0; # 接收数据的长度 $ct_last = $ct; $ct_data = # 接受的数据 $buffer = # 分段读取数据 $id++; echo Client $id come . PHP_EOL; # 持续监听 while (!preg_match( {/r/n} , $buffer)) { // 没有读到结束符,继续读// if (feof($conn)) break; // 防止 popen 和 fread 的 bug 导致的死循环 $buffer = fread($conn, 1024); echo R . PHP_EOL; # 打印读的次数 $ct += strlen($buffer); $ct_data .= preg_replace( {/r/n} , , $buffer); $ct_size = ($ct - $ct_last) * 8; echo [$id] . __METHOD__ . . $ct_data . PHP_EOL; fwrite($conn, Received $ct_size byte data./r/n fclose($conn); fclose($socket);new SocketServer(2000);
客户端程序 acceptClient.php
 ?php# 日志记录function debug ($msg) error_log($msg, 3, ./socket.log if ($argv[1]) { $socket_client = stream_socket_client( tcp://0.0.0.0:2000 , $errno, $errstr, 30); /* 设置脚本为非阻塞 */# stream_set_blocking($socket_client, 0); /* 设置脚本超时时间 */# stream_set_timeout($socket_client, 0, 100000); if (!$socket_client) { die( $errstr ($errno)  } else { # 填充容器 $msg = trim($argv[1]); for ($i = 0; $i $i++) { $res = fwrite($socket_client, $msg($i)  usleep(100000); echo W // 打印写的次数# debug(fread($socket_client, 1024)); // 将产生死锁,因为 fread 在阻塞模式下未读到数据时将等待 fwrite($socket_client, /r/n // 传输结束符 # 记录日志 debug(fread($socket_client, 1024)); fclose($socket_client);else {// $phArr = array();// for ($i = 0; $i $i++) {// $phArr[$i] = popen( php .__FILE__. {$i}:test , r // foreach ($phArr as $ph) {// pclose($ph); for ($i = 0; $i $i++) { system( php .__FILE__. {$i}:test # 这里等于 php 当前文件 脚本参数 }
代码解析

首先,解释一下以上的代码逻辑:客户端 acceptClient.php 循环发送数据,最后发送结束符;服务端 accept Server.php 使用 accept 阻塞方式接收 socket 连接,然后循环接收数据,直到收到结束符,返回结果数据(接收到的字节数)客户端收到服务器返回的数据,写入日志。虽然逻辑很简单,但是其中有几种情况很值得分析一下:

A 默认情况下,运行 php socket_client.php test,客户端打出 10 个 W,服务端打出若干个 R 后面是接收到的数据,socket.log 记录下服务端返回的接收结果数据,效果如下:
1500280071-5b5b18674c9ed_articlex.png
这种情况很容易理解,不再赘述。然后,使用 telnet 命令同时打开多个客户端,你会发现服务器一个时间只处理一个客户端,如图所示:
3477710993-5b5b1a8e66d59_articlex.png
其他需要在后面“排队”;这就是阻塞 IO 的特点,这种模式的弱点很明显,效率极低。

B 只打开 socket_client.php 第 29 行的注释代码,再次运行 php socket_client.php test 客户端打出一个 W,服务端也打出一个 R,之后两个程序都卡住了。这是为什么呢,分析逻辑后你会发现,这是由于客户端在未发送结束符之前就向服务端要返回数据;而服务端由于未收到结束符,也在向客户端要结束符,造成死锁。而之所以只打出一个 W 和 R,是因为 fread 默认是阻塞的。要解决这个死锁,必须打开 socket_client.php 第 17 行的注释代码,给 socket 设置一个 0.1 秒的超时,再次运行你会发现隔 0.1 秒出现一个 W 和 R 之后正常结束,服务端返回的接收结果数据也正常记录了。可见 fread 缺省是阻塞的,我们在编程的时候要特别注意,如果没有设置超时,就很容易会出现死锁。

C 只打开 14 行注释设置脚本为非阻塞,运行 php socket_client.php test,结果基本和情况 A 相同,唯一不同的是 socket.log 没有记录下返回数据,这是因为当我们非阻塞下客户端不必等待接收到服务器的响应结果就可以继续往下执行,当执行到 debug 的时候,读取的数据还是空的,所以 socket.log 也是空的。这里可以看出客户端运行在阻塞和非阻塞模式的区别,当然在客户端不在乎接受结果的情况下,可以使用非阻塞模式来获得最大效率。

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表