"给我一个浏览器,我要让服务器主动开口说话。"
这句话在 2011 年以前的答案只能是轮询;今天,我们有了 WebSocket 和 Socket.io。
一、WebSocket:浏览器里的"对讲机"
WebSocket 不是 HTTP 的补丁,而是一条全新的全双工通道 。
握手阶段借 HTTP 的路,升级协议后就完全脱离 HTTP,数据变成二进制帧在 TCP 里自由穿梭。
原生 API 三板斧
js
const ws = new WebSocket('wss://example.com/chat');
ws.onopen = () => ws.send('天王盖地虎');
ws.onmessage = e => console.log('收到:', e.data);
ws.onclose = () => console.log('连接断开');
短短五行,你就能让浏览器和服务器互相喊话。
但真实项目往往不止"喊话"这么简单:
- 消息类型多样,谁来解析?
- 断线重连、心跳、鉴权、房间管理,谁来兜底?
- 老浏览器不支持怎么办?
于是,Socket.io 出现了。
二、Socket.io
Socket.io 不是 WebSocket 的替代品,而是工程化后的增强层 。
它在 WebSocket 之上做了三件事:
-
事件驱动
把裸字符串变成语义化事件,告别手写解析器。
jssocket.emit('$message', { text: '你好', sender: '张三' }); socket.on('$message', data => renderChat(data));
-
自动降级
浏览器不支持 WebSocket?Socket.io 悄悄切到长轮询,开发者无感知。
-
高级特性
命名空间、房间、广播、确认回调、心跳、重连、二进制传输......开箱即用。
三、从裸连接到聊天室
1. 服务端(Node.js + Socket.io)
js
import { Server } from 'socket.io';
const io = new Server(9528, { cors: true });
io.on('connection', socket => {
// 分配随机昵称
socket.emit('$name', `用户-${Math.random() * 1000 | 0}`);
// 推送历史消息
socket.emit('$history', messages);
// 监听新消息
socket.on('$message', text => {
const msg = { text, date: Date.now() };
messages.push(msg);
io.emit('$message', msg); // 广播给所有人
});
});
2. 客户端
html
<script src="https://cdn.socket.io/4.7/socket.io.min.js"></script>
<script>
const socket = io('ws://localhost:9528');
socket.on('$name', name => (myName = name));
socket.on('$history', list => renderHistory(list));
socket.on('$message', msg => appendMessage(msg));
function send(text) {
socket.emit('$message', text);
}
</script>