WebSocket 消息帧格式是怎样的?各字段含义详解
WebSocket消息帧是通信的基本单位,由固定格式的二进制结构组成。一帧包含以下字段:FIN(1位)标识是否为最后一个分片;RSV1-3(各1位)保留给扩展使用;Opcode(4位)标识帧类型,0x0为继续帧、0x1文本帧、0x2二进制帧、0x8关闭帧、0x9 Ping、0xA Pong;MASK(1位)标识是否使用掩码;Payload Length(7位或扩展)表示载荷长度;Masking-key(0或4字节)为掩码密钥;Payload Data为实际数据。
客户端发送的帧MASK必须为1,服务器发送的帧MASK必须为0。Payload Length采用变长编码:0-125直接用7位表示;126表示后续2字节为真实长度;127表示后续8字节为真实长度。掩码算法将Payload Data的每字节与Masking-key循环异或(XOR),服务器收到后用同样方式还原。大消息可通过分片传输,首帧FIN=0、Opcode为实际类型,中间帧FIN=0、Opcode=0x0,末帧FIN=1、Opcode=0x0。关闭帧(Opcode=0x8)可携带状态码和原因,用于正常或异常断开连接。
追问
掩码算法具体怎么计算?
客户端发送时生成4字节随机Masking-key,对Payload Data逐字节异或:
jsfor (let i = 0; i < payload.length; i++) { decoded[i] = payload[i] ^ maskingKey[i % 4]; }
编码和解码是同一操作,异或两次还原原文。设计目的是防止缓存污染攻击,不让中间代理误判载荷内容。
分片消息如何重组?
接收方维护一个缓冲区。收到FIN=0的帧时,将Payload追加到缓冲区,Opcode只在首帧有效。后续帧Opcode为0x0,表示"接续上一帧"。直到收到FIN=1的帧,拼接完成,最终消息类型取首帧的Opcode。注意控制帧(Ping/Pong/Close)不允许分片,可在数据帧分片中间插入。
为什么要区分三种Payload Length编码?
用7位最多只能表示125,不够用。加2字节可到65535,覆盖绝大多数场景。超过64KB才动用8字节,最大支持2^63-1。这种变长设计兼顾了小帧的紧凑性和大帧的扩展性——小帧不浪费头部空间,大帧也不受限制。
关闭帧里能带什么内容?
Payload前2字节是状态码(如1000正常关闭、1001端点离开、1002协议错误),剩余部分是UTF-8编码的关闭原因字符串,最长123字节(受125字节载荷上限减去2字节状态码限制)。双方都可以发起关闭帧,收到关闭帧的一方应回复一个关闭帧,然后双方关闭TCP连接。