必须用 swoole_websocket_server 实现群发,因其内建连接管理、房间分组和广播能力;PHP-FPM 不支持 WebSocket,需独立启动服务;按房间 join/broadcast,客户端负责身份渲染,服务端只转发原始消息。

用 swoole_websocket_server 实现群发,别碰 PHP-FPM
PHP 原生不支持长连接,PHP-FPM 模式下根本跑不了 WebSocket 服务。想群发消息,必须换运行模型——swoole_websocket_server 是目前最稳的选择,它内建连接管理、房间(fd 分组)、广播能力。
常见错误是试图在 Laravel 或 ThinkPHP 的 HTTP 控制器里调用 WebSocket::broadcast() 这类伪方法,结果连握手都失败。WebSocket 服务必须独立启动,和 Web 请求进程完全隔离。
- 启动命令示例:
php server.php,不是php artisan serve - 所有客户端连接由
swoole_websocket_server接收并分配唯一fd,服务端靠这个标识通信 - 不要在
onMessage回调里做耗时操作(如查 MySQL),会阻塞整个事件循环
按房间分组:用 server->join() 和 server->broadcast()
“房间”不是自动存在的,得手动维护。Swoole 提供 join()/leave() 把 fd 加入/移出一个逻辑房间名(比如 "room_1001"),之后对房间广播就只推给当前在线成员。
注意:房间名是字符串,不能含特殊字符或空格;同一个 fd 可加入多个房间,但重复 join() 不报错也不重复添加。
- 客户端上线时,先发个自定义协议消息(如
{"action":"join","room":"room_1001"}),服务端解析后执行$server->join($fd, "room_1001") - 群发用
$server->broadcast("room_1001", $data),$data必须是字符串,通常json_encode()后传入 - 客户端断开时,Swoole 自动清理该
fd所有房间关系,不用手动leave()
server->push() 和 server->broadcast() 的实际差异
push() 是点对点,broadcast() 是房间级群发,但底层都是遍历 fd 列表再调 push()。性能差异几乎为零,关键区别在语义和维护成本。
有人图省事,把所有在线 fd 存进数组,每次群发手动遍历 push() —— 这样会漏掉刚加入还没来得及存进数组的用户,也容易因未及时清理离线 fd 导致 push() 失败报错。
-
server->broadcast("room_x", $msg)自动跳过已断连的fd,不抛异常 -
server->push($fd, $msg)对无效fd会返回false,需手动判断 - 跨服务器部署时,
broadcast()仅限单机;要集群广播,得引入 Redis Pub/Sub 或消息队列中转
广播内容里带用户身份?别在服务端拼接敏感字段
常见需求是“向房间广播一条‘张三发了消息’”,但千万别在服务端从数据库查出“张三”再拼 JSON 发出去。WebSocket 连接生命周期长,用户 token 或 session 可能已过期,且查库行为会拖慢广播速度。
正确做法是:客户端自己负责身份标识。服务端只转发原始消息体,附带发送方 fd 或前端传来的 user_id 字段,渲染逻辑全交给前端。
- 客户端发消息时带上
{"user_id":123,"content":"hi"} - 服务端不做校验、不查用户名,原样
broadcast()给房间其他成员 - 前端收到后,用本地缓存的昵称映射表(或另起轻量请求)补全显示,避免广播延迟和数据不一致
房间广播的复杂点不在代码行数,而在于谁负责状态同步、谁承担容错成本。服务端越薄,越不容易出问题。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











