Skip to main content

4. Opening Handshake (开放握手)

WebSocket协议的开放握手旨在与现有HTTP基础设施兼容。该握手是一个HTTP Upgrade请求。

4.1 Client Requirements (客户端要求)

客户端发送的握手请求必须是有效的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

必需的头部字段

Host

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

Upgrade: websocket

  • 必须 (MUST) 包含此头部
  • 值为"websocket"(不区分大小写)

Connection: Upgrade

  • 必须 (MUST) 包含此头部
  • 指示这是一个Upgrade请求

Sec-WebSocket-Key

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

Sec-WebSocket-Version: 13

  • 必须 (MUST) 包含此头部
  • 当前版本号为13

可选的头部字段

Origin

  • 浏览器客户端必须 (MUST) 包含
  • 非浏览器客户端可以 (MAY) 包含
  • 用于服务器端的安全验证

Sec-WebSocket-Protocol

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

Sec-WebSocket-Extensions

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

4.2 Server-Side Requirements (服务器端要求)

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

4.2.1 Reading the Client's Opening Handshake (读取客户端握手)

服务器必须 (MUST) 验证以下内容:

  1. HTTP方法: 必须是GET
  2. HTTP版本: 至少HTTP/1.1
  3. 必需头部: Host, Upgrade, Connection, Sec-WebSocket-Key, Sec-WebSocket-Version必须存在
  4. Sec-WebSocket-Version: 必须是13(或服务器支持的版本)

如果验证失败,服务器应该 (SHOULD) 返回适当的HTTP错误状态码。

4.2.2 Sending the Server's 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哈希
  4. 对哈希结果进行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');
}

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

4.3 Collecting Extensions and Subprotocols (收集扩展和子协议)

子协议协商

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

Sec-WebSocket-Protocol: chat, superchat

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

Sec-WebSocket-Protocol: chat

如果服务器不支持任何客户端请求的子协议,可以不包含Sec-WebSocket-Protocol头。

扩展协商

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

客户端请求:

Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

服务器响应:

Sec-WebSocket-Extensions: permessage-deflate

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

4.4 Supporting Multiple Versions (支持多版本)

如果服务器不支持客户端请求的版本,应该 (SHOULD) 返回HTTP 426 Upgrade Required,并包含Sec-WebSocket-Version头列出支持的版本:

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头,防止跨站攻击
  2. 使用TLS: 在生产环境使用wss://
  3. 验证所有头部: 确保所有必需头部存在且格式正确
  4. 限制连接: 实施速率限制,防止DoS攻击

参考链接