likes
comments
collection
share

物联网中websocket的使用

作者站长头像
站长
· 阅读数 3

大家好,本人就职的是一家物联网公司,主要是出售温湿度、环境监测设备,用于冷链运输、医药、环境监测。而我主要负责的是平台以及小程序的开发与维护。 在我工作中最主要的就是与服务器、硬件设备打交道,数据的传输小程序、PC网站主要采用的 websocket 实时获取检测数据用于显示查询。 因此在此我主要对我工作中使用 websocket 进行一下总结,同时也希望能给他人带来一些帮助。好了,话不多说,我们进入正题:了解一下 websocket 以及在物联网中的使用(仅从个人角度出发,不喜勿喷,希望大家多点赞收藏)。

websocket 简介

在讲解 websocket 使用之前我们先来初步了解一下它的基本概念。 通过网上一些资料我们可以知道:Websocket 是一种在单个 TCP 连接上进行全双工通信的协议。Websocket使得客户端和服务端之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在Websocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的链接,并进行双向数据传输。 以上是书本资料中对websocket的介绍,在实际项目中我们选择它的原因也就是硬件传感器设备上传到服务器的数据,实时的从服务器推给客户端(也就是我们的小程序、网站),毕竟实时数据客户端是不能够知道硬件传感器是什么时候上传的。 当然了,在Websocket没出来之前,很多网站为了实现推送技术,所用的都是轮询:在特定的时间间隔,由浏览器对服务器发出HTTP请求,其实就是写个周期定时器,然后不停地发请求,获取最新的数据。遗憾的是我入行后,就用的websocket,对于这种方式也仅限于了解,并没有在实际项目中使用过。但是在看一些同行的网站,目前这种方式也有只是很少了。

轮询与websocket对比

轮询方案

  1. 优点:对于前端、后端开发人员来说,该方案都省事儿
  2. 缺点:客户端不断地向服务器发出请求,即使服务器中存储的数据没有变化更新也会不断地发出请求。此外HTTP请求可能包含较长的头部,当真正有效的数据很小时,此时就会显得浪费很多带宽资源

websocket

  1. 优点:能更好的节省服务器资源和带宽,并且能够更实时的进行通讯。 解释: (1)更好的节省服务器资源和带宽:这是因为当服务端与客户端在连接创建后,交换数据时,用于协议控制的数据包头相对HTTP请求来说小,因此更好的节省服务器资源和带宽。 (2)更强的实时性:是基于websocket协议是全双工的,是由服务端主动给客户端下发数据的,这种方式可以仅当设备传感器上传新数据的时候,服务器再下发最新的数据给客户端,对比轮询的周期HTTP会减少无用的请求,保证强的实时性。
  2. 缺点:目前从我的角度讲,对于开发人员而言相比于轮询要费事,多写写代码以及处理逻辑。当然现在有很多三方的库,已经解决了我们很多事儿了。 目前市场上的浏览器基本上都支持 websocket 了,在兼容方面一般不用考虑。

物联网项目实际使用

讲解了这么多,做了如此长的铺垫,相信大家都对websocket有了一些了解,并且了解了使用场景。那么我们基于此,了解一下实际项目中到底如何使用呢!

技术环境

我们采用以下的技术环境进行逻辑讲解(本人主要是进行前端开发,所以在这里只讲解客户端逻辑的处理):

  1. react V16以上,PC端

创建websocket

// webSocketUrl 链接的 ws url
let ws = new WebSocket(webSocketUrl);

注意:在实际物联网中不同的硬件设备对应的ws协议或许不同,也有可能由于历史原因造成初始要创建多个websocket链接。 在微信小程序中要注意wx.connectSocket的最大并发限制是 5 个;各个浏览器也有对websocket并发数量有限制,因此要注意当页面卸载时要关闭 websocket 。 不过不用担心,只要我们能弄明白创建一个websocket的逻辑以及注意事项,就能拓展到多个。 注意:当要创建多个websocket链接的时候,最好每个ws创建有一定的间隔可以间隔5秒,避免一些不知名的情况导致创建失败。

websocket逻辑处理

1、websocket 建立成功

监听 websocket 创建成功,在该回调中我们可以处理:首次发送用户标识信息、建立心跳监听防止意外情况导致websocket断开。

ws.onopen = function () {
    // 注意 ws.send 中接收的为字符串
    ws.send(msgString);
    // 建立心跳
    component.buildHeartCheck(version, component, ws);
};

2、websocket 建立失败

当websocket链接建立失败时,我们可以在回调函数中处理用户提示逻辑

ws.onerror = function () {
   console.log("连接创建失败");
}

3、接收websocket信息

在此方法中,我们可以接收到websocket服务端传递过来的信息,然后进行处理,更新存储的设备数据并渲染页面、更新心跳 注意:接收到的数据信息是字符串类型,需要使用JSON.parse(evt.data)进行转化。

