websocket: 了解并利用nodejs实现webSocket前后端通信

目录

第一章 前言

1.1 起源

1.2 短轮询与长轮询

1.2.1 短轮询

1.2.2 长轮询

1.2.3 长连接(SSE)

1.2.4 websocket

第二章 利用Node以及ws创建webSocket服务器

2.1 创建ws服务器(后端部分)

2.1.1 了解一下

2.1.2 代创建WebSocket服务器

2.1.3 监听前端连接websocket(ws.on的connection事件)

2.1.4 服务器接收数据(ws.on的message事件)

2.1.5 服务器发送数据(ws.send()方法)

2.1.6 关闭服务器(ws.on的close事件)

2.2 创建后台服务器(后端部分)

第三章 前端使用websocket的方法

3.1 常用的使用方法(前端部分)

3.1.1 代码引入

3.1.2 引入ws

3.1.3 打开ws连接(ws.onopen)

3.1.4 连接错误(ws.onerror)

3.1.5 前端接收服务器的数据(ws.onmessage)

3.1.6 关闭连接(ws.onclose)

第四章 效果展示

提示

源代码


第一章 前言

1.1 起源

在 Web 开发领域,我们最常用的协议是 HTTP,HTTP 协议和 WS 协议都是基于 TCP 所做的封装,但是 HTTP 协议它是从一开始便被设计成请求 -> 响应的模式,所以在很长一段时间内 HTTP 都是只能从客户端发向服务端并不具备从服务端主动推送消息的功能,这也导致在浏览器端想要做到服务器主动推送的效果只能用一些轮询和长轮询的方案来做,但因为它们并不是真正的全双工,所以在消耗资源多的同时,实时性也没理想中那么好。

1.2 短轮询与长轮询

1.2.1 短轮询

  • 解释:前端(客户端)利用循环定时器不断的向后台做HTTP请求(会产生多个HTTP请求),后台(服务端)接到请求后返回响应信息的一种方式
  • 适用于:适用于小型应用,或者同时在线人数较少的应用
  • 优点:简单省时,后端程序编写比较容易(几乎不用做什么特殊处理)
  • 缺点:不及时(得看定时器的间隔),消耗大 ( 服务器宽带和资源)

1.2.2 长轮询

  • 解释:长轮询只启动一个HTTP请求,其连接的服务端会挂起此次连接,后端定时器去查询数据库有没有新消息,直到有新消息才返回响应信息客户端处理完响应信息后再向服务器发送新的Http请求,以此类推。区别于轮询的就是没有新消息就不会发送新的请求
  • 适用于:适用于小型应用,或者同时在线人数较少的应用
  • 优点:可实现实时数据回传,长轮询和轮询比起来,明显减少了很多不必要的http请求次数,相比之下节约了资源。
  • 缺点:连接挂起也会导致资源的浪费(服务器压力大,频繁操作询问数据库有没有新结果)

1.2.3 长连接(SSE)

  • SSE是HTML5新增的功能,SSE(sever-sent events)服务器端推送事件,是指服务器推送数据给客户端,而不是传统的请求响应模式。简单的说,就是浏览器向服务器发送一个HTTP请求然后服务器不断单向地向浏览器推送“信息”而SSE最大的特点就是可以实现只要服务器端数据有更新,就可以马上发送到客户端。

1.2.4 websocket

  • websocket 最大的特点就是可以全双工的双向通信
  • 全双工:全双工是指两方能同时发送和接收数据
  • 半双工:半双工是指传输过程中只能向一个方向传输
  • 传输的消息类型:
  1. 文本消息(string)
  2. 二进制消息
  3. 分片消息(分片消息代表此消息是一个某个消息中的一部分,想想大文件分片)
  4. 连接关闭消息
  5. PING 消息
  6. PONG 消息(PING的回复就是PONG)

WebSocket 教程 - 阮一峰的网络日志

第二章 利用Node以及ws创建webSocket服务器

2.1 创建ws服务器(后端部分)

2.1.1 了解一下

  • Node.js原生API没有提供对WebSocket的支持,需要安装第三方包ws才能使用WebSocket功能
  • ws模块:是一个用于支持WebSocket客户端和服务器的框架。它易于使用,功能强大,且不依赖于其他环境
  • 安装
npm install ws

2.1.2 代创建WebSocket服务器

const WebSocket = require('ws')
// 创建WebSocket服务端的对象,绑定的端口号是9998
// 相当于为ws创建了个接口,这个就是连接websocket的链接,后续前端会用到
const wss = new WebSocket.Server({port: 9998
})

