WebSocket 基础:协议原理、与 HTTP 的区别、Node.js 搭建简易服务
好,咱们进入第二个章节。WebSocket 这东西,说实话,现在做多屏联动几乎绕不开它。我记得我刚接触实时通信那会儿,还在用轮询,那体验,啧啧……后来换了 WebSocket,才感觉打开了新世界的大门。
WebSocket 协议原理
WebSocket 是什么?说白了,它就是一个在单个 TCP 连接上实现全双工通信的协议。你想想看,传统的 HTTP 请求,永远是客户端先说话,服务器才能回。但 WebSocket 不一样,服务器可以随时主动给客户端发消息。
它的工作流程其实很简单:
- 握手阶段:客户端发一个特殊的 HTTP 请求,要求升级协议。
- 协议升级:服务器同意后,状态码返回 101,连接就从 HTTP 切换到了 WebSocket。
- 数据传输:之后双方就可以自由地互发数据了,不再有请求-响应的束缚。
我在项目中遇到过一个问题:有些老旧的代理服务器不认识 WebSocket 的 Upgrade 头,会直接拦截掉握手请求。嗯,这里要注意,生产环境一定要确认网络设备是否支持。
核心要点:WebSocket 的握手是 HTTP 的,但握手之后的数据传输是独立的帧协议。它用的是 ws:// 或 wss://(加密版)协议头。
与 HTTP 的区别
很多人问我,WebSocket 和 HTTP 到底有啥不一样?我习惯用一个比喻来解释:
- HTTP 像打电话:你拨号(请求),对方接听(响应),说完就挂断。每次通话都要重新拨号。
- WebSocket 像对讲机:按下开关就能说话,对方随时能回,而且一直在线。
具体区别,我整理了一张表:
| 对比项 | HTTP | WebSocket |
|---|---|---|
| 通信模式 | 半双工(请求-响应) | 全双工(双向实时) |
| 连接状态 | 无状态,每次请求独立 | 有状态,保持长连接 |
| 开销 | 每次请求携带完整头部 | 握手后头部极小(2字节) |
| 实时性 | 依赖轮询或长轮询 | 真正的服务器推送 |
| 适用场景 | 文档、API 请求 | 聊天、游戏、实时数据 |
你想想看,如果做多屏联动,用 HTTP 轮询,每秒钟发几十个请求,服务器压力大不说,延迟还高。WebSocket 一条连接搞定所有,这才是正道。
个人经验:我建议在需要实时性的场景优先考虑 WebSocket,但如果只是偶尔获取数据,HTTP 轮询反而更简单。别为了用新技术而用新技术。
Node.js 搭建简易 WebSocket 服务
理论说完了,咱们动手。我用 Node.js 搭一个最简单的 WebSocket 服务,用 ws 这个库,它是最轻量的选择。
先安装依赖:
npm init -y
npm install ws
然后写服务端代码,我习惯叫它 server.js:
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', function connection(ws) {
console.log('新客户端连接上了');
// 收到消息就广播给所有人
ws.on('message', function incoming(message) {
console.log('收到: %s', message);
// 广播给所有连接的客户端
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send('服务器说: ' + message);
}
});
});
// 发送欢迎消息
ws.send('欢迎连接到 WebSocket 服务');
});
客户端代码更简单,浏览器原生就支持 WebSocket:
const ws = new WebSocket('ws://localhost:8080');
ws.onopen = function() {
console.log('连接已建立');
ws.send('你好,服务器!');
};
ws.onmessage = function(event) {
console.log('收到消息:', event.data);
};
ws.onclose = function() {
console.log('连接已关闭');
};
避坑指南:我曾经在生产环境遇到过一个问题——WebSocket 连接在长时间无数据交互后会被自动断开。这是因为中间的网络设备(如负载均衡器)有 idle timeout 设置。解决方案是加心跳机制,每隔一段时间发一个 ping/pong 帧保持连接活跃。
运行起来很简单:
node server.js
然后在浏览器控制台粘贴客户端代码,你就能看到双向通信了。嗯,这里要注意,如果用的是 HTTPS 页面,WebSocket 必须用 wss:// 而不是 ws://,否则浏览器会报安全错误。
心跳与重连机制
刚才提到了心跳,我顺便把这块补上。一个健壮的 WebSocket 应用,必须处理断线重连。我一般这么写:
// 服务端心跳
const interval = setInterval(function ping() {
wss.clients.forEach(function each(ws) {
if (ws.isAlive === false) return ws.terminate();
ws.isAlive = false;
ws.ping();
});
}, 30000);
wss.on('connection', function(ws) {
ws.isAlive = true;
ws.on('pong', function() {
ws.isAlive = true;
});
});
// 客户端自动重连
function connect() {
const ws = new WebSocket('ws://localhost:8080');
ws.onclose = function() {
console.log('连接断开,5秒后重连...');
setTimeout(connect, 5000);
};
}
connect();
这个模式我在多个项目里用过,稳定得很。说白了,WebSocket 本身不复杂,复杂的是怎么让它稳定运行。
总结一下:WebSocket 的核心就三点——握手升级、全双工通信、长连接。Node.js 搭服务用 ws 库,几分钟就能跑起来。但真正用到生产,心跳、重连、安全(wss)这些一个都不能少。
下一章咱们会深入 WebSocket 的帧协议和数据格式,到时候再聊更底层的东西。今天就先到这里,动手试试吧。