webSocket学习与使用

8 篇文章 0 订阅

1.webSocket的简介

WebSocket 可以保持着浏览器和客户端之间的长连接, 通WebSocket 可以实现数据由后端推送到前端,保了数据传输的实时性。

2.后端代码的改造(以koa2为例)

koa2博客

  • 安装 WebSocket 包

    npm i ws -S

  • 创建 WebSocket 实例对象

 const WebSocket = require("ws") 
  // 创建出WebSocket实例对象 
  const wss = new WebSocket.Server({ 
  ​      port: 9998 
  })
  • 监听事件

    demo

wss.on("connection", client => { 
  ​	console.log("有客户端连接...") 
  ​	client.on("message", msg => { 
  ​		console.log("客户端发送数据过来了")// 发送数据给客户端 
  ​	client.send('hello socket')}) 
  })

现实使用的

	web_socket_service.js

  const path = require('path')
  const fileUtils = require('../utils/file_utils')
  const WebSocket = require('ws')
  // 创建WebSocket服务端的对象, 绑定的端口号是9998
  const wss = new WebSocket.Server({
    port: 9998
  })
  // 服务端开启了监听
  module.exports.listen = () => {
    // 对客户端的连接事件进行监听
    // client:代表的是客户端的连接socket对象
    wss.on('connection', client => {
      console.log('有客户端连接成功了...')
      // 对客户端的连接对象进行message事件的监听
      // msg: 由客户端发给服务端的数据
      client.on('message',async msg => {
        console.log('客户端发送数据给服务端了: ' + msg)
        let payload = JSON.parse(msg)
        const action = payload.action
        if (action === 'getData') {
          let filePath = '../data/' + payload.chartName + '.json'
          // payload.chartName // trend seller map rank hot stock
          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(msg)
          })
        }
        // 由服务端往客户端发送数据
        // client.send('hello socket from backend')
      })
    })
  }

  app.js

  // 服务器的入口文件
  // 1.创建KOA的实例对象
  const Koa = require('koa')
  const app = new Koa()
  // 2.绑定中间件
  // 绑定第一层中间件
  const respDurationMiddleware =  require('./middleware/koa_response_duration')
  app.use(respDurationMiddleware)
  // 绑定第二层中间件
  const respHeaderMiddleware = require('./middleware/koa_response_header')
  app.use(respHeaderMiddleware)
  // 绑定第三层中间件
  const respDataMiddleware = require('./middleware/koa_response_data')
  app.use(respDataMiddleware)
  // 3.绑定端口号 8888
  app.listen(8888)

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

  <!DOCTYPE html>
  <html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
  </head>
  <body>
    <button id="connect">连接</button>
    <button id="send" disabled="true">发送数据</button> <br>
    从服务端接收的数据如下: <br>
    <span id="recv"></span>
    <script>
      var connect = document.querySelector('#connect')
      var send = document.querySelector('#send')
      var 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(JSON.stringify({
          action: 'themeChange',
          socketType: 'themeChange',
          chartName: '',
          value: 'chalk'
        }))
      }
    </script>
  </body>
  </html>

3.前端响应(vue实例案例)

  • 创建 src/utils/socket_service.js 文件
  • 定义单例
  - export default class SocketService { 
    /**
    \* 单例 
    */ 
	    static instance = null 
	    static get Instance () { 
		    if (!this.instance) { 
		    	this.instance = new SocketService() 
		    }
	    return this.instance 
	    } 
    }
  • 监听 WebSocket 事件
    • 定义 connect 函数,将创建的 WebSocket 赋值给实例属性
export default class SocketService { 
...... 
	// 实例属性 
	ws = null 
	// 初始化连接websocket 
	connect () { 
		if (!window.WebSocket) { 
			return console.log('您的浏览器不支持 WebSocket!') 
		}
		this.ws = new WebSocket('ws://localhost:9998') 
	} 
}
  • 监听事件
  connect () { 
	  if (!window.WebSocket) { 
	  	return console.log('您的浏览器不支持 WebSocket!') 
	  }
	  this.ws = new WebSocket('ws://localhost:9998') 
	  // 监听连接成功 
	  this.ws.onopen = () => { 
	  	console.log('WebSocket 连接成功') 
	  }
	  // 1.服务器连接不成功 2.服务器关闭了连接 
	  this.ws.onclose = e => { 
	  	console.log('服务器关闭了连接') 
	  }
	  // 监听接收消息 
	  this.ws.onmessage = msg => { 
	 	 console.log('WebSocket 接收到数据') 
	  } 
  }
  • 定义注册函数

    记录一下当得到数据时, 应该调用的函数回调

export default class SocketService { 
	  // 业务类型和回调函数的对于关系 
	  callBackMapping = {} 
	  /**
	
	  \* socketType 
	
	  \* trendData sellerData mapData rankData hotData stockData 
	
	  \* fullScreen 
	
	  \* themeChange 
	
	  \* callBack 
	
	  \* 回调函数 
	
	  */ 
	  registerCallBack (socketType, callBack) { 
	  // 往 callBackMap中存放回调函数 
	  	this.callBackMapping[socketType] = callBack 
	  }
	  unRegisterCallBack (socketType) { 
	  	this.callBackMapping[socketType] = null 
	  } 
  } 
  • 连接服务端

    • 在 main.js 中连接服务器端
