java用nio实现websocket服务端需手动处理http升级握手、解析/生成rfc 6455帧、用selector管理连接;支持文本帧与关闭帧,需处理粘包、掩码、分片及write未完成情况,适合学习协议本质。

Java 用 NIO 实现一个简易的 WebSocket 服务端,核心在于:手动处理 HTTP 升级请求、解析/生成 WebSocket 帧(RFC 6455)、用 Selector 管理连接和 I/O。它不依赖 Tomcat 或 Netty,适合理解协议本质,但生产环境建议用成熟框架。
1. 理解 WebSocket 握手(HTTP Upgrade)
客户端首次发起的是标准 HTTP GET 请求,带特定头:
- Connection: Upgrade
- Upgrade: websocket
-
Sec-WebSocket-Key:Base64 编码的随机字符串(如
dGhlIHNhbXBsZSBub25jZQ==) - Sec-WebSocket-Version: 13(必须)
服务端需计算响应 key:base64(sha1(Sec-WebSocket-Key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"))
然后返回 101 Switching Protocols 响应,包含 Sec-WebSocket-Accept 头。
2. 用 NIO 搭建基础 TCP 服务器
创建 ServerSocketChannel,配置为非阻塞,注册到 Selector 监听 OP_ACCEPT:
ServerSocketChannel server = ServerSocketChannel.open(); server.configureBlocking(false); server.bind(new InetSocketAddress(port)); server.register(selector, SelectionKey.OP_ACCEPT);
在事件循环中处理 ACCEPT,为每个新连接创建 SocketChannel,设为非阻塞,并注册 OP_READ —— 注意:此时还是 HTTP 阶段,先读完整请求头(按 \r\n\r\n 分界)。
3. 手动解析帧与收发消息
握手成功后,连接进入 WebSocket 数据帧模式。帧结构含固定格式字段(FIN、RSV、OPCODE、MASK、Payload Length、Masking Key、Payload Data)。简易实现只需支持:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 文本帧(OPCODE=0x1):payload 是 UTF-8 字节
- 关闭帧(OPCODE=0x8):需响应关闭帧并断开
- 支持掩码(客户端发来的帧一定 MASK=1):用 Masking Key 异或解包
发送时需构造帧头(至少 2 字节),设置 FIN=1、OPCODE=0x1、MASK=0(服务端不掩码),写入 payload 长度字段(支持 126/127 扩展),再写 payload。
4. 管理连接状态与避免粘包
NIO 是流式读取,需自己缓存未读完的字节。建议为每个 SelectionKey 关联一个 ByteBuffer 作为接收缓冲区,并记录当前解析阶段:
- 阶段 0:等待 HTTP 请求头结束(查找 "\r\n\r\n")
- 阶段 1:已握手,等待 WebSocket 帧头(至少 2 字节)
- 阶段 2:已读帧头,知道 payload 长度,继续收齐 payload
收到完整文本帧后,UTF-8 解码内容;发送时先编码再组帧写入 channel。记得调用 channel.write() 后检查是否写完,未完需注册 OP_WRITE 并暂存 buffer。
不复杂但容易忽略:关闭连接前必须发 0x8 帧;所有帧操作要严格遵循 RFC 6455 字节序和掩码规则;NIO 的 write 可能只写部分数据,需轮询或使用 write buffer 队列。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










