onMessage 回调仅在 SwooleWebSocketServer 环境下生效,专门处理已解析完成的 WebSocket 帧;onReceive 则面向 TCP/UDP 原始字节流,开发者需自行处理粘包与半包问题;onPacket 专为 UDP 场景设计,语义表达更加明确清晰。

需要牢记:onMessage 回调专用于 WebSocket 连接的消息处理,而 onReceive 回调则是底层 TCP/UDP 接收原始数据的入口。两者处于完全不同的抽象层级,既不能混用,也无法相互替代。
onMessage 只在 WebSocket Server 中有效
当你创建一个 SwooleWebSocketServer 实例时,onMessage 成为处理客户端消息的唯一入口。它仅接收已完成解析的 WebSocket 帧(包括 TEXT 与 BINARY 类型),握手验证、掩码解码、分片重组等底层复杂操作均由框架自动完成。若在 TCP 或 HTTP Server 上注册 onMessage 回调,它将永远不会被触发。
- 常见错误现象:
onMessage回调函数从未执行过,反倒是onReceive频繁触发——这通常是因为你使用的是SwooleServer或SwooleHttpServer,而非 WebSocket 类型 - WebSocket 连接建立之后,所有后续帧都会经由
onMessage处理,包括心跳 ping/pong(如果未单独设置onPing/onPong) - 参数
$frame是一个SwooleWebSocketFrame对象,包含data、opcode、finish等字段,而非裸字节流
onReceive 是 TCP/UDP 的原始数据回调
onReceive 回调存在于 SwooleServer(TCP)和 SwooleUDPServer 中,接收的是未经任何协议解析的原始字节流数据。对于 TCP 协议,它不保证每次回调对应一个完整的业务数据包,粘包与半包现象需要开发者自行处理;而对于 UDP 协议,每次回调则对应一个完整的 UDP 数据报(最大支持 64KB)。
- 一个常见的误用场景:在 WebSocket Server 中监听
onReceive—— 实际上它根本不会触发,因为 WebSocket Server 已接管并封装为onMessage - 如果你需要自定义 TCP 协议(例如私有二进制协议),就必须使用
onReceive结合set配置open_length_check或package_max_length等参数进行包解析 - UDP 场景下,
onReceive的$from_id是reactor_id,没有$fd概念(无连接),因此不能调用send(),需改用sendto()
onPacket 专用于 UDP 数据包解析
当使用 SwooleServer 启动 UDP 类型监听(SWOOLE_SOCK_UDP)时,onPacket 是更为推荐的选择——它在语义层面明确表示“这是一个完整的 UDP 数据报”,比 onReceive 更具表达力。虽然在 UDP 场景下功能与 onReceive 一致,但语义更清晰、代码可读性更高,且 Swoole 内部已为其做了针对性优化适配。
- 如果未设置
onPacket,UDP 收包默认仍会进入onReceive(兼容旧有写法) - 不要在 TCP Server 中注册
onPacket—— 它不会触发,也没有实际意义 - 参数签名不同:
onPacket的回调函数接收($server, $data, $client_info),其中$client_info包含address和port;而onReceive在 UDP 模式下$fd为 -1,$from_id为 reactor ID
容易忽略的底层细节
真正决定回调行为的关键并非函数名称,而是 Server 实例类型以及调用 listen() 时指定的 $socket_type 参数。Swoole 不会校验在 HTTP Server 上注册 onMessage 是否合理,而是直接静默忽略该回调设置。
- 检查当前 Server 类型:
var_dump(get_class($server))—— 必须是SwooleWebSocketServer才能使用onMessage - UDP 场景下,
sendto()的$server_socket参数如果为空,会使用第一个 UDP 监听端口;若存在多个端口,必须显式传入对应的 socket 名称 onMessage抛出异常会导致连接被强制关闭,而onReceive异常只会中断当前包的处理,连接会保持活跃