2.1.3 监听前端连接websocket(ws.on的connection事件)

const WebSocket = require('ws')
// 创建WebSocket服务端的对象,绑定的端口号是9998
const wss = new WebSocket.Server({port: 9998
})
module.exports.listener = () => {// 对客户端连接事件进行监听,只要有WebSocket连接到该服务器,就会触发'connection'事件// ws代表的是客户端的连接的socket对象;req对象可以用来获取客户端的信息,如ip、端口号wss.on('connection', (ws, req) => {console.log('有客户端连接成功了', ws, req)})
}
// 若要获取所有已连接的客户端信息,则可以使用server.clients数据集

2.1.4 服务器接收数据(ws.on的message事件)

const WebSocket = require('ws')
// 创建WebSocket服务端的对象,绑定的端口号是9998
const wss = new WebSocket.Server({port: 9998
})
module.exports.listener = () => {wss.on('connection', (ws, req) => {console.log('有客户端连接成功了', ws, req)// 对客户端的连接对象进行message事件的监听// 当客户端有消息发送给服务器时,服务器就能够触发该消息// msg:由客户端发给服务端的数据ws.on('message', msg => {console.log('客户端发送给服务器端', msg)// 当接收到客户端传的参数之后服务器端可以执行某些操作(具体看需求)// 小编这里是做了一个数据返回给客户端// 是当客户端连接成功之后会发送一条信息告诉服务器,服务器监听到信息之后再返回数据给客户端const data = [[80, 110, 150, 60, 30, 130, 110],[80, 120, 150, 80, 40, 120, 112],[80, 130, 150, 40, 70, 133, 115],[80, 140, 150, 30, 80, 110, 110],[80, 130, 150, 70, 100, 140, 115],[80, 120, 180, 90, 90, 150, 120],[80, 100, 120, 90, 80, 120, 160]]let i = 0setInterval(() => {if (i === data.length) {i = 0}// 发送数据给客户端ws.send(JSON.stringify(data[i]))i++}, 1000)// 由服务端往客户端发送数据})})
}

2.1.5 服务器发送数据(ws.send()方法)

 /* send(data [,options][,callback])data:发送的数据options对象(可选):(1)compress:指定数据是否需要压缩。默认为true(2)binary:指定数据是否通过二进制传送。默认是自动检测(3)mask:指定是否应遮罩数据。(4)fin:指定数据是否为消息的最后一个片段。默认为true*/
const WebSocket = require('ws')
// 创建WebSocket服务端的对象,绑定的端口号是9998
const wss = new WebSocket.Server({port: 9998
})
const WebSocket = require('ws')
// 创建WebSocket服务端的对象,绑定的端口号是9998
const wss = new WebSocket.Server({port: 9998
})
module.exports.listener = () => {wss.on('connection', (ws, req) => {console.log('有客户端连接成功了', ws, req)// 对客户端的连接对象进行message事件的监听// 当客户端有消息发送给服务器时,服务器就能够触发该消息// msg:由客户端发给服务端的数据ws.on('message', msg => {console.log('客户端发送给服务器端', msg)// 当接收到客户端传的参数之后服务器端可以执行某些操作(具体看需求)// 小编这里是做了一个数据返回给客户端// 是当客户端连接成功之后会发送一条信息告诉服务器,服务器监听到信息之后再返回数据给客户端const data = [[80, 110, 150, 60, 30, 130, 110],[80, 120, 150, 80, 40, 120, 112],[80, 130, 150, 40, 70, 133, 115],[80, 140, 150, 30, 80, 110, 110],[80, 130, 150, 70, 100, 140, 115],[80, 120, 180, 90, 90, 150, 120],[80, 100, 120, 90, 80, 120, 160]]let i = 0setInterval(() => {if (i === data.length) {i = 0}// ========发送数据给客户端========ws.send(JSON.stringify(data[i]))i++}, 1000)// 由服务端往客户端发送数据})})
}

2.1.6 关闭服务器(ws.on的close事件)

const WebSocket = require('ws')
// 创建WebSocket服务端的对象,绑定的端口号是9998
const wss = new WebSocket.Server({port: 9998
})
module.exports.listener = () => {wss.on('connection', (ws, req) => {ws.on('message', msg => {console.log('客户端发送给服务器端', msg.toString('utf8'))const data = [[80, 110, 150, 60, 30, 130, 110],[80, 120, 150, 80, 40, 120, 112],[80, 130, 150, 40, 70, 133, 115],[80, 140, 150, 30, 80, 110, 110],[80, 130, 150, 70, 100, 140, 115],[80, 120, 180, 90, 90, 150, 120],[80, 100, 120, 90, 80, 120, 160]]let i = 0// 模拟了由服务端往客户端发送数据setInterval(() => {if (i === data.length) {i = 0}// 服务器向前端发送数据ws.send(JSON.stringify(data[i]))i++}, 1000)})// 监听要关闭连接的函数ws.on('close', function close () {// 这里面关闭的逻辑console.log('WebSocket连接已关闭')})})
}

—— 至此,后端的websocket逻辑其实已经完结了

2.2 创建后台服务器(后端部分)

  • 利用express框架在后台开服务器,供我们运行后端代码以及跑websocket
// express具体使用看我提供个文章中有
const express = require('express')
const app = express()
const port = 3001
// app.get('/', (req, res) => res.send('hello express'))
// express提供了一个非常好用的函数,叫做express.static(),能快速托管静态资源的内置中间件
// 如下配置是将public目录下的图片、css文件、JavaScript文件对外开放访问
app.use('/', express.static('public'))
// 解析 JSON 格式的请求体数据
app.use(express.json())
// 监听设置的端口
app.listen(port, () => { console.log('server is running,port is' + port) })// 监听开启的websocket服务器
const websocketservice = require('./web_socket_service')
websocketservice.listener()

AJAX及其相关知识应用(很详细)_使用ajax需要引入什么-CSDN博客

第三章 前端使用websocket的方法

3.1 常用的使用方法(前端部分)

(注意:小编这里使用的是一个echarts可视化的动态展示,也方便以后大家使用可视化大屏时有一定的参考价值)

3.1.1 代码引入

<template><div><div id='main' style="width:800px;height:600px"></div></div></template><script>
import * as echarts from 'echarts'
// 引入ws,路径是后端配置好给前端的
const ws = new WebSocket('ws://localhost:9998')
export default {data () {return {}},mounted () {// 要想展示实时效果,需要对series配置下的data数据进行实时的监听const options = {title: {text: '数量统计'},xAxis: {data: ['衣服', '牛奶', '巧克力', '矿泉水', '方便面', '面包', '花生']},yAxis: {},series: [{name: '销量',type: 'line',data: [100, 150, 120, 90, 30, 130, 110]}]}const mychart = echarts.init(document.getElementById('main'))ws.onopen = () => {console.log('连接服务器端成功')ws.send('Hello websocket')}ws.onerror = () => {console.log('连接服务器失败')}ws.onmessage = (msg) => {console.log('接收到服务端发送的数据')console.log('msg', msg)console.log(JSON.parse(msg.data))options.series[0].data = JSON.parse(msg.data)mychart.setOption(options)// ws.send('Hello websocket to message')}},beforeDestroy () {ws.onclose = () => {console.log('websocket已关闭')}},components: {}
}
</script>

3.1.2 引入ws

  • 如果做前后端分分离的项目前端只需要跟后端要配置好的ws的路径即可 
<template><div><div id='main' style="width:800px;height:600px"></div></div></template><script>
import * as echarts from 'echarts'
// 引入ws,路径是后端配置好给前端的
// 小编在这里引入的目的是除了要展示连接成功,还需要执行关闭连接的逻辑,两者不在相同的生命周期钩子中执行
const ws = new WebSocket('ws://localhost:9998')export default {data () {return {}},mounted () {},beforeDestroy () {},components: {}
}
</script>

3.1.3 打开ws连接(ws.onopen)

// 打开连接
ws.onopen = () => {console.log('连接服务器端成功')// 如果与ws连接成功,我们发送消息跟服务器说一下已经连接ws.send('Hello websocket')
}

3.1.4 连接错误(ws.onerror)

ws.onerror = () => {console.log('连接服务器失败')
}

3.1.5 前端接收服务器的数据(ws.onmessage)

// 接收服务器传的数据,msg,每当数据发生变化,后端都会返回数据到前端
// 该方法会一直都在监听ws返回的数据,然后不断的更新我的的图表,从而模拟实现了一个websocket的demo
ws.onmessage = (msg) => {console.log('接收到服务端发送的数据')console.log('msg', msg)// 这里面就可以处理数据的逻辑了console.log(JSON.parse(msg.data))options.series[0].data = JSON.parse(msg.data)mychart.setOption(options)
}

3.1.6 关闭连接(ws.onclose)

beforeDestroy () {ws.onclose = () => {console.log('websocket已关闭')}
},

-- 至此,前端代码结束

第四章 效果展示

  • 启动后端,开启ws服务器

  • 启动前端

  •  运行后前端控制台展示

  •  后端控制台输出

  • 关闭前端页面后台输出

  •  注意代码:msg.toString('utf8')——注意一下websocket支持的数据类型,由于小编看到在控制台输出的是buffer格式,所以进行了一下转换
  • 展示效果 

提示

看到最后了,提示一下,如果还是前后端分开开发时,前端处理其实并没有那么复杂,只需要了解第三章的结构即可。 

源代码

https://gitee.com/shallow-winds/websocket_demo

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/610493.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从研发转销售,90后联合创始人分享从 0 到 1 开拓市场的秘诀

小 T 导读&#xff1a;在去年涛思数据举办的 TDengine 开发者大会上&#xff0c;侯江燚以联合创始人的身份登台演讲&#xff0c;将自己对于开源商业化的独特观点娓娓道来&#xff0c;赢得阵阵掌声。今年是他加入涛思数据的第五年&#xff0c;从放弃读博选择成为一名研发&#x…

“To-Do Master“ GPTs:重塑任务管理的趣味与效率

有 GPTs 访问权限的可以点击链接进行体验&#xff1a;https://chat.openai.com/g/g-IhGsoyIkP-to-do-master 部署私人的 To-Do Master 教程&#xff1a;https://github.com/Reborn14/To-Do-Master/tree/main 引言 在忙碌的日常生活中&#xff0c;有效地管理日常任务对于提高生…

K2P路由器刷OpenWrt官方最新版本固件OpenWrt 23.05.2方法 其他型号的智能路由器OpenWrt固件刷入方法也基本上适用

最近路由器在开机时总出问题,于是就那他来开刀,直接刷一个OpenWrt官方最新版本的固件, 刷其他第三方的固件总是觉得不安全, 而且很多第三方固件都带了些小工具,始终会有安全隐患, 而且占用内存空间太多,本来这个东西就没有多少内存,于是就干脆刷一个官方的原始固件(才6.3M, 相…

HarmonyOS应用开发学习笔记 UI布局学习 相对布局 (RelativeContainer)

UI布局学习 之 相对布局 &#xff08;RelativeContainer&#xff09; 官方文档 一、关键字 RelativeContainer&#xff0c; alignRules&#xff08;适配规则&#xff09; Text(Text02).alignRules({left: { anchor: text01, align: HorizontalAlign.Start },top: { anchor: t…

[足式机器人]Part3 机构运动学与动力学分析与建模 Ch00-2(3) 质量刚体的在坐标系下运动

本文仅供学习使用&#xff0c;总结很多本现有讲述运动学或动力学书籍后的总结&#xff0c;从矢量的角度进行分析&#xff0c;方法比较传统&#xff0c;但更易理解&#xff0c;并且现有的看似抽象方法&#xff0c;两者本质上并无不同。 2024年底本人学位论文发表后方可摘抄 若有…

WAF(Web应用防火墙)全面解析

Web应用防火墙&#xff08;WAF&#xff09;是确保网络安全的重要工具&#xff0c;尤其在保护Web应用免受各种网络攻击方面发挥着至关重要的作用。以下是关于WAF的各方面详细介绍&#xff1a; 定义和目的 WAF是一种特殊类型的防火墙&#xff0c;专门设计用于监视、过滤和阻挡进…

增广路算法 DFS求解 最大网络流问题

最大网络流问题 最大网络流问题是这样的&#xff0c;有一个有向图&#xff0c;假定有一个源点&#xff0c;有一个汇点&#xff0c;源点有流量出来&#xff0c;汇点有流量进入&#xff0c;有向图上的边的权重为该条边可通过的最大流量(方向为边的方向)&#xff0c;问从源点到汇…

的修平台——院校智能报修的强大助手,轻松解决报修难题!

在当今信息化时代&#xff0c;智能化的后勤管理成为了提升院校服务水平的关键。其中&#xff0c;报修流程的智能化改革更是重中之重。面对传统报修方式存在的种种问题&#xff0c;如报修流程冗长、信息沟通不畅、进度难以跟踪等&#xff0c;的修平台应运而生&#xff0c;为院校…

自动化测试框架pytest系列之基础概念介绍(一)

如果你要打算学习自动化测试 &#xff0c;无论是web自动化、app自动化还是接口自动化 &#xff0c;在学习的道路上&#xff0c;你几乎会遇到pytest这个测试框架&#xff0c;因为自动化编写没有测试框架&#xff0c;根本玩不了 。 如果你已经是一位自动化测试人员 &#xff0c;…

HarmonyOS鸿蒙应用开发——原生与H5通信框架DSBrigde-HarmonyOS

文章目录 介绍安装使用原生JavaScript进度回调 参考 介绍 HarmonyOS版的DSBridge&#xff0c;通过本库可以在鸿蒙原生与JavaScript完成交互&#xff0c;相互调用彼此的功能。 目前兼容Android、iOS第三方DSBridge库的核心功能&#xff0c;基本保持原来的使用方式&#xff0c;…

c++|关键字extern

一个C语言项目往往由多个文件组合而成。而对于多个文件来说&#xff0c;它们可能会共用到一些相同的变量。而有些情况下&#xff0c;这些相同的变量并没有出现在本文件内&#xff0c;有可能在其他文件内。而一个文件可能只会搜寻该文件内部是否有该变量。 所以&#xff0c;需要…

c++的构造函数

目录 构造函数 1.构造函数&#xff1a; 2.构造函数的特点&#xff1a; 默认构造函数 -- 没有参数的构造函数 1. 合成(自动)的默认构造函数(一般不常用) 1&#xff09; 介绍&#xff0c;以及为什么不使用 2&#xff09;可以使用合成默认构造函数的情况 2. 自定义的默认…

xss-labs(6-9)

level6:欢迎来到level6 老规矩还是先看看输入框的闭合情况 尝试事件函数绕过 test" onclick="alert(欢迎来钓鱼) 既然事件函数被转义了,那就使用我们第二关用过的绕过方法插入标签看看 test"><script>alert(欢迎来钓鱼)</script>// <

新书速览|循序渐进Vue.js 3.x前端开发实战

Vue.js初学者和前端开发人员使用&#xff0c;网课、培训机构与大中专院校的教学用书 作者简介 张益珲 美国亚利桑那州立大学计算机工程技术硕士&#xff0c;架构师&#xff0c;从业近10年&#xff0c;多年大前端开发经验&#xff0c;曾就职于知名上市公司&#xff0c;主导开发…

go 语言常见问题(2)

11. recover的执行时机 无&#xff0c;recover 必须在 defer 函数中运行。recover 捕获的是祖父级调用时的异常&#xff0c;直接调用时无效。 func main() {recover()panic(1) }直接 defer 调用也是无效。 func main() {defer recover()panic(1) }defer 调用时多层嵌套依然无…

算法训练营Day42(背包问题)

基础 非竞赛只需要搞懂0-1背包和完全背包 0-1背包基础 0-1背包是完全背包和多重背包的基础 n个物品&#xff0c;每个物品一个&#xff0c;每个物品有自己的重量和价值&#xff0c;&#xff0c;一个背包能装m物品&#xff0c;问最多装多少物品。 暴力解法&#xff0c;n个物品…

Acwing845 八数码

在2019年y神认为是困难题&#xff0c;2023年便是中等题了。。。嗯。。。 题目 在一个 33的网格中&#xff0c;1∼8 这 8个数字和一个 x 恰好不重不漏地分布在这 33 的网格中。 例如&#xff1a; 1 2 3 x 4 6 7 5 8在游戏过程中&#xff0c;可以把 x 与其上、下、左、右四个…

SpringMVC 的入门

SpringMVC 的入门 1环境搭建 1.1.创建工程 1.2.添加web支持 右键项目选择Add framework support... 2.添加web支持 ​ 3.效果 注意&#xff1a; 不要先添加打包方式将web目录要拖拽到main目录下&#xff0c;并改名为webapp 1.3.pom.xml <?xml version"1.0&q…

鱼哥赠书活动第⑥期:《内网渗透实战攻略》看完这本书教你玩转内网渗透测试成为实战高手!!!!

鱼哥赠书活动第⑥期&#xff1a;《内网渗透实战攻略》 如何阅读本书&#xff1a;本书章节介绍&#xff1a;本书大致目录&#xff1a;适合阅读对象&#xff1a;赠书抽奖规则:往期赠书福利&#xff1a; 当今&#xff0c;网络系统面临着越来越严峻的安全挑战。在众多的安全挑战中&…

7双指针问题-接雨水2

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 示例 1&#xff1a; 输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1] 输出&#xff1a;6 解释&#xff1a;上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表…