netty 的 websocket 消息处理应由 websocketserverprotocolhandler 自动完成,不可手动解帧或绕过;其必须位于 pipeline 前置位置,路径需严格匹配,textwebsocketframe.text() 即为可用字符串,业务加密应在协议层之后实现。

Netty 的 WebSocket 消息接收和传输本身已经高度优化,你不需要手动“解密帧”或重写帧解析逻辑——这么做反而会破坏协议正确性,引发 ERR_INCOMPLETE_CHUNKED_ENCODING、IllegalReferenceCountException 等典型错误。
WebSocketServerProtocolHandler 是协议处理的唯一入口
所有握手、掩码还原、UTF-8 解码、控制帧过滤都由 WebSocketServerProtocolHandler 自动完成。它必须出现在 pipeline 中自定义 handler 之前,且不可绕过。
- 错误做法:在 pipeline 中提前插入
HttpObjectAggregator+ 手动解析Upgrade头——会导致浏览器握手失败,连接直接关闭 - 正确顺序示例:
HttpServerCodec→HttpObjectAggregator→WebSocketServerProtocolHandler("/ws")→ 你的SimpleChannelInboundHandler<textwebsocketframe></textwebsocketframe> - 如果路径不匹配(比如前端连
/api/ws,但 handler 注册的是/ws),WebSocketServerProtocolHandler会静默丢弃请求,不报错也不响应——这是最常被忽略的连不上原因
TextWebSocketFrame.text() 就是最终可用字符串
当你继承 SimpleChannelInboundHandler<textwebsocketframe></textwebsocketframe>,frame.text() 返回的是已 UTF-8 解码、已去掩码、已校验的 Java 字符串。不要对 frame.content() 做任何操作。
- 常见误操作:
frame.content().toString(CharsetUtil.UTF_8)——content()是已被释放的引用计数ByteBuf,强制调用会抛IllegalReferenceCountException - 若需原始字节(比如转发给下游服务),应改用
BinaryWebSocketFrame,并在 handler 中处理frame.content().nioBuffer() - 文本帧最大长度默认为 64KB;如需支持更大消息,初始化
WebSocketServerProtocolHandler时传入maxFramePayloadLength参数(例如new WebSocketServerProtocolHandler("/ws", true, 10 * 1024 * 1024))
业务加密必须放在 WebSocketServerProtocolHandler 之后
所谓“WebSocket 加密”99% 是业务层需求(如前端 AES 加密 JSON),和协议无关。Netty 不提供、也不允许你在协议层做加解密。
- 务必确保
WebSocketServerProtocolHandler在 pipeline 最前处理完帧,再交由你自己的AesMessageDecoder或JsonMessageDecoder处理frame.text()结果 - 不要尝试覆盖或替换
WebSocketServerProtocolHandler的行为——它不暴露原始帧字节,也不开放解码钩子 - 如果用了 WSS(即
wss://),TLS 层加密由 JDK/Netty 的SslContext完成,与业务逻辑完全隔离;配置错误会导致连接在 TCP 握手后立即中断,日志中通常只显示SSLException: handshake timed out
真正影响性能的不是帧解析,而是 handler 内部是否阻塞、是否频繁创建对象、是否滥用同步集合。比如用 ConcurrentHashMap 存 channel 引用没问题,但每次广播都遍历全量 channel 并发 writeAndFlush,就容易在千级连接时出现写队列积压——这时候该考虑用 Netty 的 ChannelGroup + 批量 flush 策略,而不是去动协议层。











