资讯专栏INFORMATION COLUMN

在 nodejs 中 利用 websocket 实现简单的 “1对1” 消息传递

NoraXie / 2461人阅读

摘要:为了体现前后端分离,提高开发效率的精髓。转发消息服务器将收到的来自于发送方消息中的值作为要转发的目标接收方,在服务器自身维护的对象中找到接收方的这个连接,然后将发送方的标识作为转发给接收方。

背景

简单的描述一下需求场景:应用需要进行客户端到客户端的通信,websocket 就能很好的进行这一操作,目前 网易云信的 IM 等功能也是利用 websocket 进行的。

必要性

对前端开发人员来说,目前能够提供 mock 服务的第三方工具还是比较多的,基本上,与后台开发人员约定好请求路径、请求字段和响应字段之后就能前后台独立开发了。

但 websocket 服务器与 http 服务器最大的区别就是 websocket 服务器必须得一直提供服务,否则客户端之间就无法进行通信。

为了体现前后端分离,提高开发效率的精髓。肯定是不能先把逻辑全部盲写好了之后再与后台联调的,便决定与后台约定好了接口名和数据形式之后,用 nodejs 写一个简单的 websocket 通信服务。

功能

websocket 用的比较多的应该就是传输文本(简单点说就是字符串),所以,这个字符串携带着接收方的用户标识(toId),其他的信息(比如消息类型 type 和 消息内容 data 等),通常的做法是 JSON.stringfy() 之后转成字符串,服务端将发送方的用户标识(fromId)、其他的信息(比如发送过来的消息类型 type 和 消息内容 data)等信息转发给接收方,接收方接收到字符串之后再 JSON.parse() 进行下一步操作。

用下面这张图描述一下我希望 websocket 服务器能提供给客户端的功能。

客户端

websocket 提供的方法中常用的有:newonopenoncloseonerroronmessagesend

毫无疑问,对前端开发人员来说需要与业务逻辑相结合、重点关注的方法就是以下三个:

new

在连接 websocket 服务器时传递自己的 id

const ws = new WebSocket(`ws://localhost:8080/websocketServer/${id}`);
send

发送消息(字符串)给服务器,服务器再转发给目标

const msgObj = {
    toId: 666,
    type: "hello",
    data: "message......"
};
const msgStr = JSON.stringfy(msgObj);
ws.send(msgStr);
sendTo

由于 send 不能接收对象或者数组类型的数据,每次都得写一个临时的对象,再调用 JSON.stringfy() 方法转成字符串。

那就封装一个符合自己业务场景的方法,简化开发过程中实现发送功能的步骤。

// 在原型对象上增加的 sendTo 方法
if ( ! WebSocket.prototype.sendTo ) {
    WebSocket.prototype.sendTo = function(toId, type, data) {
        const msg = JSON.stringify({ toId, type, data });
        this.send(msg);
    }
}

// 调用形式
ws.sendTo(toId, type, data);
onmessage

接收消息,通常会根据消息中 type 的不同值:

自己做点什么

给对方一个响应(调用 sendTo 方法)

ws.onmessage = function(msg) {
    const { fromId, type, data } = JSON.parse(msg);
    switch (type) {
        case "message":
            // 自己做点什么
            break;
        
        case "hello":
            // 返回一个消息
            this.sendTo(fromId, "response", "response data");
    }
}

最后简单的封装一下初始化方法,方便在不同的地方使用。

const wsUrl = "ws://localhost:8080/";
const wsPath = "socketServer/";
// 生成实例
let ws = null;
const connect = async () => {
    ws = await connectWS(uid, messageHandle);
    // ...
    // ...
    // 连接成功之后做点什么
}

