封装一套几乎通用的WebSocket代码

前言

对接过几次WebSocket连接,无论是在纯JavaScript、Vue亦或Uniapp等框架语言中使用,Socket代码流程基本上差不多。无非就是:

  • 发起连接
  • 发送数据(发送心跳等)
  • 接收数据
  • 关闭连接
  • 断线重连
  • 异常处理...
    每次都需要重写比较麻烦,故封装一套大致流程的WebSocket代码,哪怕从纯JavaScript项目搬到Vue、Uniapp等框架中,也只需要做小部分修改即可。

具体实现

创建socket.js,代码如下:

// 在Vue中使用,不需要可以去除以下引用
import Vue from 'vue'
import storage from 'store'

// 导出socket对象
export {
  socket
}

// socket主要对象
var socket = {
  websock: null,
  // 固定的WebSocket地址:此处是从env文件中读取socket地址,可以自行从其他config文件中读取或直接写死
  // 如需使用动态WebSocket地址,请自行作ajax通讯后扩展
  ws_url: process.env.VUE_APP_API_SOCKET_URL,
  // 开启标识
  socket_open: false,
  // 心跳timer
  hearbeat_timer: null,
  // 心跳发送频率
  hearbeat_interval: 5000,

  // 是否自动重连
  is_reonnect: true,
  // 重连次数
  reconnect_count: 3,
  // 已发起重连次数
  reconnect_current: 1,
  // 重连timer
  reconnect_timer: null,
  // 重连频率
  reconnect_interval: 3000,

  /**
   * 初始化连接
   */
  init: () => {
    if (!("WebSocket" in window)) {
      console.log('浏览器不支持WebSocket')
      return null
    }

    // 已经创建过连接不再重复创建
    if (socket.websock) {
      return socket.websock
    }

    socket.websock = new WebSocket(socket.ws_url)
    socket.websock.onmessage = function (e) {
      socket.receive(e)
    }

    // 关闭连接
    socket.websock.onclose = function (e) {
      console.log('连接已断开')
      console.log('connection closed (' + e.code + ')')
      clearInterval(socket.hearbeat_interval)
      socket.socket_open = false

      // 需要重新连接
      if (socket.is_reonnect) {
        socket.reconnect_timer = setTimeout(() => {
          // 超过重连次数
          if (socket.reconnect_current > socket.reconnect_count) {
            clearTimeout(socket.reconnect_timer)
            return
          }

          // 记录重连次数
          socket.reconnect_current++
          socket.reconnect()
        }, socket.reconnect_interval)
      }
    }

    // 连接成功
    socket.websock.onopen = function () {
      console.log('连接成功')
      socket.socket_open = true
      socket.is_reonnect = true
      // 开启心跳
      socket.heartbeat()
    }

    // 连接发生错误
    socket.websock.onerror = function () {
      console.log('WebSocket连接发生错误')
    }
  },

  /**
   * 发送消息
   * @param {*} data 发送数据
   * @param {*} callback 发送后的自定义回调函数
   */
  send: (data, callback = null) => {
    // 开启状态直接发送
    if (socket.websock.readyState === socket.websock.OPEN) {
      socket.websock.send(JSON.stringify(data))

      if (callback) {
        callback()
      }

      // 正在开启状态,则等待1s后重新调用
    } else if (socket.websock.readyState === socket.websock.CONNECTING) {
      setTimeout(function () {
        socket.send(data, callback)
      }, 1000)

      // 未开启,则等待1s后重新调用
    } else {
      socket.init()
      setTimeout(function () {
        socket.send(data, callback)
      }, 1000)
    }
  },

  /**
   * 接收消息
   * @param {*} message 接收到的消息
   */
  receive: (message) => {
    var params = JSON.parse(message.data)

    if (params.kind != 0) {
      console.log('收到服务器内容:', message.data)
    }

    if (params == undefined) {
      console.log("收到服务器空内容")
      return false
    }
    
      // 以下是接收消息后的业务处理,仅供参考

    // 被服务器强制断开
    if (params.kind != undefined && params.kind == 110) {
      socket.socket_open = false
      socket.is_reonnect = true

      // 被服务器踢掉
    } else if (params.kind == 99) {
      socket.socket_open = true
      socket.is_reonnect = false
      console.log("被挤下线 不做处理")
      return false
    } else if (params.kind == 'order_new') {
      console.log('有新的订单通知')
      var time = Date.parse(new Date()) / 1000
      params.timestamp = parseInt(params.timestamp)

      console.log(time - params.timestamp)

      // 测试环境不限制推送时间
      if (process.env.NODE_ENV == 'development') {
        // 小于半小时push和播放  大于半小时并且小于3天只push  大于3天不处理
        if ((time - params.timestamp) > 3600 * 24 * 3) {
          console.log('超过三天')
          return false
        }

        if ((time - params.timestamp) > 30 * 60 && (time - params.timestamp) < 3600 * 24 * 3) {
          console.log('超过半小时')
          return false
        }
      }

      // uniapp中可以使用$on和$emit来实现对应的业务处理

    } else if (params.kind == 'refund_created') {
      console.log('有新的退款订单')
      
    }

    if (params.kind == 'order_new' || params.kind == 'refund_created') {
      console.log('订单列表刷新')

    }
    
      // 自行扩展其他业务处理...
  },

  /**
   * 心跳
   */
  heartbeat: () => {
    console.log('socket', 'ping')
    if (socket.hearbeat_timer) {
      clearInterval(socket.hearbeat_timer)
    }

    socket.hearbeat_timer = setInterval(() => {
      const token = storage.get('Access-Token')
      var data = {
        kind: 0, //请求类型 kind 0 心跳包
        shop_id: Vue.prototype.$shop_id(false), //如果是商家 传当前店铺ID 否则可不传
        'API-Token': token, //用户的token
        'API-Source': 'MERCHANT', // MERCHANT  商家  CUSTOMER  顾客
      }
      socket.send(data)
    }, socket.hearbeat_interval)
  },

  /**
   * 主动关闭连接
   */
  close: () => {
    console.log('主动断开连接')
    clearInterval(socket.hearbeat_interval)
    socket.is_reonnect = false
    socket.websock.close()
  },

  /**
   * 重新连接
   */
  reconnect: () => {
    console.log('发起重新连接', socket.reconnect_current)

    if (socket.websock && socket.socket_open) {
      socket.websock.close()
    }

    socket.init()
  },
}

使用时主要修改socket对象中的一些配置、发送数据处理、接收数据业务处理等。

如何使用

// 引入socket.js
import { socket } from '@/utils/socket'

// 发起连接
socket.init()

// 发送数据
socket.send({test:123}, () => {console.log('这是回调函数,发送test 123后执行')})

// 断开连接
socket.close()

// 重新连接
socket.reconnect()

封装后,使用简单粗暴。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,383评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,522评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,852评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,621评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,741评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,929评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,076评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,803评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,265评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,582评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,716评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,395评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,039评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,027评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,488评论 2 361
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,612评论 2 350