ws.onmessage = function (evt) {
    let messageObject = JSON.parse(evt.data);
    let type = messageObject.type;
    component.wsOnMessageSwitch(type, ws, messageObject, deviceAddressMap)
    // 非 v6 传递过来消息;更新消息时间戳
    component.heartCheckUpdateMsgTime(version)
};

4、关闭websocket

在此回调函数中我们可以判断当前是主动关闭还是意外关闭、处理意外关闭然后重新创建websocket。

 ws.onclose = function (msg) {
    // 关闭 websocket
    /**
     * 对当前触发的连接关闭事件进行区分
     * msg中 msg.code 为3001 表示主动断开的(3001 是自定义的,后面有相关讲解)
     * 其他情况表示 连接进行意外断开
     */
    if (msg.code == 3001) {
      // 管理员主动进行连接关闭
      console.log("非协议6 连接 主动 关闭...");
    } else {
      // 连接意外进行关闭
      // 重新打开 websocket 连接 
      // 在建立新的连接之前,将之前得数据以及连接全部清空
      component.clearNowMsg();
      component.reconnect();
    }
};

那么说回来了,我们怎样去判断websocket是意外关闭的还是主动关闭的呢

    ws.close(3001);

这样我们就可以在 ws.onclose 回调中进行判断。

心跳逻辑的处理

心跳逻辑的功能:用于防止服务端出现问题,导致websocket断开,而客户端还认为链接正常,导致无法实时更新平台数据。

let heartCheck = {
  timeout: 1000 * 60, // 定时器触发时间
  heartOverTime: 1000 * 60 * 6, // 心跳超时时间
  timeoutObj: null, // 周期性定时器
  msgTime: 0, // 每一回,消息传来之后更新时间
  msg: { type: 201 }, // 心跳包信息
  connectNum: 0, // 连接断开后重连的次数,不超过 6 次
  updateMsgTime: function () { // 接收消息更新时间
    // console.log('v6 更新时间')
    // 当有消息传递过来时,更新消息时间
    this.msgTime = new Date().getTime();
    // 更新当前重复连接次数
    this.connectNum = 0;
  },
  // 创建心跳 创建定时器
  buildHeartCheck: function (component, ws) {
    this.timeoutObj = setInterval(() => {
      /**
       * 判断当前时间与 最近的一次消息传递来的时间
       * 是否超过了 ,超过了之后发心跳
       */
      if (new Date().getTime() - this.msgTime > this.heartOverTime) {
        // 超时了,发送心跳包
        ws.send(JSON.stringify(this.msg));
        /**
         * 判断是否接收到了,然后 判断是否进行连接操作
         * 如果接收到了消息,那么 消息时间就会更新、心跳次数更新,则下一次不会进入该操作
         * 如果没有接收到,则 下次继续发心跳,发送六次,之后还没消息表明需要重连,
         * 心跳经过六次之后,都没有消息表明需要重连
         * 重连经过 3 次之后,都没有,进行消息提示网络出现问题
         */
        if (this.connectNum < 6) {
          // 第一次发送心跳包
          this.connectNum ++;
        } else if (this.connectNum < 9) {
          /**
           * 经历过第一次发送心跳包后,还没有接收到信息
           * 进行重连操作
           */
          this.connectNum ++;
          // 重连之前清除之前的连接
          component.heartCheckConnect();
        } else {
          // 发送六次心跳包后,进行重连,然后还是没能收到消息,进行提示网络出问题
          message.error('网络出现问题,无法建立连接')
          clearInterval(this.timeoutObj);
          this.connectNum = 0;
        }
      } else {
        // console.log('v6 未超时 不发心跳包');
      }
    }, this.timeout);
  },
  // 清除心跳定时器
  clearTimer: function () {
    console.log('清除定时器 v6')
    clearInterval(this.timeoutObj)
  }
}

export {
  heartCheck,
}

总结

至此,关于websocket在物联网项目中的使用主要逻辑以及注意点,均已介绍完毕。在实际项目中,对于 websocket 实时获取的数据,不要频繁使用 setStateuseState进行更新,这样当设备过多的时候会由于频繁的页面重绘导致卡顿影响用户体验。 我们可以采用一个新的变量(不放置在state中),然后周期批量更新state变量,渲染页面以及对应的组件,在组件中也监听变量,判断是否进行更新重新渲染。 学习技术的路很漫长,但是不断地收获成长令我们乐此不疲,如果你读到了这里,那么点个赞收藏一下吧!技术路上,与君共勉!!!加油!!!

番外

其实在物联网中,mqtt协议也是很常用的,但是我并不了解没有实际使用过。后续待我对此有一定了解后,再尝试总结一下。