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) 验证以下内容:
- HTTP方法: 必须是GET
- HTTP版本: 至少HTTP/1.1
- 必需头部: Host, Upgrade, Connection, Sec-WebSocket-Key, Sec-WebSocket-Version必须存在
- 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
这是握手的关键部分。服务器必须按以下步骤计算:
- 取客户端的
Sec-WebSocket-Key值 - 连接固定的GUID字符串:
258EAFA5-E914-47DA-95CA-C5AB0DC85B11 - 对连接后的字符串进行SHA-1哈希
- 对哈希结果进行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
安全考虑
- 验证Origin: 服务器应该验证
Origin头,防止跨站攻击 - 使用TLS: 在生产环境使用wss://
- 验证所有头部: 确保所有必需头部存在且格式正确
- 限制连接: 实施速率限制,防止DoS攻击
参考链接
- 上一章: 3. WebSocket URIs
- 下一章: 5. Data Framing
- 实现示例: 握手密钥计算