首页
下载
文档
问答社区
视频
捐赠
源代码
AI 助理
赞助商
CRMEB
Apipost
腾讯云
微擎
禅道
51Talk
商业产品
Swoole AI 智能文档翻译器
Swoole-Compiler PHP 代码加密器
CRMEB 新零售社交电商系统
登录
注册
全部
提问
分享
讨论
建议
公告
开发框架
CodeGalaxy
发表新帖
fgets标准输入输出,会阻塞tcp客户端通信,怎么处理?[已解决]
{{{php $handle=fopen("php://stdin", "r"); $str=fgets($handle); }}} 编写tcp客户端,这两行代码无论放在哪,都会阻塞$client->send()方法。请问如何才能不阻塞? ## 坑 tcp client端编程,无论是循环,还是读取标准输入,都会阻塞client的事件监听,导致各种奇怪的问题。虽然问题解决了,但我还不肯定我明白了其中的道理。 ## 解决方法如下: ### tcp server 端 {{{php <?php /** * 演示swoole tcp server * User: dylan * Date: 2018/1/20 * Time: 14:28 */ // 客户端id=>上次心跳时间数组 $GLOBALS['heartMap']=[]; //创建Server对象,监听 127.0.0.1:9501端口 $serv = new swoole_server("127.0.0.1", 9501); $serv->set(array( 'open_eof_split' => true, //打开EOF_SPLIT检测 'open_eof_check' => true, //打开EOF检测 'package_eof' => "\r\n", //设置EOF )); //监听连接进入事件 $serv->on('connect', function ($serv, $fd) { echo "Client: Connect.\n"; $GLOBALS['heartMap'][$fd] = time(); }); //监听数据接收事件 $serv->on('receive', function ($serv, $fd, $from_id, $data) use(&$heartMap) { $data = trim($data); if($data == "ping"){ $GLOBALS['heartMap'][$fd] = time(); // 其实只要有数据就可以认为有心跳 $serv->send($fd,"pong\r\n"); }else{ $serv->send($fd, "Server: ".$data."\r\n"); } }); //监听连接关闭事件 $serv->on('close', function ($serv, $fd) { echo "Client: Close.\n"; }); $serv->on('workerStart', function (swoole_server $serv,$worker_id) use(&$heartMap) { echo "Server: start.\n"; // 心跳检测 // 全局变量 $heartCheck = $serv->tick(1000*5, function () use (&$serv,&$heartMap) { $now = time(); // var_dump($GLOBALS['heartMap']); if($GLOBALS['heartMap']){ foreach ($GLOBALS['heartMap'] as $id=>$time){ if(($now - $time) >10){ echo $id."因超时而断开链接"; $serv->close($id,true); unset($GLOBALS['heartMap'][$id]); } } } }); }); //启动服务器 $serv->start(); }}} ### tcp client 端 {{{php <?php /** * 演示 tcp client端循环发送标准输入给tcp server端 * User: dylan * Date: 2018/1/21 * Time: 16:50 */ $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); $client->set(array( 'open_eof_check' => true, 'open_eof_split' => true, 'package_eof' => "\r\n", )); //注册连接成功回调 $client->on("connect", function($cli) { $cli->send("hello world\r\n"); }); //注册数据接收回调 $client->on("receive", function($cli, $data) use(&$timeout_heater){ $data = trim($data); if($data == "pong"){ $timeout_heater = time(); }else{ echo "Received: ".$data."\n"; } }); // 注册连接失败回调 $client->on("error", function($cli){ echo "Connect failed\n"; }); // 注册连接关闭回调 // 连接关闭时,清理业务和心跳检测 $client->on("close", function($cli) use(&$tick_heater,&$info){ // Swoole\Timer::clear($info); Swoole\Timer::clear($tick_heater); echo "Connection close\n"; }); //发起连接 $client->connect('127.0.0.1', 9501, 0.5); // 心跳检测 $timeout_heater = time(); $tick_heater = Swoole\Timer::tick(1000 * 3, function () use ($client,&$timeout_heater,&$tick_heater) { $client->send("ping\r\n"); if((time() - $timeout_heater) >30){ echo "心跳检测超时"; $client->close(); Swoole\Timer::clear($tick_heater); } }); /** * 启动子进程 * @param $client */ function io(&$client){ $process = new Swoole\Process('asyncChildren',false); $pid = $process->start(); echo "进程{$pid}创建成功.\r\n"; // 用事件机制接收子进程消息 Swoole\Event::add($process->pipe, function($pipe) use ($process,$client) { $recv = trim($process->read()); if ($recv) { echo "From Worker: " . $recv; $client->send($recv."\r\n"); } }); // 回收子进程 swoole_process::signal(SIGCHLD, function($sig){ while($p = Swoole\Process::wait(false)){ echo "进程{$p['pid']}回收完成\r\n"; } }); } /** * 异步子进程构造函数 * * @param \Swoole\Process $worker */ function asyncChildren(Swoole\Process $worker){ $pid = $worker->pid; $worker->name(sprintf('php-ps:%d',$pid)); // 不知道干嘛用 swoole_process::signal(SIGTERM, function($signal_num) use ($worker) { echo "signal call = $signal_num, #{$worker->pid}\n"; }); // 发送消息给主进程测试 // Swoole\Timer::tick(1000, function () use ($worker) // { // if (rand(1, 3) % 2) { // $worker->write("hello master {$worker->pid}\n"); // } // }); // 标准输入消息测试 $io = true; while ($io) { $data = trim(fread(STDIN, 8192)); if ($data == ":q") { $io = false; break; } else { // echo $data; $worker->write("hello master: {$data}\n"); } } // 从主进程读取消息 Swoole\Event::add($worker->pipe, function($pipe) use($worker) { $recv = $worker->read(); echo "From Master: $recv\n"; }); } io($client); }}}
发布于5年前 · 2 次浏览 · 来自
提问
彳亍上杉
{{{php $handle=fopen("php://stdin", "r"); $str=fgets($handle); }}} 编写tcp客户端,这两行代码无论放在哪,都会阻塞$client->send()方法。请问如何才能不阻塞? ## 坑 tcp client端编程,无论是循环,还是读取标准输入,都会阻塞client的事件监听,导致各种奇怪的问题。虽然问题解决了,但我还不肯定我明白了其中的道理。 ## 解决方法如下: ### tcp server 端 {{{php <?php /** * 演示swoole tcp server * User: dylan * Date: 2018/1/20 * Time: 14:28 */ // 客户端id=>上次心跳时间数组 $GLOBALS['heartMap']=[]; //创建Server对象,监听 127.0.0.1:9501端口 $serv = new swoole_server("127.0.0.1", 9501); $serv->set(array( 'open_eof_split' => true, //打开EOF_SPLIT检测 'open_eof_check' => true, //打开EOF检测 'package_eof' => "\r\n", //设置EOF )); //监听连接进入事件 $serv->on('connect', function ($serv, $fd) { echo "Client: Connect.\n"; $GLOBALS['heartMap'][$fd] = time(); }); //监听数据接收事件 $serv->on('receive', function ($serv, $fd, $from_id, $data) use(&$heartMap) { $data = trim($data); if($data == "ping"){ $GLOBALS['heartMap'][$fd] = time(); // 其实只要有数据就可以认为有心跳 $serv->send($fd,"pong\r\n"); }else{ $serv->send($fd, "Server: ".$data."\r\n"); } }); //监听连接关闭事件 $serv->on('close', function ($serv, $fd) { echo "Client: Close.\n"; }); $serv->on('workerStart', function (swoole_server $serv,$worker_id) use(&$heartMap) { echo "Server: start.\n"; // 心跳检测 // 全局变量 $heartCheck = $serv->tick(1000*5, function () use (&$serv,&$heartMap) { $now = time(); // var_dump($GLOBALS['heartMap']); if($GLOBALS['heartMap']){ foreach ($GLOBALS['heartMap'] as $id=>$time){ if(($now - $time) >10){ echo $id."因超时而断开链接"; $serv->close($id,true); unset($GLOBALS['heartMap'][$id]); } } } }); }); //启动服务器 $serv->start(); }}} ### tcp client 端 {{{php <?php /** * 演示 tcp client端循环发送标准输入给tcp server端 * User: dylan * Date: 2018/1/21 * Time: 16:50 */ $client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC); $client->set(array( 'open_eof_check' => true, 'open_eof_split' => true, 'package_eof' => "\r\n", )); //注册连接成功回调 $client->on("connect", function($cli) { $cli->send("hello world\r\n"); }); //注册数据接收回调 $client->on("receive", function($cli, $data) use(&$timeout_heater){ $data = trim($data); if($data == "pong"){ $timeout_heater = time(); }else{ echo "Received: ".$data."\n"; } }); // 注册连接失败回调 $client->on("error", function($cli){ echo "Connect failed\n"; }); // 注册连接关闭回调 // 连接关闭时,清理业务和心跳检测 $client->on("close", function($cli) use(&$tick_heater,&$info){ // Swoole\Timer::clear($info); Swoole\Timer::clear($tick_heater); echo "Connection close\n"; }); //发起连接 $client->connect('127.0.0.1', 9501, 0.5); // 心跳检测 $timeout_heater = time(); $tick_heater = Swoole\Timer::tick(1000 * 3, function () use ($client,&$timeout_heater,&$tick_heater) { $client->send("ping\r\n"); if((time() - $timeout_heater) >30){ echo "心跳检测超时"; $client->close(); Swoole\Timer::clear($tick_heater); } }); /** * 启动子进程 * @param $client */ function io(&$client){ $process = new Swoole\Process('asyncChildren',false); $pid = $process->start(); echo "进程{$pid}创建成功.\r\n"; // 用事件机制接收子进程消息 Swoole\Event::add($process->pipe, function($pipe) use ($process,$client) { $recv = trim($process->read()); if ($recv) { echo "From Worker: " . $recv; $client->send($recv."\r\n"); } }); // 回收子进程 swoole_process::signal(SIGCHLD, function($sig){ while($p = Swoole\Process::wait(false)){ echo "进程{$p['pid']}回收完成\r\n"; } }); } /** * 异步子进程构造函数 * * @param \Swoole\Process $worker */ function asyncChildren(Swoole\Process $worker){ $pid = $worker->pid; $worker->name(sprintf('php-ps:%d',$pid)); // 不知道干嘛用 swoole_process::signal(SIGTERM, function($signal_num) use ($worker) { echo "signal call = $signal_num, #{$worker->pid}\n"; }); // 发送消息给主进程测试 // Swoole\Timer::tick(1000, function () use ($worker) // { // if (rand(1, 3) % 2) { // $worker->write("hello master {$worker->pid}\n"); // } // }); // 标准输入消息测试 $io = true; while ($io) { $data = trim(fread(STDIN, 8192)); if ($data == ":q") { $io = false; break; } else { // echo $data; $worker->write("hello master: {$data}\n"); } } // 从主进程读取消息 Swoole\Event::add($worker->pipe, function($pipe) use($worker) { $recv = $worker->read(); echo "From Master: $recv\n"; }); } io($client); }}}
赞
0
分享
收藏
提问
分享
讨论
建议
公告
开发框架
CodeGalaxy
评论
2019-05-22
J
JunStar
`fread`是PHP提供的同步阻塞方法,请使用`swoole_event_add`将它变为异步事件监听。 实例 ---- ```php swoole_event_add(STDIN, function($fp) { echo "STDIN: ".fread($fp, 8192); }); ```
赞
0
回复
微信公众号
热门内容
暂无回复的问答
- CodeGalaxy K3s 轻量集群节点之间如何实现负载均衡
- 关于openssl CURL WARNING swSSL_connect: SSL_connect(fd=69) failed. Error: error:141A318A:SSL routines:tls_process_ske_dhe:dh key too small[1|394]
- 多个模型如何进行事务异常回退?
- websocket开启wss报错
- 协程tcp服务器如何使用多进程?recv()方法接收信息,打印出来的pid一直是同一个。没用使用到多进程啊。