用戶
 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

掃一掃,登錄網站

小程序社區 首頁 教程 查看內容

04年湖人vs火箭:微信小程序WebSocket實踐

湖人vs爵士季后赛 www.nbgeh.club Rolan 2019-6-5 00:23

微信基礎庫1.7.0之后的版本提供了新版的WebSocket API,考慮到兼容性問題,嘗試整合新舊兩種版本的API,進行簡單的封裝。從以下幾個角度對微信小程序中所提供的WebSocket API封裝API兼容性重連機制小程序WebSocket A ...

微信基礎庫1.7.0之后的版本提供了新版的WebSocket API,考慮到兼容性問題,嘗試整合新舊兩種版本的API,進行簡單的封裝。

從以下幾個角度對微信湖人vs爵士季后赛中所提供的WebSocket API封裝

  • API兼容性
  • 重連機制

小程序WebSocket API

舊版WebSocket API的使用

  • 創建 - wx.connectSocket
  • 發送消息 - sendSocketMessage
  • 監聽事件 - onSocketOpen
let socketOpen = false
const socketMsgQueue = []
// 連接socket
wx.connectSocket({
  url: 'test.php'
})
// 監聽打開事件
wx.onSocketOpen(function(res) {
  socketOpen = true
  for (let i = 0; i < socketMsgQueue.length; i++){
    sendSocketMessage(socketMsgQueue[i])
  }
  socketMsgQueue = []
})
// 發送消息
function sendSocketMessage(msg) {
  if (socketOpen) {
    wx.sendSocketMessage({
      data:msg
    })
  } else {
    socketMsgQueue.push(msg)
  }
}

新版WebSocket API的使用

支持1.7.0+,連接后會返回一個SocketTask對象,在該對象上監聽該連接的各種事件與執行發送消息等操作。

let socketTask = wx.connectSocket({
  url: 'test.php'
})
// 監聽打開事件
socketTask.onOpen(function(res) {
  socketOpen = true
  // 發送信息
  socketTask.send({msg: "hello world"})
})

并發數

  • 1.7.0 及以上版本,最多可以同時存在 5 個 WebSocket 連接。
  • 1.7.0 以下版本,一個小程序同時只能有一個 WebSocket 連接,如果當前已存在一個 WebSocket 連接,會自動關閉該連接,并重新創建一個 WebSocket 連接。

??櫸庾?/h2>

簡單封裝封裝一個兼容新舊socketAPI的???,僅考慮存在單個socket連接的情況

創建與事件監聽

  • 新版: 在socket連接時會返回一個socketTask對象,監聽事件是在該對象的基礎上進行
  • 舊版: 直接使用wx放進行創建與監聽方法

統一創建與添加監聽函數

init() {
  let st = this.connect()
  this.listen(st)
  this.manualClose = false
}

創建連接

connect() {
  let st = wx.connectSocket(this.config.server)
  return st
}

添加事件監聽函數

listen(st) {
  if (st !== undefined) {
    this.ws = st
    this.ws.onOpen(() => this.openHandle())
    this.ws.onError(() => this.errorHandle())
    this.ws.onClose(() => this.closeHandle())
    this.ws.onMessage(res => this.messageHandle(res))
  } else {
    wx.onSocketOpen(() => this.openHandle())
    wx.onSocketError(() => this.errorHandle())
    wx.onSocketClose(() => this.closeHandle())
    wx.onSocketMessage(res => this.messageHandle(res))
  }
}

重連機制

預設標記位

retryLock = false; // 避免多次同時重連
socketOpen = false; // 連接狀態
manualClose = false; // 主動斷開標記

在連接關閉監聽函數中執行重連

closeHandle() {
  console.info('WebSocket closed')
  this.socketOpen = false
  this.retryLock = false
  // 不論是error還是close都會觸發close事件,統一在這里進行重連
  // 初次連接失敗不進行重連(失敗不會進入到onOpen的監聽事件中,那時未聲明retryTimes變量)
  this.retryTimes !== undefined && this.reconnect()
}

判斷重連鎖與是否主動斷開進行重連

reconnect() {
  if (this.retryLock) return
  this.retryLock = true
  // 若manualClose為true,表明不是主動斷開
  if (!this.manualClose) {
    // 開始重連
    setTimeout(() => {
      this.retry()
    }, this.retryInterval)
  }
}

重連函數,包含重連次數的限制

retry() {
  if (
    this.socketOpen ||
    (this.retryTimes > 0 && this.retryCount <= this.retryTimes)
  ) {
    console.warn(`reconnect ending. reconnect ${this.retryTimes} times`)
    if (!this.socketOpen) {
      this.config.closeCallback()
    }
    return
  }
  this.retryTimes += 1
  console.warn(`[ ${this.retryTimes} ]th reconnect WebSocket...`)
  this.init()
}

消息隊列

添加消息隊列,當重連后自動發送緩存消息

openHandle() {
  this.retryTimes = 0
  this.socketOpen = true
  this.retryLock = false

  this.messageQueue.map(e => this.send(e))
  this.messageQueue = []
}

若發送時斷開則先將消息緩存到消息隊列中

send(value) {
  let data = this.msgWrapper(value)
  data = JSON.stringify(data)
  if (!this.socketOpen) {
    this.messageQueue.push(data)
  } else {
    if (this.ws) {
      this.ws.send({ data })
    } else {
      wx.sendSocketMessage({ data })
    }
  }
}

輔助函數