function connectWS(uid, msgHandle) {
    if ( ! WebSocket.prototype.sendTo ) {
        WebSocket.prototype.sendTo = function(toId, type, data) {
            const msg = JSON.stringify({ toId, type, data });
            this.send(msg);
        }
    }
    
    return new Promise((resolve, reject) => {
        const ws = new WebSocket(`${wsUrl}${wsPath}${uid}`);
        ws.onopen = () => {
            console.log("open");
            resolve(ws);
        };
        ws.onclose = () => {
            console.log("close");
        };
        ws.onerror = () => {
            console.log("error");
        };
        ws.onmessage = (msg) => {
            msgHandle(JSON.parse(msg.data));
        };
    });
}
// 接收消息的处理函数
function messageHandle(msgData) {
    const { fromId, type, data } = msgData;
    switch (type) {
        case "":
            break;
        default:
            break;
    }
}
服务端

常用的 nodejs-websocketexpress-ws 两个框架,都需要自身维护一个对象来记录每个 websocket 连接是哪个用户,从而实现消息转发。

express-ws 为例:

记录连接

首先,服务端要记录当前连接的用户,以便转发消息的时候能够正确发送给目标。

转发消息

服务器将收到的来自于发送方消息中的 toId 值作为要转发的目标(接收方),在服务器自身维护的对象中找到接收方的这个连接,然后将发送方的标识作为 fromId 转发给接收方。

const express = require("express");
const express_ws = require("express-ws");
const app = express();
const wsObj = {};
express_ws(app);

app.ws("/socketServer/:uid", (ws, req) => {
    const uid = req.params.uid;
    wsObj[uid] = ws;
    ws.onmessage = (msg) => {
        let { toId, type, data} = JSON.parse(msg.data);
        const fromId = uid;
        if (fromId != toId && wsObj[toId]) {
            // wsObj[toId]   表示 接收方 与服务器的那条连接
            // wsObj[fromId] 表示 发送方 与服务器的那条连接
            wsObj[toId].send(JSON.stringify( { fromId, type, data } ))
        }
    }
});

app.listen(8080);

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/106307.html

相关文章

  • 那些年曾谈起跨域

    摘要:在中,在不同的域名下面进行数据交互,就会遇到跨域问题,说到跨域首先要从同源说起,浏览器为了提供一种安全的运行环境,各个浏览器厂商协定使用同源策略。在上面说过是不受同源策略限制的,但是出于安全原因,浏览器限制从脚本内发起的跨源请求。 对于前端开发来说跨域应该是最不陌生的问题了,无论是开发过程中还是在面试过程中都是一个经常遇到的一个问题,在开发过程中遇到这个问题的话一般都是找后端同学去解决...

    galois 评论0 收藏0
  • 初探WebSocket

    摘要:流控制通常就是在客户端的页面使用一个隐藏的窗口向服务端发出一个长连接的请求。和长链接以上几种服务器推的技术中长轮询和流控制其实都是基于长链接来实现的,也就是中所谓的。通信协议于年被定为标准,并被所补充规范。 初探WebSocket node websocket socket.io 我们平常开发的大部分web页面都是主动‘拉’的形式,如果需要更新页面内容,则需要刷新一个,但Slack工...

    Channe 评论0 收藏0
  • Node.js+WebSocket创建简单聊天室

    摘要:好的,这样以来我们的前期准备工作就已经完成了,下面我们来搭建聊天室对应的客户端和服务器端。 websocket简介 websocket其实HTML中新增加的内容,其本质还是一种网络通信协议,以下是websocket的一些特点: (1)因为连接在端口80(ws)或者443(wss)上创建,与HTTP使用的端口相同,几乎所有的防火墙都不会阻塞WebSocket链接 (2)因...

    cppprimer 评论0 收藏0
  • Node.js+WebSocket创建简单聊天室

    摘要:好的,这样以来我们的前期准备工作就已经完成了,下面我们来搭建聊天室对应的客户端和服务器端。 websocket简介 websocket其实HTML中新增加的内容,其本质还是一种网络通信协议,以下是websocket的一些特点: (1)因为连接在端口80(ws)或者443(wss)上创建,与HTTP使用的端口相同,几乎所有的防火墙都不会阻塞WebSocket链接 (2)因...

    Seay 评论0 收藏0

发表评论

0条评论

NoraXie

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<