Socket.io 如何限制同一IP地址的连接数量
单IP限制Socket.io连接数的最优实现方案(Node.js环境)
该方案校验时机在TCP连接建立后、Socket.io会话初始化完成前,不会额外占用服务端资源维护无效连接,性能开销极低、逻辑简单易维护,是对应场景的最优实现。
核心逻辑:利用Socket.io的前置中间件,维护IP与对应活跃连接数的映射,新连接进入时判断是否超过阈值,超过直接拒绝;连接断开时同步更新计数,保证数据准确性。
实现步骤
- 存储选型:单机部署直接用内存Map存储计数即可,分布式多实例部署可以用Redis存储计数,保证多实例间数据同步。
- 真实IP获取:如果服务端前有Nginx等反向代理,需要从
X-Forwarded-For请求头中获取客户端真实IP,避免拿到代理服务器IP导致规则失效。 - 前置校验逻辑:在Socket.io中间件中完成计数判断,超过阈值直接抛出错误终止连接流程,未超过阈值则计数+1放行。
- 计数维护:监听连接断开事件,对应IP计数-1,计数归0时删除对应键节省存储空间。
示例代码
服务端代码
const { Server } = require("socket.io"); const io = new Server(3000, { cors: { origin: "*" } // 按需修改为你的业务跨域规则 }); // 单IP最大连接数阈值,可按需调整 const MAX_CONNECTIONS_PER_IP = 3; // 单机场景用Map存储IP-连接数映射,分布式场景替换为Redis读写逻辑即可 const ipConnectionCount = new Map(); // Socket.io前置校验中间件 io.use((socket, next) => { // 取客户端真实IP const clientIp = socket.handshake.headers['x-forwarded-for'] || socket.handshake.address; const currentCount = ipConnectionCount.get(clientIp) || 0; if (currentCount >= MAX_CONNECTIONS_PER_IP) { // 超过阈值,直接拒绝连接 return next(new Error('IP connection limit exceeded')); } // 计数更新,放行连接 ipConnectionCount.set(clientIp, currentCount + 1); next(); }); io.on("connection", (socket) => { const clientIp = socket.handshake.headers['x-forwarded-for'] || socket.handshake.address; // 监听连接断开,更新计数 socket.on("disconnect", () => { const currentCount = ipConnectionCount.get(clientIp) || 0; if (currentCount > 1) { ipConnectionCount.set(clientIp, currentCount - 1); } else { ipConnectionCount.delete(clientIp); } }); // 其余业务逻辑 });
客户端友好提示代码
import { io } from "socket.io-client"; const socket = io("你的Socket.io服务端地址"); // 监听连接错误,给用户明确提示 socket.on("connect_error", (err) => { if (err.message.includes("IP connection limit exceeded")) { alert("当前IP连接数已达上限,请关闭多余标签页后重试"); } });
额外优化点
- 异常兜底:可以给IP计数增加过期时间,避免服务端异常重启、连接异常断开没有触发disconnect事件导致计数不准的问题,分布式场景直接用Redis的EXPIRE命令即可实现,单机场景可以引入
lru-cache库实现带过期时间的存储。 - 白名单配置:可以新增IP白名单列表,内部测试IP、可信IP可跳过连接数限制。
- 防IP伪造:如果从
X-Forwarded-For头取IP,需要配置反向代理只信任上层节点的请求头,避免客户端伪造IP绕过限制。
内容的提问来源于stack exchange,提问作者Infinite-3D
相关产品推荐
相关产品推荐

