跳到主要内容

4. Opening Handshake

WebSocket 协议的 opening handshake (开启握手) 被设计为与现有 HTTP 基础设施兼容. 该握手是一个 HTTP Upgrade 请求.

4.1 客户端要求

客户端发送的握手请求必须是如下格式的有效 HTTP 请求:

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Extensions: permessage-deflate

必需 Header 字段

Host

  • 包含服务器的主机和端口号
  • 符合 HTTP/1.1 规范

Upgrade: websocket

  • MUST 包含此 header
  • 值为 "websocket" (大小写不敏感)

Connection: Upgrade

  • MUST 包含此 header
  • 表明这是一个 Upgrade 请求

Sec-WebSocket-Key

  • MUST 包含此 header
  • 随机生成的 16 字节值, 经过 Base64 编码
  • 用于验证服务器理解 WebSocket 协议

Sec-WebSocket-Version: 13

  • MUST 包含此 header
  • 当前版本号为 13

可选 Header 字段

Origin

  • 浏览器客户端 MUST 包含
  • 非浏览器客户端 MAY 包含
  • 用于服务器端安全验证

Sec-WebSocket-Protocol

  • OPTIONAL
  • 指定客户端支持的 subprotocol (子协议) 列表
  • 多个值用逗号分隔

Sec-WebSocket-Extensions

  • OPTIONAL
  • 指定客户端支持的 extension (扩展)
  • 格式: extension-name [; parameter=value]*

4.2 服务器端要求

服务器必须验证客户端握手并返回适当响应.

4.2.1 读取客户端的 Opening Handshake

服务器 MUST 验证以下内容:

  1. HTTP method: 必须是 GET
  2. HTTP version: 至少为 HTTP/1.1
  3. Required headers: 必须存在 Host, Upgrade, Connection, Sec-WebSocket-Key, Sec-WebSocket-Version
  4. Sec-WebSocket-Version: 必须为 13 (或服务器支持的版本)

如果验证失败, 服务器 SHOULD 返回适当的 HTTP 错误状态码.

4.2.2 发送服务器的 Opening Handshake

如果验证成功, 服务器 MUST 发送握手响应:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat

计算 Sec-WebSocket-Accept

这是握手中的关键部分. 服务器必须按如下方式计算:

  1. 取客户端的 Sec-WebSocket-Key
  2. 与固定 GUID 字符串拼接: 258EAFA5-E914-47DA-95CA-C5AB0DC85B11
  3. 对拼接后的字符串执行 SHA-1 hash
  4. 对 hash 结果进行 Base64 编码

算法示例 (JavaScript):

const crypto = require('crypto');

function generateAcceptKey(clientKey) {
const GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
const concatenated = clientKey + GUID;
const hash = crypto.createHash('sha1').update(concatenated).digest();
return hash.toString('base64');
}

// Example
const clientKey = 'dGhlIHNhbXBsZSBub25jZQ==';
const acceptKey = generateAcceptKey(clientKey);
console.log(acceptKey); // s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

4.3 收集扩展和子协议

子协议协商

客户端可以在 Sec-WebSocket-Protocol header 中列出多个子协议:

Sec-WebSocket-Protocol: chat, superchat

服务器 MUST 从客户端列表中选择一个 (或不选择任何一个), 并在响应中返回它:

Sec-WebSocket-Protocol: chat

如果服务器不支持客户端请求的任何子协议, 可以省略 Sec-WebSocket-Protocol header.

扩展协商

扩展协商通过 Sec-WebSocket-Extensions header 进行:

客户端请求:

Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

服务器响应:

Sec-WebSocket-Extensions: permessage-deflate

服务器可以接受扩展, 修改参数, 或拒绝扩展.

4.4 支持多个版本

如果服务器不支持客户端请求的版本, 它 SHOULD 返回 HTTP 426 Upgrade Required, 并包含一个列出受支持版本的 Sec-WebSocket-Version header:

HTTP/1.1 426 Upgrade Required
Sec-WebSocket-Version: 13, 8, 7

完整握手示例

成功握手

客户端 -> 服务器:

GET /chat HTTP/1.1
Host: example.com:8000
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat
Sec-WebSocket-Version: 13
Origin: http://example.com

服务器 -> 客户端:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

失败握手示例

不支持的版本:

HTTP/1.1 426 Upgrade Required
Sec-WebSocket-Version: 13

Origin 未被接受:

HTTP/1.1 403 Forbidden

安全考虑

  1. 验证 Origin: 服务器应验证 Origin header, 以防止跨站攻击
  2. 使用 TLS: 在生产环境中使用 wss://
  3. 验证所有 Header: 确保所有必需 header 都存在且格式正确
  4. 限制连接: 实现速率限制, 以防止 DoS 攻击

参考链接