WebSocket 协议原理
WebSocket 握手过程、通信机制及与 HTTP 的区别
问题
解释 WebSocket 协议的工作原理、握手过程,以及它与 HTTP 协议的区别。
解答
WebSocket 是什么
WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议。它允许服务器主动向客户端推送数据,解决了 HTTP 协议只能由客户端发起请求的限制。
握手过程
WebSocket 连接通过 HTTP 升级机制建立:
1. 客户端发起握手请求
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
2. 服务器响应握手
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Accept 的计算方式:
const crypto = require('crypto');
function generateAcceptKey(clientKey) {
// 固定的 GUID,由 RFC 6455 规定
const GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
return crypto
.createHash('sha1')
.update(clientKey + GUID)
.digest('base64');
}
// 示例
const clientKey = 'dGhlIHNhbXBsZSBub25jZQ==';
console.log(generateAcceptKey(clientKey)); // s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
客户端使用示例
// 创建 WebSocket 连接
const ws = new WebSocket('ws://example.com/socket');
// 连接建立
ws.onopen = function() {
console.log('连接已建立');
ws.send('Hello Server!');
};
// 接收消息
ws.onmessage = function(event) {
console.log('收到消息:', event.data);
};
// 连接关闭
ws.onclose = function(event) {
console.log('连接关闭:', event.code, event.reason);
};
// 错误处理
ws.onerror = function(error) {
console.error('WebSocket 错误:', error);
};
// 发送数据
ws.send('文本消息');
ws.send(new Blob(['二进制数据']));
ws.send(new ArrayBuffer(8));
// 关闭连接
ws.close(1000, '正常关闭');
服务端示例(Node.js)
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', function(ws) {
console.log('客户端已连接');
// 接收消息
ws.on('message', function(message) {
console.log('收到:', message.toString());
// 向客户端发送消息
ws.send('服务器收到: ' + message);
});
// 主动推送消息
setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send('服务器推送: ' + new Date().toISOString());
}
}, 5000);
ws.on('close', function() {
console.log('客户端断开');
});
});
WebSocket 与 HTTP 的区别
| 特性 | HTTP | WebSocket |
|---|---|---|
| 通信方式 | 单向(客户端发起) | 双向(全双工) |
| 连接状态 | 无状态,每次请求新建连接 | 持久连接 |
| 数据格式 | 文本 | 文本或二进制 |
| 头部开销 | 每次请求携带完整头部 | 握手后头部开销小(2-10 字节) |
| 实时性 | 需要轮询或长轮询 | 原生支持实时通信 |
| 协议标识 | http:// 或 https:// | ws:// 或 wss:// |
数据帧格式
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+-------------------------------+
| Extended payload length continued, if payload len == 127 |
+-------------------------------+-------------------------------+
| |Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------+-------------------------------+
| Payload Data continued ... |
+---------------------------------------------------------------+
常见 opcode 值:
0x0: 继续帧0x1: 文本帧0x2: 二进制帧0x8: 关闭连接0x9: Ping0xA: Pong
关键点
- 握手基于 HTTP:通过
Upgrade: websocket头将 HTTP 连接升级为 WebSocket - 全双工通信:连接建立后,客户端和服务器可以随时互相发送数据
- 持久连接:一次握手后保持连接,避免重复建立连接的开销
- 低延迟:数据帧头部仅 2-10 字节,适合实时应用
- 心跳机制:通过 Ping/Pong 帧保持连接活跃,检测连接状态
目录