在这里,客户端可以请求扩展和/或子协议。此外,也可以使用常见的请求头,比如 、 、 User-Agent Referer Cookie
或者身份验证请求头。这些请求头与 WebSocket 没有直接关联。
如果存在不合法的请求头,那么服务端应该发送 400 响应(“Bad Request”),并且立即关闭套接字。通常情况下,服
务端可以在 HTTP 响应体中提供握手失败的原因 。如果服务端不支持该版本的 WebSocket,那么它应该发送包含它支持的
版本的 头。在上面的示例中,它指示 WebSocket 协议的版本为 13。 Sec-WebSocket-Version
在请求头中,最值得关注的是 。接下来,将讲述它。 Sec-WebSocket-Key
2.2 服务端握手响应
当服务端收到握手请求时,将发送一个特殊响应,该响应表明协议将从 HTTP 变更为 WebSocket。该响应头大致如下(记
住,每个响应头行以 结尾,在最后一行的后面添加额外的 ,以说明响应头结束): \r\n \r\n
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
此外,服务端可以在这里对扩展/子协议请求做出选择。 响应头很重要,服务端必须通过客户端 Sec-WebSocket-Accept
发送的 请求头生成它。具体的方式是,将客户端的 与字符串 Sec-WebSocket-Key Sec-WebSocket-Key "258EAFA5-
(“魔法字符串”)连接在一起,然后对结果进行 SHA-1 哈希运算,最后返回哈希E914-47DA-95CA-C5AB0DC85B11"
值的 Base64 编码。
因此,如果 Key 为 ,那么 响应头的值是 "dGhlIHNhbXBsZSBub25jZQ==" Sec-WebSocket-Accept
。服务端发送这些响应头后,握手完成,可以开始交换数据。"s3pPLMBiTxaQ9kYGzzhZRbK+xOo="
下面的 Python 代码根据 请求头生成 响应头的值: Sec-WebSocket-Key Sec-WebSocket-Accept
typingimport
hashlib sha1from import
base64import
SEC_WS_MAGIC_STRING: bbytes = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
get_sec_ws_accept(sec_ws_key: typing.Union[ , ]) :def bytes str -> bytes
(sec_ws_key, ):if isinstance str
sec_ws_key sec_ws_key.encode()=
base64.b64encode(sha1(sec_ws_key SEC_WS_MAGIC_STRING).digest())return +
:if __name__ == "__main__"
get_sec_ws_accept(b ) bassert "dGhlIHNhbXBsZSBub25jZQ==" == "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="
3. 数据帧(Data Framing)
3.1 概览
在 WebSocket 协议中,使用一系列帧传输数据。为避免混淆网络中间人(比如拦截代理),以及出于安全考虑,客户端必
须对发送给服务端的所有帧进行掩码(Mask)处理。(注意,无论 WebSocket 协议是否运行在 TLS 上,都需要进行掩码
处理。)服务端在收到未进行掩码处理的帧时,必须关闭连接。在这种情况下,服务端可以发送状态码为 1002(协议错
误)的关闭帧。服务端不得对发送给客户端的任何帧进行掩码处理。如果客户端检测到掩码帧,那么必须关闭连接。在这种
情况下,可以使用状态码 1002(协议错误)。(在将来的规范中,可能放宽这些规则。)