当前位置:网站首页>webSocket学习与使用
webSocket学习与使用
2022-07-20 04:54:00 【小叶_01】
1.webSocket的简介
WebSocket 可以保持着浏览器和客户端之间的长连接, 通WebSocket 可以实现数据由后端推送到前端,保了数据传输的实时性。
2.后端代码的改造(以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')
},
边栏推荐
- fiddler5+雷电模拟器4.0对app抓包设置
- [AD learning record] Why are schematic diagrams and PCBs in the same folder, and PCB cannot be generated?
- 从概念到安全实践:软件供应链基础指南
- How should enterprise users choose aiops or APM?
- Love to learn blog - life is short, only good classes!
- Calculate the number of days from today
- AI2 (APP inventor 2) offline version
- Reverse generation of crud code using Renren generator
- 虚拟主机代理商需要注意什么?
- Monomer or microservice? You think it's an architectural tradeoff? It's actually a cognitive load!
猜你喜欢
Zhimeng adds a column and prompts "failed to save the catalog data, please check whether there is a problem with your input data" when modifying the column
Relevant characteristics of TCP protocol
无密码身份验证如何保障用户隐私安全?
Office software cannot start normally 0xc0000142 how to solve it?
织梦添加栏目修改栏目时提示“保存目录数据时失败,请检查你的输入资料是否存在问题”
[Android开发学iOS系列] 语言篇: Swift vs Kotlin
Websocket服务器代码协议解析,学会自己做协议思路。
Reverse generation of crud code using Renren generator
Jenkins Automated Deployment
How should enterprise users choose aiops or APM?
随机推荐
老域名建站有什么优势?
How to choose data application development language and environment
Buying insight and channel evaluation help optimize marketing decisions
Software testing career development direction (don't be confused, roll up quickly)
Partial voice feature recording
HMS core audio editing service supports 7 kinds of audio effects to help one-stop audio processing
【模板引擎】微服务学习笔记六:freemarker
[wechat applet] text field input with maximum word limit (1/100)
Record the uni app and package it into APK to get the location
About Variables
Difi: a go as you pay Wi Fi access system intensive reading notes (II)
Swagger重点配置项
Password key hard coding check
[Android development IOS series] Language: swift vs kotlin
无密码身份验证如何保障用户隐私安全?
Software interface and simple system simulation
Why should Apple change objc
Get the date of the year (number in the range 1-366) date from the object
AI2 (APP inventor 2) offline version
织梦添加栏目修改栏目时提示“保存目录数据时失败,请检查你的输入资料是否存在问题”