安装
服务端安装(Node.js)
import { Server } from "socket.io"; const io = new Server(3000, { cors: { origin: "http://localhost:5173" } });
客户端安装
import { io } from "socket.io-client"; const socket = io("http://localhost:3000");
与 Express 集成
import express from "express"; import { createServer } from "http"; import { Server } from "socket.io"; const app = express(); const server = createServer(app); const io = new Server(server);
服务端选项
cors跨域配置
path自定义路径(默认:/socket.io)
pingInterval心跳间隔(毫秒,默认 25000)
pingTimeout断开连接前的超时(默认 20000)
maxHttpBufferSize最大消息体积(字节,默认 1MB)
事件
内置事件(服务端)
connection客户端连接
disconnect客户端断开连接
disconnecting客户端正在断开(仍在房间中)
error错误事件
内置事件(客户端)
connect已连接到服务器
disconnect已与服务器断开连接
connect_error连接失败
reconnect重新连接成功
reconnect_attempt正在尝试重新连接
连接生命周期
io.on("connection", (socket) => { console.log(`connected: ${socket.id}`); socket.on("disconnect", (reason) => { console.log(`disconnected: ${reason}`); }); });
发送消息
服务端发送
socket.emit("hello", { msg: "world" }); socket.emit("data", arg1, arg2); io.emit("broadcast", data);
客户端发送
socket.emit("chat:message", { text }); socket.emit("update", data, (res) => { console.log("ack:", res); });
发送模式
socket.emit(ev, data)仅发送到此 socket
io.emit(ev, data)发送给所有已连接的客户端
socket.broadcast.emit()除发送者以外的所有客户端
io.to(room).emit()房间内的所有客户端
socket.to(room).emit()房间内除发送者以外的成员
广播
广播方法
io.emit("msg", data); socket.broadcast.emit("msg", data); io.to("room1").emit("msg", data); io.except("room2").emit("msg", data);
Volatile 与压缩
socket.volatile.emit()客户端未就绪时丢弃(不缓冲)
socket.compress(true).emit()启用单条消息压缩
io.local.emit()仅在本地服务器广播(多节点)
socket.timeout(5000).emit()发送并等待确认,超时出错
房间
房间操作
socket.join("room-1"); socket.join(["room-1", "room-2"]); socket.leave("room-1"); io.to("room-1").emit("msg", data);
房间属性
socket.rooms当前 socket 所在的房间集合
socket.id每个 socket 自动加入以自身 ID 命名的房间
io.sockets.adapter.rooms所有房间及其成员的 Map
房间模式
socket.on("join:room", (room) => { socket.join(room); io.to(room).emit("user:joined", socket.id); }); socket.on("disconnecting", () => { for (const room of socket.rooms) { socket.to(room).emit("user:left", socket.id); } });
命名空间
创建命名空间
const chat = io.of("/chat"); const admin = io.of("/admin"); chat.on("connection", (socket) => { chat.emit("user:online", socket.id); });
客户端连接到命名空间
const chat = io("http://localhost:3000/chat"); const admin = io("http://localhost:3000/admin");
动态命名空间
io.of(/^\/project-\d+$/).on("connection", (socket) => { const ns = socket.nsp.name; console.log(`joined namespace: ${ns}`); } );
中间件
服务端中间件
io.use((socket, next) => { const token = socket.handshake.auth.token; if (isValid(token)) return next(); next(new Error("authentication failed")); });
命名空间中间件
const admin = io.of("/admin"); admin.use((socket, next) => { if (socket.handshake.auth.role === "admin") return next(); next(new Error("not authorized")); });
中间件属性
socket.handshake.auth客户端发送的认证数据
socket.handshake.headers初始请求的 HTTP 头
socket.handshake.query连接 URL 中的查询参数
socket.data在中间件中附加的任意数据
错误处理
服务端错误
socket.on("action", (data, callback) => { try { const result = process(data); callback({ status: "ok", data: result }); } catch (err) { callback({ status: "error", msg: err.message }); } });
客户端错误
socket.on("connect_error", (err) => { console.log("connection error:", err.message); }); socket.io.on("reconnect_failed", () => { console.log("reconnection failed"); });
客户端重连选项
reconnection启用自动重连(默认 true)
reconnectionAttempts最大尝试次数(默认无限)
reconnectionDelay初始延迟毫秒(默认 1000)
reconnectionDelayMax最大延迟毫秒(默认 5000)
确认回执
客户端发送,服务端确认
// client socket.emit("save", data, (response) => { console.log("server ack:", response); }); // server socket.on("save", (data, callback) => { callback({ saved: true, id: 42 }); });
服务端发送,客户端确认
// server socket.emit("ping", (response) => { console.log("client ack:", response); }); // client socket.on("ping", (callback) => { callback("pong"); });
带超时
socket.timeout(5000).emit("save", data, (err, response) => { if (err) console.log("timeout!"); else console.log("ack:", response); } );
常用模式
聊天室
io.on("connection", (socket) => { socket.on("chat:join", (room) => { socket.join(room); socket.to(room).emit("chat:joined", socket.id); }); socket.on("chat:message", ({ room, text }) => { io.to(room).emit("chat:message", { from: socket.id, text }); }); });
在线状态
const users = new Map(); io.on("connection", (socket) => { users.set(socket.id, socket.handshake.auth); io.emit("users:list", [...users.values()]); socket.on("disconnect", () => { users.delete(socket.id); io.emit("users:list", [...users.values()]); }); });
限流
io.use((socket, next) => { const ip = socket.handshake.address; if (rateLimiter.consume(ip)) return next(); next(new Error("rate limit exceeded")); });