websocket仍会粘包/半包,因其底层基于tcp字节流;需在帧内设计带长度头的自定义协议(如4字节body长度+2字节command),并通过workerman的protocolinterface实现分包逻辑。

Workerman 的 WebSocket 服务本身不处理粘包/半包,因为 WebSocket 帧仍运行在 TCP 流之上。即便用了 websocket:// 协议,只要业务层把多条消息拼在一个帧里发,或客户端批量推送未分帧,就可能在应用层出现粘包;而大消息被 TCP 拆分传输时,则会出现半包。真正要稳住数据边界,得在 WebSocket 帧内部再加一层自定义二进制协议,并由 Workerman 的 ProtocolInterface 驱动分包逻辑。
为什么 WebSocket 也会粘包?
很多人以为启用了 WebSocket 就自动按“消息”收发,其实:
- WebSocket 握手后只是升级为 TCP 长连接,底层仍是字节流
- Netty 或 Workerman 解出的
BinaryWebSocketFrame是按 WebSocket 规范(FIN、MASK、opcode)拆的帧,不是你业务意义上的“一条消息” - 如果你在客户端用
ws.send(new Uint8Array([...]))一次性塞入多个逻辑包,服务端收到的就是一整块二进制——这就是应用层粘包 - 同样,一个 2MB 的 protobuf 消息经 TCP 传输,可能被拆成十几个 TCP 段,Workerman 缓冲区里先到前 64KB,
onMessage就会触发一次不完整读取
设计带长度头的自定义协议
推荐结构:4 字节 body 长度(Big-Endian) + 2 字节 command 类型 + N 字节 payload。总头长 6 字节,body 长度字段只表示后续 payload 字节数(不含 header),这样解码清晰、跨语言兼容。
关键细节:
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- 长度字段必须用
pack('N', $len)(网络序 uint32),不能用strlen($str)直接取 PHP 字符串长度——UTF-8 多字节字符会导致错位 - 最大长度建议设上限(如
4 * 1024 * 1024),防止恶意构造pack('N', 0xffffffff)导致内存爆炸 - command 类型可用来区分 login/push/chat 等,便于路由分发
在 Workerman 中实现 ProtocolInterface
新建类 MyWsProtocol,实现三个静态方法:
-
input(string $buffer):检查缓冲区是否够一个完整包。若
strlen($buffer) ,返回 <code>0;否则 unpack 前 4 字节得$bodyLen,若strlen($buffer) ,返回 <code>0;否则返回6 + $bodyLen -
decode(string $buffer):已确认是完整包,跳过前 6 字节,返回
substr($buffer, 6);也可在此处校验 crc、反序列化 protobuf/json -
encode(array $data):先序列化 payload(如
msgpack_pack($data['body'])),再拼接pack('N', strlen($payload)) . pack('n', $data['cmd']) . $payload
然后绑定协议:
$ws_worker = new Worker('websocket://0.0.0.0:2346');$ws_worker->protocol = MyWsProtocol::class;
大消息分片与重装(可选增强)
单帧超 10MB 时,浏览器或 Workerman 可能因内存或配置限制拒绝接收。此时可在协议层支持分片:
- 在 header 中扩展 1 字节 flag:bit0 表示 is_first,bit1 表示 is_last,bit2–7 留作扩展
- 每片携带相同 message_id 和分片序号(uint16)
- 服务端用
$connection->id => ['msg_id' => [...]]在内存中暂存未完成的分片,超时未收齐则丢弃 - 不建议全量缓存,可用
apcu_store()或 Redis 做轻量状态管理
这种设计兼顾了可靠性与性能,比单纯依赖 WebSocket 自带的 fragmentation 更可控。









