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,一经查实,立即删除!

相关文章

“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…

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

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

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

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

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

如果你要打算学习自动化测试 &#xff0c;无论是web自动化、app自动化还是接口自动化 &#xff0c;在学习的道路上&#xff0c;你几乎会遇到pytest这个测试框架&#xff0c;因为自动化编写没有测试框架&#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;主导开发…

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

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

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] 表…

控制注塑机PQ比例阀放大器

控制不带电反馈的单或双比例电磁铁的博世力士乐&#xff08;Bosch Rexroth&#xff09;、伊顿威格士&#xff08;EATON Vickers&#xff09;、油研&#xff08;YUKEN&#xff09;、贺德克&#xff08;HYDAC&#xff09;、大金&#xff08;DAIKIN&#xff09;、不二越&#xff0…

网安入门13-文件上传(htaccess,其他绕过)

空格绕过&#xff0c;点号绕过 Pass-07 直接上传肯定是失败的 把文件名1.php改成1.php.或1.php_(下划线为空格)&#xff0c;这种命名方式在windows系统里是不被允许的&#xff0c;所以需要在burp之类里进行修改&#xff0c;然后绕过验证后&#xff0c;会被windows系统自动去掉…

三维猴打印PCB外壳预留板壳间距

3D文件下单那里有一个“3D模型设计规范”&#xff0c;里面详细讲了设计时要考虑打印的参数细节。如果有其他的设计规范不了解的也可以进去查看&#xff0c;里面写的很详细。 这里是打印PCB外壳预留板壳间距相关说明&#xff1a; 设计模型为装配体&#xff0c;请务必满足装配最…

什么是springmvc(介绍)

什么是springmvc 1. 什么是springmvc2.项目中加入springmvc支持2.1 导入依赖2.2 springMVC配置文件2.3 web.xml配置2.4 中文编码处理 3. 编写一个简单的controller4. 视图层配置4.1 视图解析器配置4.2 静态资源配置4.2 编写页面4.3 页面跳转方式 5. SpringMVC处理请求的流程6. …

纯血鸿蒙「扩圈」100天,酝酿已久的突围

坦白讲&#xff0c;去年参加华为开发者大会看到HarmonyOS NEXT&#xff08;仅运行鸿蒙原生应用&#xff0c;所以也称作「纯血鸿蒙」&#xff09;的时候&#xff0c;小雷也没料想到鸿蒙原生应用生态的发展速度会如此之快。 9月25日&#xff0c;华为正式对外宣布启动HarmonyOS NE…

【C语言】指针——从底层原理到应用

C语言指针-从底层原理到花式技巧&#xff0c;用图文和代码帮你讲解透彻 目录 一、前言二、变量与指针的本质 1. 内存地址2. 32位与64位系统3. 变量4. 指针变量5. 操作指针变量 5.1 指针变量自身的值5.2 获取指针变量所指向的数据5.3 以什么样的数据类型来使用/解释指针变量所指…