import SocketService from '@/utils/socket_service' 
SocketService.Instance.connect() 
  • 将 SocketService 实例对象挂载到 Vue 的原型对象上
Vue.prototype.$socket = SocketService.Instance 
  • 发送数据给服务端

    • 在 socket_service.js 中定义发送数据的方法
  export default class SocketService { 
	    ...... 
	    send (data) { 
	   		 console.log('发送数据给服务器:') 
	    this.ws.send(JSON.stringify(data)) 
	
	    } 
    }
  • vue组件注册回调函数
 created () { 
    // 当socket来数据的时候, 会调用getData这个函数 
    	this.$socket.registerCallBack('trendData', this.getData) 
    } 
  • destroyed 中取消注册
 destroyed () { 
    	this.$socket.unRegisterCallBack('trendData') 
    } 
  • mounted 中往 socket 发送数据
  mounted () { 
	    this.initChart() 
	    // this.getData() 先将getData的调用注释起来 
	    this.$socket.send({ 
		    action: 'getData', 
		    socketType: 'trendData', 
		    chartName: 'trend' 
	    })
	    ...... 
    }, 
  • 运行代码, 发现数据发不出去
    因为在刷新界面之后, 客户端和服务端的连接并不会立马连接成功, 在处于连接状态下就调用,send 是发送不成功的, 因此需要修改 service_socket.js 中的 send 方法进行容错处理 。
  // 是否已经连接成功 
    connected = false 
    sendRetryCount = 0 
    send (data) { 
	    console.log('发送数据给服务器:') 
	    if (this.connected) { 
		    this.sendRetryCount = 0 
		    this.ws.send(JSON.stringify(data)) 
	    } else { 
		    setTimeout(() => { 
		    this.sendRetryCount++ 
		    this.send(data) 
	    	}, 200 * this.sendRetryCount) // 发送数据尝试的次数越大, 则下一次连接的 
    延迟也就越长 
    	} 
    } 
    在 onopen 时设置 connected 的值
    connect () { 
	    ...... 
	    this.ws.onopen = () => { 
		    console.log('WebSocket 连接成功') 
		    this.connected = true 
	    } 
    }

    在 socket_service.js 中修改接收到消息的代码处理
    connect () { 
	    // 监听接收消息 
	    this.ws.onmessage = msg => { 
		    console.log('WebSocket 接收到数据') 
		    const recvData = JSON.parse(msg.data) // 取出服务端传递的数据 
		    const socketType = recvData.socketType // 取出业务类型,要根据业务类 型,得到回调函数 
		    // 先判断有没有回调函数 
		    if (this.callBackMapping[socketType]) { 
			    if (recvData.action === 'getData') { 
				    const realData = recvData.data // 得到该图表的数据 
				    this.callBackMapping[socketType].call(this, 
				    JSON.parse(realData)) 
			    } 
	    } 
    } 
  • 断开重连机制
    如果初始化连接服务端不成功, 或者连接成功了, 后来服务器关闭了, 这两种情况都会触发 onclose 事件, 我们需要在这个事件中,进行重连 。
    connectRetryCount = 0 // 重连次数, 重连次数越大, 下一次再发起重连的延时也就越长
connect () { 
    this.ws.onopen = () => { 
	    ...... 
	    this.connectRetryCount = 0 // 连接成功之后, 重置重连次数 
	}
	...... 
	// 1.服务器连接不成功 2.服务器关闭了连接 
	this.ws.onclose = e => { 
		console.log('服务器关闭了连接') 
		setTimeout(() => { 
			this.connectRetryCount++ 
			this.connect() 
		}, 200 * this.connectRetryCount) 
	} 
}

完整前端案例
app.js

import SocketService from '@/utils/socket_service'
// 对服务端进行websocket的连接
SocketService.Instance.connect()
// 其他的组件  this.$socket
Vue.prototype.$socket = SocketService.Instance

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
    }
    // 1.连接服务端失败
    // 2.当连接成功之后, 服务器关闭的情况
    this.ws.onclose = () => {
      console.log('连接服务端失败')
      this.connected = false
      this.connectRetryCount++
      setTimeout(() => {
        this.connect()
      }, 500 * this.connectRetryCount)
    }
    // 得到服务端发送过来的数据
    this.ws.onmessage = msg => {
      console.log('从服务端获取到了数据')
      // 真正服务端发送过来的原始数据时在msg中的data字段
      // console.log(msg.data)
      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') {
          this.callBackMapping[socketType].call(this, recvData)
        } else if (action === 'themeChange') {
          this.callBackMapping[socketType].call(this, recvData)
        }
      }
    }
  }

  // 回调函数的注册
  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)
    }
  }
}

组件使用

created () {
   // 在组件创建完成之后 进行回调函数的注册
   this.$socket.registerCallBack('hotData', this.getData)
 },
 mounted () {
    this.$socket.send({
      action: 'getData',
      socketType: 'hotData',
      chartName: 'hot',
      value: ''
    })
  },
  destroyed () {
    window.removeEventListener('resize', this.screenAdapter)
    this.$socket.unRegisterCallBack('hotData')
  },
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值