后端代码改造
创建 service\web_socket_service.js 文件
- 客户链接成功 转义msg 成对象 判断 msg.action 是不是getData
- 是的话 拼接路径名 使用工具函数 获得 ret 赋值给 payload.data 转成json 返回给客户端
- 不是的话 原封不动的将从某一个客户端发过来的数据转发给每一个处于连接状态
的客户端
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)
// 根据路径找到 json 数据 传出去
payload.data = ret
client.send(JSON.stringify(payload))
}else {
// 不是getDta 的话就原封不动的将数据转发给每一个处于链接状态的客户端 根据value 变更主图或者 全屏切换 wss.clients 所有客户端的链接
wss.clients.forEach(client => {
client.send(msg)
})
}
// 由服务器往客户端发送数据
//client.send('hello socket from 后端')
})
})
}
在app.js 中引入
在
app.js
中引入
web_socket_service.js
这个文件
,
并调用
listen
方法
// 服务端入口文件
// 1. 创建KOA的实例对象
const Koa = require('koa')
const app = new Koa()
// 2.绑定中间件
// 第一层 响应时间的中间件
const resDuration = require('./middleware/response_duration')
app.use(resDuration)
// 第二层 设置响应头的中间件
const resHeader = require('./middleware/response_header')
app.use(resHeader)
// 第三层 处理业务逻辑的中间件
const resData = require('./middleware/response_data')
app.use(resData)
// 3.绑定端口号
app.listen(2903)
const webSocketService = require('./service/web_socket_service')
// 开启服务器的监听,监听客户端的链接
// 当某一个客户端连接成功之后,就会对这个客户端进行message 事件的监听
webSocketService.listen()
前端代码的改造
定义单例,创建 WebSocket 实例对象
创建
src/utils/socket_service.js
文件
定义单例
- 类只有一个实例
- 全局可访问
- 推迟初始化(与静态类,对象的区别)
- 主动实例化
- 例如Vuex
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')
// 监听连接成功
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中连接服务端
在 main.js 中连接服务器端 import SocketService from "@/utils/socket_service"; // 对服务器进行webSocket的链接 SocketService.Instance.connect()// 原型上挂载 this.$socket Vue.prototype.$socket = SocketService.Instance
发送数据给服务端
export default class SocketService {
......
send (data) {
console.log('发送数据给服务器:')
this.ws.send(JSON.stringify(data))
}
}
客户端和服务端的连接并不会立马连接成功
,
在处于连接状态下就调用
send
是发送不成功的
,
因此需要修改
service_socket.js
中的
send
方法进行容错处理
新增一些变量 用于标识控制
// 和服务器端链接的socket对象 ws = null // 储存回调函数 callBackMapping = {} // 标识是否链接成功 connected = false // 记录重试的次数 sendRetryCount = 0 // 重新尝试链接的次数 connectRetryCount = 0
断开重连机制
如果初始化连接服务端不成功
,
或者连接成功了
,
后来服务器关闭了
,
这两种情况都会触发
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)
}
}
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.send(data)
},500 * this.connectRetryCount)
}
// 得到服务器发送过来的数据
this.ws.onmessage = msg => {
console.log('从服务器获取到了数据')
// 真正的服务端发送过来的原始数据在msg中的data字段
//console.log(msg.data)
const recData = JSON.parse(msg.data)
const socketType = recData.socketType
// 判断回调函数是否存在
if(this.callBackMapping[socketType]){
const action = recData.action
if (action === 'getData'){
const realData = JSON.parse(recData.data)
this.callBackMapping[socketType].call(this,realData)
}else if (action === 'fullScreen'){
this.callBackMapping[socketType].call(this,recData)
}else if (action === 'themeChange'){
this.callBackMapping[socketType].call(this,recData)
}
}
}
}
// 回调函数的注册
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)
},500 * this.sendRetryCount)
}
}
}