WebSocket的引入

目录

1.WebSocket的基本使用

后端

安装包:npm i -S ws
创建对象
const WebSocket = require('ws')
const wss = new WebSocket.Server({
    port: 9998
})
监听事件
连接事件
wss.on('connection', client => {
})
接收数据事件
wss.on('connection', client => {
     client.on('message', msg => {
     })
})
发送数据 client.send() 

前端

创建对象:const ws = new WedSocket('ws://localhost:9998') WebSocket是window对象就提供了的,因此不需要额外的包 

监听事件
连接成功事件:ws.onopen
接收数据事件:ws.onmessage
关闭连接事件:ws.onclose
发送数据:ws.send  

websocket_demo.html 

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title></title>
  </head>
  <body>
    <button id="connect">连接</button>
    <button id="send" disabled>发送数据</button><br>
    从服务端接收的数据如下:<br>
    <span id="recv"></span>
    <script>
      let connect = document.querySelector('#connect')
      let send = document.querySelector('#send')
      let recv = document.querySelector('#recv')
      let ws = null
      connect.onclick = function() {
        ws = new WebSocket('ws://localhost:9998')
        ws.onopen = () => {
          console.log('连接服务器端成功了...')
          send.disabled = false
        }
        ws.onclose = () => {
          console.log('连接服务器失败')
          send.disabled = true
        }
        ws.onmessage = msg => {
          console.log('接收到从服务端发送过来的数据了')
          console.log(msg)
          recv.innerHTML = msg.data
        }
      }
      send.onclick = function() {
        ws.send('hello socket from frontend')
      }
    </script>
  </body>
</html>

 app.js

const WebSocket = require('ws')
// 创建WebSocket服务器的对象,绑定9998端口号
const wss = new WebSocket.Server({
  port: 9998
})
// 对客户端的连接事件进行监听
// client:代表的是客户端的连接socket对象
wss.on('connection', client => {
  console.log('有客户端连接成功了...')
  // 对客户端的连接对象进行message事件的监听
  // msg: 由客户端发给服务端的数据
  client.on('message', msg => {
    console.log('客户端发送数据给服务端:' + msg)
    // 由服务端往客户端发送数据
    client.send('hello socket from backend')
  })
})

2.使用WebSocket改造项目 

后端工程 

创建web_socket_service.js
创建Socket服务端对象,绑定端口 
监听事件:connetion、message
将监听事件的代码放到一个函数中,并将这个函数导出

服务端接收数据字段的约定

{
  "action": "getData",
  "socketType": "trendData",
  "chartName": "trend",
  "value": "",
}
action: 代表某项行为,可选值有getData(获取图表数据)、fullScreen(全屏事件)、themeChange(主题切换事件)
socketType: 代表业务模块类型、代表前端响应函数的标识,可选值有trendData、sellerData、mapData、rankData、hotData、stockData、fullScreen、themeChange
chartName: 代表图表名称,可选值有trend、seller、map、rank、hot、stock,如果是主题切换事件,可不传此值

服务端发送数据字段的约定

接收到action为getData时:取出数据中的chartName字段、拼接json文件的路径、读取该文件的内容、在接收到数据的基础之上,增加data字段(值是从文件中读出的内容)

接收到action不为getData时:原封不动地将从客户端接收到的数据,并转发给每一个处于连接状态的客户端

app.js

... // 原代码不变

const WebSocketService = require('./service/web_socket_service')
// 开启服务器的监听 ,监听客户端的连接
// 当某一个客户端连接成功之后,就会对这个客户端进行message事件的监听
WebSocketService.listen()

service/web_socket_service.js 

const path = require('path')
const fileUtils = require('../utils/file_utils')
const WebSocket = require('ws')

const wss = new WebSocket.Server({
  port: 9998
})
// 服务端开启了监听
module.exports.listen = () => {
  wss.on('connection', client => {
    console.log('有客户端连接成功了...')
    client.on('message', async msg => {
      console.log('客户端发送数据给服务端:' + msg)
      let payload = JSON.parse(msg)
      const action = payload.action
      if (action === 'getData') {
        // payload.chartName可能的值:trend seller map rank hot stock
        let filePath = '../data/' + payload.chartName + '.json'
        filePath = path.join(__dirname, filePath)
        const ret = await fileUtils.getFileJsonData(filePath)
        // 需要在服务端获取到数据的基础之上,增加一个data的字段
        // data所对应的值,就是某个json文件的内容
        payload.data = ret
        client.send(JSON.stringify(payload))
      } else {
        // 原封不动地将所接收到的数据转发给每一个处于连接状态的客户端
        // wss.clients 所有客户端的连接
        wss.clients.forEach(client => {
          client.send(JSON.stringify(payload))
        })
      }
    })
  })
}

