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。核心就四个事件:openmessageerrorclose

先看一个最基础的例子:

// 创建连接
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 在这里扮演的角色就是"消息管道"。它不关心消息内容是什么,只负责可靠地传递。

典型的信令流程是这样的:

  1. 客户端 A 通过 WebSocket 发送 offer(SDP)到信令服务器
  2. 信令服务器转发给客户端 B
  3. 客户端 B 回复 answer(SDP)
  4. 信令服务器再转发给客户端 A
  5. 双方交换 ICE 候选地址

整个过程,WebSocket 就是那个"传话的"。它不需要理解 SDP 是什么,也不需要知道 ICE 是什么,只要保证消息不丢、不乱序就行。

注意:WebSocket 保证的是 TCP 层面的有序传输,但应用层如果有多条消息并发,还是需要自己处理消息 ID 或顺序号。我在一个多人会议项目中就踩过这个坑——两个 offer 同时到达,客户端处理顺序乱了,导致连接失败。

知识体系总览

下面这张图把 WebSocket 在信令中的位置和核心概念串起来了:

WebSocket 在信令中的知识体系 浏览器 A WebSocket 客户端 发送 offer/answer/ICE 浏览器 B WebSocket 客户端 接收/回复信令消息 信令服务器 WebSocket 服务端 消息路由 + 房间管理 wss:// 转发消息 全双工通信 双向实时传输 协议升级 HTTP → WebSocket 心跳保活 防止连接断开

从这张图你能看到,WebSocket 就是连接浏览器和信令服务器的"桥梁"。它不负责业务逻辑,只负责把消息从 A 传到 B,再从 B 传回 A。简单,但至关重要。

小结

WebSocket 的核心价值就一句话:让浏览器拥有了真正的实时双向通信能力。对于 WebRTC 信令来说,它是目前最主流、最成熟的传输方案。

下一章我们会动手搭建一个真正的 WebSocket 信令服务器。到时候你会发现,理解了这些基础,写代码就是水到渠成的事。


公众号:蓝海资料掘金营,微信deep3321