3、Socket.IO入门:Socket.IO的安装与配置、事件驱动模型、房间与命名空间
聊到多屏联动,WebSocket 是绕不开的基础设施。但说实话,原生 WebSocket 用起来有点「裸」。你想想看,要处理重连、要搞广播、要区分不同客户端...这些琐事够你喝一壶的。
这时候,Socket.IO 就站出来了。它把 WebSocket 包装得特别顺手,还加了一堆「后悔药」——比如自动降级到轮询。我个人习惯,只要涉及实时通信,第一反应就是拿 Socket.IO 来搭架子。
3.1 安装与配置:5 分钟跑起来
安装没什么花头,一行命令搞定:
npm install socket.io
服务端配置,我一般这么写:
const http = require('http');
const { Server } = require('socket.io');
const server = http.createServer();
const io = new Server(server, {
cors: {
origin: '*', // 开发环境先放通,生产环境记得收紧
methods: ['GET', 'POST']
}
});
server.listen(3000, () => {
console.log('Socket.IO 服务跑在 3000 端口');
});
客户端那边更简单:
<script src="/socket.io/socket.io.js"></script>
<script>
const socket = io('http://localhost:3000');
</script>
配置里有个坑,我提一下。跨域问题在本地开发时经常冒出来,cors 选项一定要配。我曾经有个项目,联调了一下午才发现是跨域没放开,气得我差点砸键盘。
3.2 事件驱动模型:一切皆事件
Socket.IO 的核心思想,说白了就是「你发一个事件,我收一个事件」。跟 DOM 事件很像,但它是跨网络的。
服务端监听事件:
io.on('connection', (socket) => {
console.log('有客户端连进来了');
socket.on('chat message', (msg) => {
console.log('收到消息:', msg);
// 广播给所有人
io.emit('chat message', msg);
});
socket.on('disconnect', () => {
console.log('客户端走了');
});
});
客户端发送事件:
socket.emit('chat message', '大家好,我是新来的');
这里有个细节要注意:socket.emit 只发给当前连接,io.emit 是群发。我刚开始做的时候,经常搞混这两个,结果消息要么发不出去,要么炸了锅。
socket.emit()→ 发给当前客户端io.emit()→ 发给所有客户端socket.broadcast.emit()→ 发给除自己外的所有人
事件名可以自定义,但别用 connect、disconnect 这些保留字。嗯,这个坑我踩过,自定义事件叫了个 connect,结果服务端死活不触发,查了半天文档才发现。
3.3 房间:把用户分组管理
多屏联动的场景里,你不可能让所有屏幕都收到同样的消息。比如,A 组屏幕只显示 PPT,B 组屏幕只显示提词器。这时候就需要「房间」了。
加入房间:
socket.join('room-ppt');
socket.join('room-teleprompter');
离开房间:
socket.leave('room-ppt');
给特定房间发消息:
io.to('room-ppt').emit('slide-change', { page: 3 });
我做过一个教育项目,老师端、学生端、投屏端各占一个房间。老师翻页时,只通知投屏端;提问时,才广播给学生端。这种设计,代码清晰,逻辑也不容易乱。
socket.join() 和 io.to() 就行。
3.4 命名空间:隔离不同业务
房间解决的是「同一类业务内的分组」,命名空间解决的是「不同业务之间的隔离」。说白了,就是给你的 Socket.IO 开几个「独立通道」。
定义命名空间:
const chatNsp = io.of('/chat');
const monitorNsp = io.of('/monitor');
chatNsp.on('connection', (socket) => {
console.log('有人进了聊天室');
});
monitorNsp.on('connection', (socket) => {
console.log('有人连了监控');
});
客户端连接不同命名空间:
const chatSocket = io('/chat');
const monitorSocket = io('/monitor');
命名空间之间完全隔离,事件不会串。我在一个物联网项目里用过这个特性:/device 命名空间处理设备数据,/admin 命名空间处理管理指令。两边互不干扰,维护起来特别省心。
| 特性 | 房间 | 命名空间 |
|---|---|---|
| 作用范围 | 同一命名空间内 | 全局隔离 |
| 适用场景 | 分组广播 | 业务模块分离 |
| 资源开销 | 低 | 稍高 |
| 典型用途 | 多屏联动、聊天室 | 聊天 + 监控 + 日志 |
最后说一句,Socket.IO 的调试挺方便的。加上 ?EIO=4&transport=polling 参数,就能看到详细的握手日志。我曾经靠这个参数,定位到一个诡异的断连问题——原来是 Nginx 的超时配置太短了。
好了,这一章的内容就这些。Socket.IO 入门不难,关键是理解事件驱动、房间、命名空间这三个概念。下一章,我们会拿这些知识来做一个真正的多屏联动 Demo,到时候你就知道这些东西有多好用了。