前端工程 

创建socket_service.js

定义类SocketService,并定义成单例设计模式
定义连接服务器的方法connect(创建WebSocket对象,对服务器进行连接,在main.js中调用此方法)
监听事件: onopen、onmessage、onclose
存储回调函数:callBackMapping={}、注册回调函数registerCallBack(socketType,callBack)、取消回调函数unRegisterCallBack(socketType)
接收数据的处理:onmessage 调用之前存储的回调函数,传递数据
定义发送数据的方法:send()
挂载SocketService对象到Vue的原型对象上,方便各个组件使用这个对象中的方法

组件的改造

created: 注册回调函数
destoryed: 取消回调函数
在原来获取数据的地方改为发送数据,数据的格式需要满足约定形式 

components/trend.vue(其它5个组件修改同理)

... //原代码不变

<script>
export default {
  data () {
  },
  created () {
    // 在组件创建完成之后进行回调函数的注册
    this.$socket.registerCallBack('trendData', this.getData)
  },
  mounted () {
    this.initChart()
    // 发送数据给服务器,告诉服务器我现在需要数据
    this.$socket.send({
      action: 'getData',
      socketType: 'trendData',
      chartName: 'trend',
      value: ''
    })
    window.addEventListener('resize', this.screenAdapter)
    this.screenAdapter()
  },
  destoryed () {
    window.removeEventListener('resize', this.screenAdapter)
    this.$socket.unRegisterCallBack('trendData')
  },
  computed: {
  },
  methods: {
    ... //原代码不变
    // ret 就是服务端发送给客户端的图表数据
    getData (ret) {
      this.allData = ret
      this.updateChart()
    }
    ... //原代码不变
  }
}
</script>
... //原代码不变

优化

重发数据机制:增加实例属性connected(默认值为false,onopen时设置为true、onclose时设置为false)、发送数据时需要判断connected(true直接发送、false延时发送,延时的时间随着尝试次数的增加而增加)
断开重连机制:onclose时,延时尝试连接服务器,
延时的时间随着尝试次数的增加而增加

utils/socket_service.js

export default class SocketService {
  // 单例模式
  static instance = null
  static get Instance () {
    if (!this.instance) {
      this.instance = new SocketService()
    }
    return this.instance
  }

  // 和服务端连接的socket对象
  ws = null
  // 存储回调函数
  callBackMapping = {}
  // 标识是否连接成功
  connected = false
  // 记录重试的次数
  sendRetryCount = 0
  // 重新尝试连接的次数
  connectRetryCount = 0

  // 定义连接服务器的方法
  connect () {
    if (!window.WebSocket) {
      return console.log('您的浏览器不支持WebSocket')
    }
    this.ws = new WebSocket('ws://localhost:9998')

    // 连接成功的事件
    this.ws.onopen = () => {
      console.log('连接服务器成功了')
      this.connected = true
      // 重置重新连接的次数
      this.connectRetryCount = 0
    }
    // 连接服务器失败
    this.ws.onclose = () => {
      console.log('连接服务器失败')
      this.connected = false
      this.connectRetryCount++
      setTimeout(() => {
        this.connect()
      }, this.connectRetryCount * 500)
    }
    // 得到服务器发送过来的数据
    this.ws.onmessage = msg => {
      console.log('从服务器获取到了数据')
      const recvData = JSON.parse(msg.data)
      const socketType = recvData.socketType
      // 判断回调函数是否存在
      if (this.callBackMapping[socketType]) {
        const action = recvData.action
        if (action === 'getData') {
          const realData = JSON.parse(recvData.data)
          this.callBackMapping[socketType].call(this, realData)
        } else if (action === 'fullScreen') {

        } else if (action === 'themeChange') {

        }
      }
    }
  }

  // 回调函数的注册
  registerCallBack (socketType, callBack) {
    this.callBackMapping[socketType] = callBack
  }

  // 取消某一个回调函数
  unRegisterCallBack (socketType) {
    this.callBackMapping[socketType] = null
  }

  // 发送数据的方法
  send (data) {
    if (this.connected) {
      this.sendRetryCount = 0
      this.ws.send(JSON.stringify(data))
    } else {
      this.sendRetryCount++
      setTimeout(() => {
        this.send(data)
      }, this.sendRetryCount * 500)
    }
  }
}

main.js 

... //原代码不变

import SocketService from './utils/socket_service'
// 对服务端进行websocket的连接
SocketService.Instance.connect()
// 其它组件可通过this.$socket来调用
Vue.prototype.$socket = SocketService.Instance

... //原代码不变
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

漂流の少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值