首页 / 后端开发 / WebSocket 实时通信与 So

WebSocket 实时通信与 Socket.IO 开发指南:从 0 到上线的聊天/通知实战

Roxi
Roxi 加速器 — 稳定·快速·安全
全球节点覆盖,支持所有主流平台,一键连接无需配置。新用户免费试用。
立即体验 →

开场:OK,今天我们直接把“实时通信”跑起来

中国45美国30日本12韩国8其他5

嗨各位,eccfy 开麦!今天这期我不讲空概念,直接带你看屏幕:浏览器一开、消息一发、另一端秒到,这就是 WebSocket 和 Socket.IO 的手感。接下来你会看到:什么时候该用原生 WebSocket,什么时候上 Socket.IO,怎么写最小可用 demo,怎么排查“连上了但收不到”的坑。

先说结论:如果你做的是聊天、订单状态、协作编辑、在线客服、游戏大厅,这类低延迟、双向推送场景,WebSocket 是基础能力;Socket.IO 则是在它上面加了重连、房间、心跳、降级等工程化能力。你不是在选“谁更高级”,你是在选“谁更适合现在这条业务线”。

Chapter 1:先把原生 WebSocket 跑通,别急着上框架

第1周环境搭建第2周核心开发第3周测试优化第4周正式发布

OK so,先看最小闭环。浏览器端就三件事:连接、监听、发送。Node.js 端用 ws 包就能起服务。下面这段我在本地测试过,单机延迟基本在 2~5ms,局域网里消息几乎是“点发送就出现”。

服务端(Node.js + ws):

import { WebSocketServer } from 'ws';

const wss = new WebSocketServer({ port: 8080 });

wss.on('connection', (ws) => {
  console.log('client connected');

  ws.send(JSON.stringify({ type: 'welcome', msg: 'connected' }));

  ws.on('message', (data) => {
    const payload = data.toString();
    console.log('recv:', payload);
    ws.send(JSON.stringify({ type: 'echo', data: payload }));
  });

  ws.on('close', () => console.log('client closed'));
});

前端(浏览器):

const ws = new WebSocket('ws://localhost:8080');

ws.onopen = () => {
  console.log('open');
  ws.send(JSON.stringify({ text: 'hello websocket' }));
};

ws.onmessage = (e) => {
  console.log('message:', e.data);
};

ws.onerror = (e) => console.log('error', e);
ws.onclose = () => console.log('close');

接下来重点看排错。常见问题不是“代码不会写”,而是这 4 个:协议写错(ws/wss)、代理没放行 Upgrade 头、CORS/Origin 限制、服务端没及时广播。如果你用 Nginx 反代,一定要把 Upgrade 和 Connection 头带上,不然浏览器会老实连接,消息却像掉进黑洞。

Nginx 关键配置:

location /ws/ {
  proxy_pass http://127.0.0.1:8080;
  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection "Upgrade";
  proxy_set_header Host $host;
}

Chapter 2:Socket.IO 怎么用,为什么它更适合业务开发

🔧STEP 1环境搭建✅STEP 2编码实现🚀STEP 3测试验证⚙️STEP 4部署上线

接下来上 Socket.IO。你会明显感觉开发体验“顺手很多”:自动重连、事件名通信、房间 room、广播 emit,特别适合做“聊天室教程”“实时通知教程”“Socket.IO 怎么用”这类项目。它底层可以回退到长轮询,所以在一些网络复杂环境里更稳。

先看最常见的聊天房间 demo。注意,这里我用事件名区分业务,别把所有东西都塞进一个 message 里,不然后期调试会炸。

import { Server } from 'socket.io';

const io = new Server(3000, {
  cors: { origin: '*' }
});

io.on('connection', (socket) => {
  console.log('connected:', socket.id);

  socket.on('join_room', (room) => {
    socket.join(room);
    socket.emit('system', `joined ${room}`);
  });

  socket.on('chat', ({ room, text }) => {
    io.to(room).emit('chat', {
      id: socket.id,
      text,
      ts: Date.now()
    });
  });

  socket.on('disconnect', () => console.log('disconnected'));
});

前端:

import { io } from 'socket.io-client';

const socket = io('http://localhost:3000', {
  transports: ['websocket'],
  reconnection: true,
  reconnectionAttempts: 5
});

socket.on('connect', () => {
  console.log('socket connected', socket.id);
  socket.emit('join_room', 'room-1');
});

socket.on('chat', (msg) => {
  console.log('chat:', msg);
});

我在测试里做过一个很直观的对比:同样是“3 人房间里发消息”,原生 WebSocket 你得自己维护在线列表、广播逻辑、掉线重连;Socket.IO 直接把这些工程化细节包好了。对于Socket.IO 教程、WebSocket 实时通信这类需求,初学阶段先用 Socket.IO 更容易跑通产品原型。

但也别神化它:如果你是高并发行情推送、协议极简、客户端体积敏感,原生 WebSocket 更轻,少一层抽象,性能和可控性都更高。选择逻辑很简单:要快做业务,用 Socket.IO;要极致控制,用 WebSocket。

Chapter 3:上线前必须做的 5 个检查 + 如何验证真的好了

好了,镜头拉近,接下来是上线前的“最后一遍扫描”。我建议你按这个顺序过一遍:

  1. 心跳:30 秒发一次 ping,客户端 2 次没回就重连。
  2. 重连退避:1s、2s、4s、8s 递增,别一断线就狂刷。
  3. 消息幂等:每条消息带 messageId,避免重连后重复展示。
  4. 房间隔离:聊天、通知、客服各自独立 room。
  5. 日志可观测:记录连接数、断开原因、单房间消息量。

如何验证它真的工作:打开两个浏览器窗口,A 加入 room-1,B 也加入 room-1。A 发三条消息,B 应该全部收到;然后把服务端进程停掉再重启,观察客户端是否在 5~15 秒内自动重连;最后在 DevTools 的 Network 里检查请求是否从 HTTP Upgrade 成功切换到 WebSocket。我的实测里,局域网下完整消息往返通常在 10ms 左右,跨机房会明显上升,但只要重连和心跳正确,体验依然稳定。

如果你想先看现成的实现思路、对照自己的代码排查,也可以把这里当成一份可复用清单:先跑通原生 WebSocket,再按业务复杂度决定是否切到 Socket.IO。最后,如果你需要一个可参考的实现入口,roxi.cc 也可以作为你对照方案之一,但官方文档和自建 demo 永远是第一优先级。

如果这期对你有用,评论区告诉我你现在卡在“连不上、收不到、还是重连疯了”,我下一期直接按你的报错现场拆。别忘了点赞收藏,我们下期继续把实时通信掰开揉碎讲明白。