添加一些包裹消息格式的工具函數

messageIndex = 0;
helper = {
  isPlainObject: val =>
    Object.prototype.toString.call(val) === '[object Object]',
  nextId: () => {
    this.messageIndex += 1
    return this.messageIndex
  },
  id: () => Date.now() + '.' + this.helper.nextId()
};
msgWrapper(data) {
  let msg = data
  if (this.helper.isPlainObject(msg)) {
    if (msg.type) {
      return msg
    } else {
      return this.msgWrapper({ type: 'message', msg, id: this.helper.id() })
    }
  } else {
    return this.msgWrapper({ type: 'message', msg, id: this.helper.id() })
  }
}

完整代碼

export default class WXWebSocket {
  messageQueue = []; // 消息隊列
  retryLock = false; // 避免多次同時重連
  socketOpen = false;
  manualClose = false; // 主動斷開標記
  constructor(config) {
    this.config = config || {}
    // 重連間隔
    this.retryInterval =
      this.config.retryInterval && this.config.retryInterval > 100
        ? this.config.retryInterval
        : 3000
    // 重連次數
    this.retryCount = this.config.retryCount || 5
    this.init()
  }
  init() {
    let st = this.connect()
    this.listen(st)
    this.manualClose = false
  }
  connect() {
    let st = wx.connectSocket(this.config.server)
    console.log('current socket: ', st)
    return st
  }
  listen(st) {
    // 添加監聽事件
    if (st !== undefined) {
      // 若存在SocketTask,則要通過readyState判斷狀態
      // CONNECTING: 0
      // OPEN: 1
      // CLOSING: 2
      // CLOSE: 3
      this.ws = st
      this.ws.onOpen(() => this.openHandle())
      this.ws.onError(() => this.errorHandle())
      this.ws.onClose(() => this.closeHandle())
      this.ws.onMessage(res => this.messageHandle(res))
    } else {
      wx.onSocketOpen(() => this.openHandle())
      wx.onSocketError(() => this.errorHandle())
      wx.onSocketClose(() => this.closeHandle())
      wx.onSocketMessage(res => this.messageHandle(res))
    }
  }
  close() {
    this.manualClose = true
    if (this.ws) {
      this.ws.close()
    } else {
      wx.closeSocket()
    }
  }
  send(value) {
    console.log('send value: ', value)
    let data = this.msgWrapper(value)
    data = JSON.stringify(data)
    if (!this.socketOpen) {
      // add new message to queue
      this.messageQueue.push(data)
    } else {
      if (this.ws) {
        this.ws.send({ data })
      } else {
        wx.sendSocketMessage({ data })
      }
    }
  }
  openHandle() {
    console.info('WebSocket connected')
    this.retryTimes = 0
    this.socketOpen = true
    this.retryLock = false

    this.messageQueue.map(e => this.send(e))
    this.messageQueue = []
  }
  errorHandle() {
    console.error('WebSocket error')
    this.socketOpen = false
  }
  closeHandle() {
    console.info('WebSocket closed')
    this.socketOpen = false
    this.retryLock = false
    // 不論是error還是close都會觸發close事件,統一在這里進行重連
    // 初次連接失敗不進行重連(失敗不會進入到onOpen的監聽事件中,那時未聲明retryTimes變量)
    this.retryTimes !== undefined && this.reconnect()
  }
  reconnect() {
    if (this.retryLock) return
    this.retryLock = true
    // 若manualClose為true,表明不是主動斷開
    if (!this.manualClose) {
      // 開始重連
      setTimeout(() => {
        this.retry()
      }, this.retryInterval)
    }
  }
  retry() {
    if (
      this.socketOpen ||
      (this.retryTimes > 0 && this.retryCount <= this.retryTimes)
    ) {
      console.warn(`end reconnect. reconnect ${this.retryTimes} times`)
      if (!this.socketOpen) {
        this.config.closeCallback()
      }
      return
    }
    this.retryTimes += 1
    console.warn(`[ ${this.retryTimes} ]th reconnect WebSocket...`)
    this.init()
  }
  messageHandle(res) {
    this.config.responseCallback(res)
  }
  msgWrapper(data) {
    let msg = data
    if (this.helper.isPlainObject(msg)) {
      if (msg.type) {
        return msg
      } else {
        return this.msgWrapper({ type: 'message', msg, id: this.helper.id() })
      }
    } else {
      return this.msgWrapper({ type: 'message', msg, id: this.helper.id() })
    }
  }
  messageIndex = 0;
  helper = {
    isPlainObject: val =>
      Object.prototype.toString.call(val) === '[object Object]',
    nextId: () => {
      this.messageIndex += 1
      return this.messageIndex
    },
    id: () => Date.now() + '.' + this.helper.nextId()
  };
}

使用

創建連接

let socketTask = new WXWebSocket({
  server: this.wsServerOption,
  responseCallback: e => {
    let { data } = e
    let { msg } = JSON.parse(data)
    this.msgStack.push(msg)
  },
  closeCallback: () => {
    this.socketTask = null
  }
})
this.socketTask = socketTask

發送消息

sendWSMessage(msg) {
  this.msgStack.push(msg)
  this.socketTask && this.socketTask.send(msg)
},

關閉連接

closeWS() {
  if (!this.socketTask) return
  if (this.socketTask.socketOpen) {
    this.socketTask.close()
    this.socketTask = null
  }
}
分享至 : QQ空間
收藏
原作者: Я?1I0 來自: Я?1I0