websocket简易封装,附node测试服务

1,862 阅读3分钟

前言

最近公司有项目接口要使用websocket的方式获取数据和通讯,倒腾了个简易封装,对于websocket的介绍及对比http的优劣势这里不做赘述可自行百度,废话不多说,上code,淦!(PS:附带可用测试的node服务端)。

一,客户端

1.定义websocket(后面简称ws),连接地址,onMessage回调,onError回调,均为对象格式,支持多个ws同时连接

let ws = {};
let urlObj = {};
let messageCallback = {};
let errorCallback = {};

2.定义接收ws后端返回的数据方法

function websocketOnMessage (key, e) {
  messageCallback[key](JSON.parse(e.data));
}

3.定义ws关闭回调,删除对应的ws参数

function websocketOnClose (key, e) {
  if (!ws[key]) return;
  delete ws[key];
  delete urlObj[key];
  delete messageCallback[key];
  delete errorCallback[key];
}

4.初始化ws连接

// 初始化ws
function initWebsocket (key) {
  if (typeof (WebSocket) === 'undefined') {
    console.error('您的浏览器不支持WebSocket,无法连接');
    return;
  }
  // 需要组装url 可自行组装
  // http: ws:xx, https: wss:xxx
  ws[key] = new WebSocket(urlObj[key]);
  ws[key].onopen = function () {
    console.log(`${urlObj[key]} open-----`);
  };
  ws[key].onmessage = function (e) {
    websocketOnMessage(key, e);
  };
  ws[key].onerror = function () {
    console.error(`${urlObj[key]} 连接出错,请稍候重试`);
    errorCallback[key]();
  };
  ws[key].onclose = function (e) {
    console.log(`${urlObj[key]} close----`);
    websocketOnClose(key, e);
  };
}

5.ws初始连接成功时发送消息

/**
 * @description:发起websocket连接
 * @author Arezy
 * @param {String} key
 * @param {Object} params
 * @returns
 */
function websocketConllection (key, params) {
  if (!ws[key]) return;
  if (ws[key].readyState === ws[key].OPEN) {
    // OPEN状态发送消息给服务端
    sendMessage(key, params);
  } else if (ws[key].readyState === ws[key].CONNECTING) {
    // 非OPEN状态,延长1秒(自行设置)重新调用
    setTimeout(() => {
      websocketConllection(key, params);
    }, 1000);
  }
  if (ws[key].readyState === ws[key].CLOSED) {
    console.error(`${urlObj[key]} 连接异常,请稍候重试`);
    errorCallback[key]();
  }
}

6.创建ws连接,以url为key 为每个ws创建对应的url,onMessage回调,onError回调

/**
 * @description: 添加ws连接对象
 * @author Arezy
 * @export
 * @param {Object}
 * {
 *  url, 连接url
 *  params = {}, 约定参数
 *  onMessage, 获取服务消息回调
 *  onError: 出错回调
 *  }
 */
export function sendWebsocket ({ url, params = {}, onMessage, onError }) {
  urlObj[url] = url;
  messageCallback[url] = onMessage;
  errorCallback[url] = onError;
  initWebsocket(url);
  websocketConllection(url, params);
}

7.定义发送消息的方法,导出共外部调用

/**
 * @description: 发送消息
 * @author Arezy
 * @export
 * @param {String} key
 * @param {Object} data 和服务约定参数格式
 */
export function sendMessage (key, data) {
  // 发给后端的数据需要字符串化
  ws[key].send(JSON.stringify(data));
}

8.定义关闭对应ws连接方法,供外部调用

/**
 * @description: 关闭ws连接
 * @author Arezy
 * @export
 */
export function closeWebsocket (key) {
  if (ws[key]) {
    ws[key].close();
  }
}

9.完整版js

let ws = {};
let urlObj = {};
let messageCallback = {};
let errorCallback = {};

// 接收ws后端返回的数据
function websocketOnMessage (key, e) {
  messageCallback[key](JSON.parse(e.data));
}

function websocketOnClose (key, e) {
  if (!ws[key]) return;
  delete ws[key];
  delete urlObj[key];
  delete messageCallback[key];
  delete errorCallback[key];
}
/**
 * @description:发起websocket连接
 * @author Arezy
 * @param {String} key
 * @param {Object} params
 * @returns
 */
function websocketConllection (key, params) {
  if (!ws[key]) return;
  if (ws[key].readyState === ws[key].OPEN) {
    // OPEN状态发送消息给服务端
    sendMessage(key, params);
  } else if (ws[key].readyState === ws[key].CONNECTING) {
    // 非OPEN状态,延长1秒(自行设置)重新调用
    setTimeout(() => {
      websocketConllection(key, params);
    }, 1000);
  }
  if (ws[key].readyState === ws[key].CLOSED) {
    console.error(`${urlObj[key]} 连接异常,请稍候重试`);
    errorCallback[key]();
  }
}
// 初始化ws
function initWebsocket (key) {
  if (typeof (WebSocket) === 'undefined') {
    console.error('您的浏览器不支持WebSocket,无法连接');
    return;
  }
  // 需要组装url 可自行组装
  // http: ws:xx, https: wss:xxx
  ws[key] = new WebSocket(urlObj[key]);
  ws[key].onopen = function () {
    console.log(`${urlObj[key]} open-----`);
  };
  ws[key].onmessage = function (e) {
    websocketOnMessage(key, e);
  };
  ws[key].onerror = function () {
    console.error(`${urlObj[key]} 连接出错,请稍候重试`);
    errorCallback[key]();
  };
  ws[key].onclose = function (e) {
    console.log(`${urlObj[key]} close----`);
    websocketOnClose(key, e);
  };
}
/**
 * @description: 添加ws连接对象
 * @author Arezy
 * @export
 * @param {Object}
 * {
 *  url, 连接url
 *  params = {}, 约定参数
 *  onMessage, 获取服务消息回调
 *  onError: 出错回调
 *  }
 */
