Ratchet Pawl循环中无法向WebSocket发送消息问题排查求助
问题分析与解决方案
你的代码里send()始终不执行的核心原因,是在React EventLoop的回调中使用了阻塞性操作,彻底卡死了事件循环,导致WebSocket的异步Promise回调永远没机会运行。
为什么会这样?
ReactPHP的事件循环是单线程驱动的,所有异步逻辑(比如WebSocket连接、定时器、IO事件)都依赖循环能持续迭代、处理队列中的任务。但你在addPeriodicTimer的回调里写了:
sleep(1):这是同步阻塞函数,会直接让整个线程暂停1秒,期间事件循环完全停止工作。- 外层的
do-while循环:会一直轮询串口直到数据长度达标,这个过程也是同步阻塞的,事件循环根本没时间去处理WebSocket连接的后续回调。
哪怕你看到了INVIO的输出,后面的connector()->then(...)里的逻辑也因为事件循环被阻塞,永远得不到执行的机会。
修复方案:移除阻塞操作,用异步方式重构逻辑
我们需要把同步阻塞的串口轮询,改成基于EventLoop的异步逻辑,绝对不能让任何回调卡住事件循环。
修改后的代码示例
$loop = \React\EventLoop\Factory::create(); $reactConnector = new \React\Socket\Connector($loop, ['dns' => '8.8.8.8', 'timeout' => 10]); $connector = new \Ratchet\Client\Connector($loop, $reactConnector); // 定义递归的异步串口读取函数 $readSerialAsync = function () use ($loop, $connector) { $msg = $this->getSerial()->read(); echo "LETTO <$msg>\n"; // 如果数据长度不够,1秒后通过EventLoop定时器再次读取 if (strlen($msg) < 50) { $loop->addTimer(1, $readSerialAsync); return; } echo "INVIO $msg\n"; // 此时事件循环是空闲的,可以正常处理WebSocket的异步逻辑 $connector('ws://127.0.0.1:9988') ->then(function(Ratchet\Client\WebSocket $conn) use ($msg) { $conn->on('close', function($code = null, $reason = null) { echo "Connection closed ({$code} - {$reason})\n"; }); $conn->send($msg); $conn->close(); }, function(\Exception $e) { echo "Could not connect: {$e->getMessage()}\n"; }); }; // 每8秒启动一次串口读取流程 $loop->addPeriodicTimer(8, function () use ($readSerialAsync) { echo "LOOP\n"; $readSerialAsync(); }); $loop->run();
关键优化点说明
- 替换
sleep为EventLoop定时器:用$loop->addTimer(1, $readSerialAsync)代替sleep(1),这样等待过程中事件循环可以继续处理其他任务,不会阻塞。 - 递归轮询而非同步循环:把
do-while改成递归调用,每次读取后如果数据不达标,就通过定时器安排下一次读取,让事件循环有喘息的机会。 - 优先使用异步串口库:如果你的串口库支持异步读取(比如ReactPHP官方的
react/serialport扩展),更推荐直接监听串口的data事件,完全不需要轮询,效率会更高。
重要提醒
在ReactPHP的事件循环中,绝对禁止使用任何同步阻塞的函数或长时间运行的同步逻辑,比如sleep、usleep、同步IO读取、复杂的同步计算等,这些都会直接导致事件循环停滞,所有异步回调都无法执行。
内容的提问来源于stack exchange,提问作者Maxxer
相关产品推荐
相关产品推荐

