WebSocket
WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议,与 HTTP 不同,它允许服务器主动向客户端推送数据
建立连接过程
客户端发起 HTTP 握手请求(HTTP Upgrade)
客户端通过发送一个特殊的 HTTP 请求来“升级”到 WebSocket 协议
GET /chat HTTP/1.1
Host: example.com:80
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13Upgrade: websocket:请求升级协议为 WebSocketConnection: Upgrade:表明这是一个升级请求Sec-WebSocket-Key:客户端生成的随机 Base 64 编码字符串,供服务器验证Sec-WebSocket-Version:指定 WebSocket 协议版本(当前为 13)
服务器响应握手
服务器如果同意升级,就返回如下响应:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=101 Switching Protocols:状态码表示协议切换完成,之后使用 WebSocket 进行通信
建立连接后通信
握手成功后,HTTP 协议升级为 WebSocket 协议,之后通信不再使用 HTTP,而是使用 WebSocket 帧(frame)进行双向传输
客户端和服务器可以随时相互发送消息,保持连接直到任一方关闭
代码实现
使用 Node.Js 来搭建一个简单的 WebSocket 服务
服务端
js
const Websocker = require('ws');
// 创建一个 WebSocket 服务器,指定端口为 8080
const wss = new Websocker.Server({ port: 8080 });
// 监听连接事件
wss.on('connection', (ws) => {
console.log('客户端已连接');
ws.on('message', (message) => {
console.log(`收到消息: ${message}`);
// 发送数据
ws.send(`${message},欢迎连接到 WebSocket 服务器!`);
});
// 监听关闭事件,在一个 Connection 内
ws.on('close', () => {
console.log('客户端已断开连接');
});
});客户端
js
const ws= require('ws');
// 创建一个 WebSocket 客户端,连接到指定的服务器地址
const wsClient = new ws('ws://localhost:8080');
// 监听连接事件
wsClient.on('open', () => {
console.log('已连接到服务器');
// 发送数据
wsClient.send('Hello, server!');
});
wsClient.on('message', (message) => {
console.log(`收到来自服务器的消息: ${message}`);
});我觉得用 Node.Js 当客户端调试有点麻烦,这里使用 ApiFox:
广播通信
WebSocket 是一对一的:客户端连接到服务器,服务器再与客户端通信,要实现多个客户端之间的通信(如聊天室),需要通过服务器进行转发或广播
- 所有客户端连接到 WebSocket 服务器
- 某个客户端发送消息到服务器
- 服务器接收后,将消息转发给所有其他客户端,或广播给所有客户端(包括自己)
js
// server.js
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', function connection(ws) {
console.log('新客户端已连接');
ws.on('message', function incoming(message) {
console.log('收到消息:', message.toString());
// 广播给所有客户端
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
// 转发全部信息到全部客户端
client.send(message.toString());
}
});
});
ws.send('欢迎加入聊天室!');
const onlineCount = wss.clients.size;
ws.send(`当前在线用户数量: ${onlineCount}`);
});当然连接是有先后顺序的,并不会存储用户信息
多群组的设计
设计的关键在于:
- 给每个客户端分配一个“群组”标识(如房间名)
- 服务器记录每个群组有哪些客户端
- 只在特定群组中广播消息