必须基于workerman构建websocket服务端,通过唯一uid管理连接、room_id隔离房间、心跳保活防断连,并用$connections和$rooms数组实现消息精准路由与状态同步。

要在Web端实现用户之间实时收发消息、按房间隔离、支持在线状态感知的多人聊天室,必须基于Workerman构建WebSocket服务端,并配合前端建立持久连接与消息路由逻辑。直接复用HTTP请求无法维持长连接,也无法做到毫秒级广播。
搭建基础WebSocket服务端
创建start.php文件,引入Workerman Autoloader并定义Worker实例:
使用websocket://0.0.0.0:2000监听地址,确保端口未被占用且防火墙放行;【必须设置$worker->count = 1,多进程模式下session和room数据不同步会导致消息丢失】。
在onConnect回调中为每个新连接生成唯一$connection->uid(非用户ID),并存入全局数组$connections;该uid是后续广播、私聊、踢人操作的唯一通信凭证。
启动服务:php start.php start -d,加-d以后台守护模式运行,避免终端关闭导致服务中断。
实现房间隔离与消息分发
客户端首次连接后,需主动发送JSON格式登录请求,例如:{"type":"login","room_id":"room_1","user_name":"张三"}。
服务端在onMessage中解析该消息,提取room_id并绑定到当前连接:$connection->room_id = $data['room_id'];,同时将该连接加入对应房间的用户列表(如$rooms[$room_id][] = $connection;)。
当收到群聊消息时,遍历$rooms[$connection->room_id]中所有连接对象,调用$conn->send($message)广播——【切勿使用Worker::$worker->connections全量广播,否则跨房间用户会收到无关消息】。
用户断开连接时,在onClose中从对应$rooms[$connection->room_id]数组中unset该连接,防止内存泄漏和向已断开连接重复发包。
支持私聊与用户身份识别
方法一:客户端发送带to_uid字段的消息,如{"type":"private","to_uid":"1002","content":"你好"}。
服务端查找全局$connections数组中uid === $data['to_uid']的连接对象,直接$target_conn->send();若目标不在线,可将消息写入Redis队列或MySQL离线表。
方法二:结合GatewayWorker框架,调用Gateway::sendToUid($data['to_uid'], $message),无需手动维护连接映射——但需提前通过Gateway::bindUid($client_id, $user_id)完成绑定,且$user_id应为业务系统真实用户ID。
注意:私聊消息体中必须携带发送方from_user_name或from_uid,否则接收方无法识别是谁发来的。
处理连接异常与心跳保活
第一步:在onConnect中为连接对象初始化时间戳:$connection->last_message_time = time();。
第二步:使用Worker::onWorkerStart注册定时器,每30秒遍历所有连接,检查time() - $conn->last_message_time > 60,超时则$conn->close()并清理其所在房间列表。
第三步:前端JavaScript每隔45秒主动发送空字符串或{"type":"ping"}作为心跳包,服务端收到后刷新last_message_time;【不设心跳会导致NAT超时断连,用户无感知掉线】。
第四步:在onMessage开头统一更新时间戳:$connection->last_message_time = time();,避免误判活跃连接。











