
WebSocket 协议中,当消息长度 ≥126 时,第二字节的低 7 位(b & 127)并非真实长度,而是编码标识符:126 表示后续 2 字节为长度,127 表示后续 8 字节为长度;否则长度即为此值本身。
websocket 协议中,当消息长度 ≥126 时,第二字节的低 7 位(`b & 127`)并非真实长度,而是编码标识符:126 表示后续 2 字节为长度,127 表示后续 8 字节为长度;否则长度即为此值本身。
在实现 WebSocket 服务端帧解析时,一个常见误区是将第二字节的 b & 127 直接当作 payload 长度使用——这仅对 ≤125 字节的消息成立。Chrome 浏览器严格遵循 RFC 6455 §5.2,当发送 127+ 字符时,它会将第二字节设为 0b11111110(即十进制 254),此时 b & 127 == 126,表示“长度字段占用接下来的 2 个字节”,而非“长度为 126”。同理,若该值为 127,则长度由后续 8 字节(uint64)给出。
你原代码中的关键问题在于:
- 未根据
length的值(126 或 127)动态读取真正的长度字段; - 未更新
length变量,导致后续解码始终按错误长度截取数据; - 掩码偏移计算(
key起始位置与d起始位置)未随长度编码方式变化而调整。
以下是修正后的 readWsFrame 核心逻辑(含完整边界检查与大小端处理):
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
func readWsFrame(p []byte) {
if len(p) < 2 {
fmt.Println("frame too short: missing header")
return
}
// 解析 FIN 和 opcode
b0 := p[0]
fin := b0&0x80 != 0
op := b0 & 0x0F
// 解析第二字节:mask bit + length encoding
b1 := p[1]
masked := b1&0x80 != 0
encLen := int(b1 & 0x7F) // 编码长度标识(0–125, 126, 127)
var payloadLen uint64
var maskKey []byte
var payloadStart int
switch encLen {
case 126:
if len(p) < 4 {
fmt.Println("frame too short: need 2-byte length")
return
}
payloadLen = uint64(binary.BigEndian.Uint16(p[2:4]))
maskKey = p[4:8]
payloadStart = 8
case 127:
if len(p) < 10 {
fmt.Println("frame too short: need 8-byte length")
return
}
payloadLen = binary.BigEndian.Uint64(p[2:10])
maskKey = p[10:14]
payloadStart = 14
default:
payloadLen = uint64(encLen)
maskKey = p[2:6]
payloadStart = 6
}
// 检查总长度是否足够(含掩码和 payload)
expectedMinLen := payloadStart + int(payloadLen)
if len(p) < expectedMinLen {
fmt.Printf("frame too short: expected %d bytes, got %d\n", expectedMinLen, len(p))
return
}
// 提取并解码 payload
maskedData := p[payloadStart : payloadStart+int(payloadLen)]
var decoded []byte
for i := range maskedData {
decoded = append(decoded, maskedData[i]^maskKey[i%4])
}
payload := string(decoded)
fmt.Printf("FIN: %t | OP: %d | Length: %d | Payload: %q\n", fin, op, payloadLen, payload)
}关键注意事项:
- ✅ 长度字段是网络字节序(Big Endian):使用
binary.BigEndian.Uint16/Uint64解析; - ✅ 必须校验缓冲区长度:避免 panic,尤其在
encLen == 126/127时需确保后续字节存在; - ✅ 掩码密钥始终为 4 字节,但其起始位置取决于长度编码方式(6/8/14 字节后);
- ❌ 不要假设
p[6:]总是 payload —— 它只适用于encLen ; - ⚠️ 真实 WebSocket 实现还需处理分片(FIN=0)、控制帧(opcode=8/9/10)、错误校验等,本例聚焦长度解析核心逻辑。
通过此修正,当你在 Chrome 控制台执行 ws.send("a".repeat(400)) 时,服务端将正确解析出 payloadLen == 400,并输出 "aaaa..."(400 个 a)。理解这 7 位的编码语义(而非直接长度)是 WebSocket 手写解析器的基石。

















