求助WebSocket opening handshake timed out

yangtoude
  • 326

1,最近在做一个实时监控项目,用到了html5的websocket。场景如下:
点击设备列表,进入某个设备详情页面,监控这个设备的运行状况,实时更新一些设备运行状态参数。

2,前端代码如下:

// 省略...
const socket = new WebSocket(server);

socket.addEventListener('open', function(e) {
    socket.send(该设备id);
});

socket.addEventListener('message', function(e) {
    const data = e.data;

    更新设备信息(data);
});

socket.addEventListener('close', function(e) {
    console.log('websockt连接已关闭');
});

socket.addEventListener('error', function(e) {
    alert("websockt连接发生错误,请刷新页面重试!");
});

// 省略...

3,后端我用的php的wokerman(一个php sokcet服务框架)提供的一个websocket服务用来测试(我目前做前端开发,用这个workerman只是用来测试)。

// 心跳间隔25秒
define('HEARTBEAT_TIME', 25);

$ws_worker = new Worker("websocket://0.0.0.0:9668");

// 8 processes
$ws_worker->count = 8;

// 进程启动后设置一个每秒运行一次的定时器
$ws_worker->onWorkerStart = function($worker) {
    Timer::add(1, function()use($worker){
        $time_now = time();
        foreach($worker->connections as $connection) {
            // 有可能该connection还没收到过消息,则lastMessageTime设置为当前时间
            if (empty($connection->lastMessageTime)) {
                $connection->lastMessageTime = $time_now;
                continue;
            }
            // 上次通讯时间间隔大于心跳间隔,则认为客户端已经下线,关闭连接
            if ($time_now - $connection->lastMessageTime > HEARTBEAT_TIME) {
                $connection->close();
            }
        }
    });
};

// Emitted when data received
$ws_worker->onMessage = function ($connection, $data) {

    // 给connection临时设置一个lastMessageTime属性,用来记录上次收到消息的时间
    $connection->lastMessageTime = time();

    while (true) {
        $connection->send(根据接收到的客户端的$data发送要发送的消息);
        usleep(1000000); // 睡1秒
    }
};

4,现象:
我发现如果刷新设备详情页面8次以上(这个次数正好和后端代码中设置的wokerman的进程数相等),客户端就会连接不上后端的socket服务,一直pending,直到提示:WebSocket opening handshake timed out。而wokerman的运行状态则为:

clipboard.png
5,问题:
我想问下我的代码哪里出了问题,是前端js代码或业务逻辑有问题还是后端php代码或业务有问题。

回复
阅读 11.8k
1 个回答
yangtoude
  • 326
✓ 已被采纳

问题解决了,workerman论坛上的一个哥们提醒了我是死循环导致进程一直处于busy状态。把php代码中的死循环去掉:

// while (true) {
    $connection->send(根据接收到的客户端的$data发送要发送的消息);
    // usleep(1000000); // 睡1秒  其实这个也可以去掉
// }

前端代码中增加定时向后端发送消息的代码,这样后端就可以根据监听到的前端发送的消息往前端推送消息。worker进程只有在发送消息时才会处于busy状态,否则就会idle。之前就是因为死循环一直发送消息(busy),这样就导致前端页面每次加载都会导致后端创建一个新的进程(原来的worker进程一直没释放)。我之前对后端往前端推送消息的业务逻辑和技术细节没想清楚,哎,自己给自己挖了个坑。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