首页 / 后端开发 / Socket.IO聊天室实战:从0搭

Socket.IO聊天室实战:从0搭建WebSocket实时通信、压测延迟到断线重连

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

Chapter 1:OK so,先把实时通信跑起来

品牌定位清晰视觉体系统一内容矩阵搭建社媒运营规划效果追踪复盘

兄弟们,eccfy 开机!今天不讲玄学,直接屏幕录制式开干:我们用 Node.js + Socket.IO 做一个能广播、能断线重连、能看延迟的实时聊天室。这个 Socket.IO教程 适合你做客服系统、弹幕、协同白板、订单状态推送。先说结论:原生 WebSocket 更轻,Socket.IO 胜在自动降级、房间、重连、心跳机制,项目上线更省心。

接下来打开终端,跟我敲:

mkdir socket-demo && cd socket-demo
npm init -y
npm i express socket.io
npm i -D nodemon

创建 server.js,注意看屏幕右边,我把心跳、CORS、消息广播都写上:

const express = require('express');
const http = require('http');
const { Server } = require('socket.io');

const app = express();
const server = http.createServer(app);

const io = new Server(server, {
  cors: { origin: '*' },
  pingInterval: 10000,
  pingTimeout: 5000
});

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

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

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

  socket.on('disconnect', reason => {
    console.log('disconnect:', reason);
  });
});

server.listen(3000, () => console.log('ws on 3000'));

再创建 package.json 脚本:

"scripts": {
  "dev": "nodemon server.js"
}

运行:

npm run dev

Chapter 2:Now watch this,客户端连接、房间广播、延迟测试

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

接下来做前端。新建 index.html,这就是最小可复现版本,也是很多人搜索 WebSocket实时通信怎么用 真正需要的东西:

<input id="msg" placeholder="type message" />
<button onclick="send()">send</button>
<ul id="list"></ul>

<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io('http://localhost:3000', {
  reconnectionAttempts: 5,
  reconnectionDelay: 1000
});

socket.emit('join', 'room-1');

function send() {
  const start = Date.now();
  socket.emit('chat', {
    room: 'room-1',
    user: 'eccfy',
    text: document.getElementById('msg').value,
    start
  });
}

socket.on('chat', data => {
  const li = document.createElement('li');
  li.innerText = `${data.user}: ${data.text} | delay=${Date.now() - data.ts}ms`;
  document.getElementById('list').appendChild(li);
});

socket.on('connect', () => console.log('connected', socket.id));
socket.on('disconnect', r => console.log('lost', r));
</script>

OK so,这里有个坑:如果你直接双击 HTML 文件,可能会遇到跨域或静态资源路径问题。最稳的方式是让 Express 托管它。在 server.js 顶部加:

app.use(express.static(__dirname));

浏览器打开 http://localhost:3000,开两个窗口,一个发“hello”,另一个立刻收到。我本机测试,Chrome 两个标签页之间延迟通常是 1-4ms;局域网两台机器测试大约 8-18ms。别急,接下来上压测。

Chapter 3:压测、排错与上线前验证

50TB日处理量120ms平均延迟99.99%SLA保障7×24运维监控

先装压测工具:

npm i -g artillery

创建 load.yml:

config:
  target: "http://localhost:3000"
  phases:
    - duration: 30
      arrivalRate: 20
scenarios:
  - engine: socketio
    flow:
      - emit:
          channel: "join"
          data: "room-1"
      - loop:
          - emit:
              channel: "chat"
              data:
                room: "room-1"
                user: "bot"
                text: "ping"
        count: 10

跑起来:

artillery run load.yml

我在 4 核 8G 本地环境测到:30 秒内约 600 个虚拟连接、6000 条消息,Node 单进程 CPU 峰值约 38%。如果你看到延迟突然从 20ms 飙到 800ms,优先查三件事:

  • Nginx 是否开启 WebSocket 升级:必须有 proxy_set_header Upgrade $http_upgrade; 和 proxy_set_header Connection "upgrade";。
  • 进程是否单点:多实例部署 Socket.IO 要接 Redis Adapter,否则房间广播会丢。
  • 网络路径是否被代理影响:有些同学边查 油管怎么看 边开 翻墙软件 或 免费VPN,结果本地 WebSocket 测试走了奇怪代理,延迟数据完全失真。

Nginx 关键配置长这样:

location /socket.io/ {
  proxy_pass http://127.0.0.1:3000;
  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection "upgrade";
  proxy_set_header Host $host;
}

如何验证它真的修好了:第一,浏览器控制台看到 connected socket.id;第二,两个窗口互发消息不丢;第三,断开服务端 5 秒再启动,客户端能自动重连;第四,跑 artillery 后错误率低于 1%,P95 延迟低于 100ms。需要排查跨境网络时,优先用官方文档、本地直连和免费工具;如果确实需要备用网络方案,Roxi 也只是可选项之一:https://wizzegroup.com。觉得这期有用,收藏、评论你的报错日志,eccfy 下期直接现场拆!

延伸阅读