export function sendWebsocket ({ url, params = {}, onMessage, onError }) {
  urlObj[url] = url;
  messageCallback[url] = onMessage;
  errorCallback[url] = onError;
  initWebsocket(url);
  websocketConllection(url, params);
}
/**
 * @description: 发送消息
 * @author Arezy
 * @export
 * @param {String} key
 * @param {Object} data 和服务约定参数格式
 */
export function sendMessage (key, data) {
  // 发给后端的数据需要字符串化
  ws[key].send(JSON.stringify(data));
}
/**
 * @description: 关闭ws连接
 * @author Arezy
 * @export
 */
export function closeWebsocket (key) {
  if (ws[key]) {
    ws[key].close();
  }
}

10,在Vue中调用(可在其他任意框架中使用),

调用比较简单,通过sendWebsocket 传入url,参数params,onMessage回调(获取服务数据,笔者这里直接打印出来了), onError回调(处理异常),通过sendMessage 持续通讯,closeWebsocket关闭ws连接(beforeDestroy钩子函数调用,关闭当前组件所有连接),也可手动关闭

<!--
 * @Description: 测试webSocket
 * @Author: Arezy
 * @Date: 2021-01-19 19:45:51
-->
<template>
  <div class="web-socket">
    <div class="btn"
      @click="sendMsg">发送消息toUser</div>
    <div class="btn"
      @click="closeLogin">关闭userLogin</div>
    <div class="btn"
      @click="talking">连接其他路由</div>
    <div class="btn"
      @click="sendTacking">发送消息到talking</div>
  </div>
</template>

<script>
import { sendWebsocket, closeWebsocket, sendMessage } from '../../utils/socket.service.js';
export default {
  'name': 'Websocket',
  data () {
    return {
      url: 'ws:localhost:3000/user/login?token=123456789',
      talkingUrl: 'ws:localhost:3000/user/talking/usid001'
    };
  },
  created () {
    this.init();
  },
  methods: {
    // 默认初始第一个ws连接
    init () {
      sendWebsocket({
        url: this.url,
        params: { name: 'lily' },
        onMessage: this.message,
        onError: this.onError
      });
    },
    message (data) {
      console.log('login ws message: ', data);
    },
    onError () {
      console.log('login ws error');
    },
    closeLogin () {
      closeWebsocket(this.url);
    },
    sendMsg () {
      sendMessage(this.url, { name: 'Tony' });
    },
    // 创建第二个ws连接
    talking () {
      sendWebsocket({
        url: this.talkingUrl,
        params: { name: '西西', context: '你好啊,切图仔' },
        onMessage: this.cMessage,
        onError: this.conError
      });
    },
    cMessage (data) {
      console.log('talking ws message: ', data);
    },
    conError () {
      console.log('talking ws error');
    },
    sendTacking () {
      sendMessage(this.talkingUrl, { name: '西西', context: '淦,我不切图了' });
    }
  },
  beforeDestroy () {
    closeWebsocket(this.url);
    closeWebsocket(this.talkingUrl);
  }
};
</script>

<style lang="less" scoped>
.web-socket {
  position: relative;
  width: 100%;
  margin-left: 50px;
  .btn {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 150px;
    padding: 5px 15px;
    background: #2f7dcd;
    color: #ffffff;
    border-radius: 5px;
    margin-top: 25px;
  }
}
</style>


二,服务端(node.js)

服务端很简洁,只用来测试ws连接;
以express + express-ws构建
nodemon检测修改自动重启服务

1.index.js 使用express express-ws启动服务

var express = require('express');
var expressWs = require('express-ws');
var userRouter = require('./router/user.router.js');

var app = express();
expressWs(app);
app.use('/user', userRouter);

app.set('port', process.env.PORT || 3000); // 设定监听端口

var server = app.listen(app.get('port'), function () {
  console.log('Express server listening on port ' + server.address().port);
});

2.user.router.js 支持ws连接 和 http连接


var express = require('express');
var expressWs = require('express-ws');

var router = express.Router();
expressWs(router);

router
  .ws('/login', function (ws, req) {
    ws.on('message', function (msg) {
      // 业务代码
      const name = JSON.parse(msg).name;
      const resp = {
        token: req.query.token,
        name: name,
        message: `${name} login`
      };
      console.log('login msg req: ', resp);
      ws.send(JSON.stringify(resp));
    });
  })
  .get('/login', function (req, res) {
    console.log("🚀 ~ file: user.router.js ~ line 21 ~ req", req.query)
    res.send(req.query);
  })
  .post('/login', function (req, res) {
  });

router
  .ws('/talking/:usessionId', function (ws, req) {
    console.log("🚀 ~ file: user.router.js ~ line 34 ~ req", req.query)
    console.log("🚀 ~ file: user.router.js ~ line 34 ~ req", req.params);
    ws.on('message', function (msg) {
      // 业务代码
      const { context = '', name = '' } = JSON.parse(msg);
      const resp = {
        name: name,
        message: `${name} talking ${context}`
      };
      console.log('talking msg req: ', resp);
      ws.send(JSON.stringify(resp));
    });
  })

module.exports = router;

node仓库地址(gitee):https://gitee.com/ArezyLuo/node--ws-test
clone仓库 npm i 装包 npm start即可运行服务

总结

node服务跑起来,即可测试ws,希望可以帮到需要的小伙伴~