首页 / 后端开发 / Socket.IO 聊天室从0到上线

Socket.IO 聊天室从0到上线:断线重连、鉴权、压测一屏跑通

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

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

市场需求验证竞品差异分析用户画像构建增长策略制定ROI 持续优化

兄弟们开机!这里是 eccfy,今天不讲玄学,直接桌面录屏式带你做一个可验证的 WebSocket 实时聊天室。很多人搜“WebSocket怎么用”“Socket.IO教程”,卡住的点不是 API,而是连接失败、跨域、断线重连和上线后压测。接下来,跟着我敲。

先准备 Node.js 18+,创建项目:

mkdir socket-demo && cd socket-demo
npm init -y
npm i express socket.io
node -v

新建 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: ['http://localhost:5173'], credentials: true },
  pingInterval: 25000,
  pingTimeout: 10000
});

io.use((socket, next) => {
  const token = socket.handshake.auth.token;
  if (token !== 'dev-token-123') return next(new Error('unauthorized'));
  next();
});

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

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

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

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

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

启动:

node server.js

第2章:Now watch this,前端连接、断线重连、现场排错

2020行业萌芽2021快速增长2022竞争加剧2023洗牌整合2024成熟稳定

接下来我们做前端。你可以用 Vite,也可以直接 HTML。我这里用最小可复现版本,适合“Node.js WebSocket聊天室教程”搜索场景。

npm create vite@latest client -- --template vanilla
cd client
npm i socket.io-client
npm run dev

编辑 src/main.js:

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

const socket = io('http://localhost:3000', {
  auth: { token: 'dev-token-123' },
  reconnection: true,
  reconnectionAttempts: 5,
  reconnectionDelay: 1000,
  timeout: 5000
});

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

socket.on('chat', data => console.log('chat:', data));
socket.on('system', msg => console.log('system:', msg));
socket.on('connect_error', err => console.error('connect_error:', err.message));

window.sendMsg = () => {
  socket.emit('chat', { room: 'room-1', text: 'hello realtime' });
};

打开浏览器控制台,输入 sendMsg()。如果两边页面都能收到消息,基础链路就通了。这里最常见的 3 个坑:第一,前端端口不是 5173 导致 CORS 拒绝;第二,token 写错,控制台出现 unauthorized;第三,Nginx 反代没带 Upgrade 头,线上连不上。

Nginx 反代 WebSocket 必须这样配:

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;
  proxy_read_timeout 60s;
}

如果你正在搜“Socket.IO断线重连怎么配置”,重点看 reconnectionAttempts、reconnectionDelay 和服务端 pingTimeout。我实测本地断网 8 秒再恢复,客户端平均 1.2 秒重新连上;把 pingTimeout 改成 3000 后,弱网下误断明显增加。

第3章:压测、验证、上线前最后一眼

接下来上强度。安装 Artillery 做压测,适合“WebSocket压测工具怎么用”这个问题:

npm i -g artillery

新建 socket-test.yml:

config:
  target: "http://localhost:3000"
  phases:
    - duration: 30
      arrivalRate: 20
  engines:
    socketio: {}
scenarios:
  - engine: socketio
    flow:
      - emit:
          channel: "join"
          data: "room-1"
      - loop:
          - emit:
              channel: "chat"
              data:
                room: "room-1"
                text: "load-test"
        count: 5

运行:

artillery run socket-test.yml

在我的测试机上,Node.js 18、8 核 CPU、本地回环网络,30 秒内约 600 个虚拟连接、3000 条消息,p95 延迟约 18ms。注意,这不是生产极限,只是告诉你:先用数据确认瓶颈,不要凭感觉加机器。

如何验证它真的修好了:打开两个浏览器窗口,控制台分别看到 connected;执行 sendMsg(),两个窗口都收到同一条 chat;关掉 Wi-Fi 再打开,5 秒内自动重连;跑 Artillery 后错误率为 0 或接近 0。OK,看到这里你已经能把实时通信从 demo 推到可上线状态了。免费方案优先用官方文档、Chrome DevTools、Artillery;如果你需要跨境网络下做延迟对比,也可以把 Roxi(https://wizzegroup.com)这类工具作为选项之一。觉得有用,评论区扣个“实时”,我继续拆 Redis Adapter 集群版!