最近有一个业务场景是要做实时语音转义,考虑到实时性,所以决定采用websocket实现。
业务场景是A客户端(手机)进行语音转义的结果实时同步到B客户端(pc),这就需要用到websocket将A转义的结果发送给服务端,服务端接收到A的信息直接同步推送给B,所以它就和简单的无差别广播不同了。
看了网上的websocket示例,很少关于如何针对指定客户端推送消息的,解释的也是错误的。于是决定写一个大家拿去即用的示例。
首先解释下面示例websocket服务的通信过程
1.服务端起一个websocket的端口服务
2.然后客户端去 new WebSocket(服务端地址,如:ws://127.0.0.1:5201/?userId=liubao),此时就走到了服务端的wss.on(‘connection’)建立一个一对一连接了。为了方便大家理解,我把userId直接放url里了(真实业务场景一般是从header里拿token解析用户是谁)
3.服务端就把这个userId的请求连接池储存到clients数组里
4.此时客户端发送一个消息给服务端,就走到了 ws.on(‘message’)里,我们用data去接收客户端发送的消息
备注:从客户端接收到的数据是二进制的buffer信息(二进制信息是传统json信息速度的10倍+),所以在打印data时是个buffer,要想打印出来它的具体信息可以这样
console.log('%s',data);
取data信息时必须先转成字符串,否则是buffer数组信息,无法处理。
我们从客户端发送一个json信息包含userId和要发送的message
example:
// 客户端A
{ "userId": "liubao", "message": "给liubao一个小爱心" }
// 客户端B
{ "userId": "bob", "message": "给你bob一个大铁锤" }
5.有userId时遍历连接池,找到相同的userId连接池,进行推送消息
最后效果
copy下面代码到index.js文件,然后安装依赖和运行
npm i ws
node index.js
import { WebSocketServer } from 'ws';
const clients = []; // 与客户端建立的连接池
const wss = new WebSocketServer({ port: 5201 }); // 创建一个websocket服务
wss.on('connection', function connection(ws, request, client) {
let url = request.headers.origin + request.url; // example:ws://127.0.0.1:5201/?userId=liubao
let userId = getParam(url, 'userId');
if (userId) {
clients.push({ userId, ws: ws }); // 连接时只要url带userId参数,直接往客户端数组里塞入连接池信息
}
ws.on('message', function message(data, isBinary) { // 得到客户端往服务端发送的消息
try {
let objMessage = JSON.parse(`${data}`); // example:{ 'userId': 'liubao', 'message': '给你一个小爱心' }
let { userId, message } = objMessage;
let count = 0; // 发送客户端数量
if (userId) {
clients.forEach(e => {
if (e['userId'] === userId) {
count++;
e['ws'].send(`${message}`);
}
});
ws.send(`已发送userId为${userId}的${count}个客户端`);
} else {
ws.send(JSON.stringify({ error: '请发送指定userId的客户端' }));
}
} catch (err) {
ws.send(JSON.stringify({ error: err.message }));
}
});
ws.on('close', function close(event) {
console.log('关闭了');
});
});
const getParam = (url, param) => new URLSearchParams(new URL(url).search).get(param); // es6获取URL参数方法