WebSocket 基础:从 HTTP 到实时通信的跨越
说实话,我第一次接触 WebRTC 的时候,最困惑的就是信令服务器这块。你想想看,WebRTC 本身已经能处理音视频了,为什么还要搞个信令服务器?后来我才明白——WebRTC 只负责"怎么传数据",但"和谁传、传什么格式、什么时候开始传",这些都得靠信令来协调。而信令的载体,绝大多数情况下就是 WebSocket。
所以这一章,咱们先把 WebSocket 吃透。它是整个信令系统的"血管"。
WebSocket 协议原理:它到底解决了什么问题?
传统的 HTTP 通信,说白了就是"一问一答"。客户端发请求,服务器给响应。如果你想做实时推送,比如聊天消息、股价更新,HTTP 只能靠轮询——客户端每隔几秒问一次"有新消息吗?"
我早期做过一个在线客服系统,用的就是 HTTP 轮询。用户量一上来,服务器 CPU 直接飙到 90%。后来换成 WebSocket,同样的机器,CPU 降到 15%。差距就这么大。
WebSocket 的核心思想很简单:建立一条持久连接,双方随时可以发数据。它不像 HTTP 那样每次都要重新握手,而是通过一次握手升级协议,之后就是全双工通信。
关键点:WebSocket 的握手阶段用的是 HTTP 协议,但握手成功后,协议就切换为 WebSocket。这个过程叫"协议升级"(Upgrade)。
握手请求长这样:
GET /ws HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
服务器收到后,会计算一个响应密钥,返回:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
嗯,这里要注意:Sec-WebSocket-Key 不是用来做安全认证的,它只是用来确认服务器确实支持 WebSocket。真正的安全靠的是 TLS(wss://)。
WebSocket 与 HTTP 的区别:一张表说清楚
| 对比维度 | HTTP | WebSocket |
|---|---|---|
| 通信模式 | 请求-响应(半双工) | 全双工 |
| 连接持久性 | 短连接(默认) | 长连接 |
| 头部开销 | 每次请求都有完整头部 | 握手后头部极小(2-14字节) |
| 实时性 | 依赖轮询,延迟高 | 服务器可主动推送,延迟低 |
| 跨域支持 | 通过 CORS | 通过 Origin 头校验 |
| 适用场景 | REST API、页面加载 | 实时聊天、游戏、信令 |
我个人习惯把 WebSocket 理解为"打了鸡血的 TCP"。它保留了 TCP 的全双工特性,又解决了浏览器端无法直接操作 TCP 的问题。
浏览器端 WebSocket API:实战才是硬道理
浏览器端的 WebSocket API 其实非常简洁。我当年第一次用的时候,十分钟就写出了一个聊天 Demo。核心就四个事件:open、message、error、close。
先看一个最基础的例子:
// 创建连接
const socket = new WebSocket('wss://example.com/signal');
// 连接建立
socket.addEventListener('open', (event) => {
console.log('连接已建立');
// 发送一条消息
socket.send(JSON.stringify({
type: 'join',
room: 'room-123'
}));
});
// 接收消息
socket.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
console.log('收到消息:', data);
// 根据消息类型处理
if (data.type === 'offer') {
// 处理 SDP offer
} else if (data.type === 'ice-candidate') {
// 处理 ICE 候选
}
});
// 发生错误
socket.addEventListener('error', (event) => {
console.error('WebSocket 错误:', event);
});
// 连接关闭
socket.addEventListener('close', (event) => {
console.log('连接关闭, 状态码:', event.code);
// 可以在这里实现重连逻辑
});
避坑指南:我曾经在生产环境中犯过一个低级错误——忘记在 close 事件里做重连。结果服务器重启后,所有客户端都断开了,用户得手动刷新页面。后来我加了一个指数退避的重连策略,问题才解决。
这里有几个细节值得注意:
- 协议选择:生产环境一定要用
wss://,别用ws://。我在一个内部项目里用过 ws,结果被安全团队怼了回来。 - 数据格式:WebSocket 支持发送文本和二进制数据。信令场景下,我建议统一用 JSON 字符串,方便调试。
- 心跳机制:有些代理服务器(比如 Nginx)会断开空闲连接。我习惯每隔 30 秒发一个心跳包,保持连接活跃。
WebSocket 在信令中的角色
回到 WebRTC 信令的场景。WebSocket 在这里扮演的角色就是"消息管道"。它不关心消息内容是什么,只负责可靠地传递。
典型的信令流程是这样的:
- 客户端 A 通过 WebSocket 发送
offer(SDP)到信令服务器 - 信令服务器转发给客户端 B
- 客户端 B 回复
answer(SDP) - 信令服务器再转发给客户端 A
- 双方交换 ICE 候选地址
整个过程,WebSocket 就是那个"传话的"。它不需要理解 SDP 是什么,也不需要知道 ICE 是什么,只要保证消息不丢、不乱序就行。
注意:WebSocket 保证的是 TCP 层面的有序传输,但应用层如果有多条消息并发,还是需要自己处理消息 ID 或顺序号。我在一个多人会议项目中就踩过这个坑——两个 offer 同时到达,客户端处理顺序乱了,导致连接失败。
知识体系总览
下面这张图把 WebSocket 在信令中的位置和核心概念串起来了:
从这张图你能看到,WebSocket 就是连接浏览器和信令服务器的"桥梁"。它不负责业务逻辑,只负责把消息从 A 传到 B,再从 B 传回 A。简单,但至关重要。
小结
WebSocket 的核心价值就一句话:让浏览器拥有了真正的实时双向通信能力。对于 WebRTC 信令来说,它是目前最主流、最成熟的传输方案。
下一章我们会动手搭建一个真正的 WebSocket 信令服务器。到时候你会发现,理解了这些基础,写代码就是水到渠成的事。