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

news2024/11/17 23:34:14

目录

第一章 前言

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 = 0
      setInterval(() => {
        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 = 0
      setInterval(() => {
        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.coloradmin.cn/o/1371851.html

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

相关文章

day-05 删除子串后的字符串最小长度

思路 通过不断地检查是否含有"AB"或"CD"&#xff0c;如果有则将其从字符串中删除&#xff0c;直到"AB"或"CD"都不存在时&#xff0c;返回字符串的长度 解题方法 //检测是否有"AB" for(int i0;i<len-1;i){ if(s.charAt(i…

【Python】使用tkinter设计开发Windows桌面程序记事本(1)

下一篇&#xff1a; 记事本介绍 电脑记事本是一种简单的文本编辑器&#xff0c;用于在电脑上创建、编辑和存储文本文件。它通常被用作轻量级的文本编辑工具&#xff0c;适用于简单的文本编辑任务&#xff0c;如写日记、做笔记、编写代码等。以下是对电脑记事本的详细介绍&…

LeetCode刷题--- 最小路径和

个人主页&#xff1a;元清加油_【C】,【C语言】,【数据结构与算法】-CSDN博客 个人专栏 力扣递归算法题 http://t.csdnimg.cn/yUl2I 【C】 ​​​​​​http://t.csdnimg.cn/6AbpV 数据结构与算法 ​​​http://t.csdnimg.cn/hKh2l 前言&#xff1a;这个专栏主要讲述动…

wireshark使用教程

目录 windows平台安装Wireshark组件选择Additional TasksPacket CaptureUSB CaptureNpcap Installation Options Ubuntu上安装 Wireshark不使用 sudo 运行 Wireshark 使用GUI抓包使用命令行抓包确定抓取哪个网卡的报文抓取数据包停止抓包设置过滤条件 参考资料 Wireshark 是一款…

救赎之道,就在其中

时光荏苒&#xff0c;不知不觉距离我踏入职场的第一天已经快一年了。最近也是看到平台举办年度征文活动&#xff0c;借此契机重新审视自己这两年来的成长历程&#xff0c;也希望对正在迷茫的人提供一些精神上的慰藉。 1.对未来的迷茫 如果要给两年前的自己打上标签&#xff0…

Flink-CEP 实战教程

文章目录 1. 基本概念1.1 CEP 是什么1.2 模式&#xff08;Pattern&#xff09;1.3 应用场景 2. 快速上手2.1 引入依赖2.2 入门实例 3. 模式API&#xff08;Pattern API&#xff09;3.1 个体模式3.1.1 基本形式3.1.2 量词&#xff08;Quantifiers &#xff09;3.1.3 条件&#x…

KVM系统虚拟化性能测试过程总结

buildroot编译 为啥要用buildroot 支持很多&#xff1a;交叉编译工具链、根文件系统生成、内核映像编译和引导加载程序编译。使用简单&#xff1a;使用类似内核的menuconfig、gconfig和xconfig配置界面&#xff0c;使用buildroot构建基本系统很容易。支持很多的包&#xff1a…

蓝凌EIS智慧协同平台 ShowUserInfo.aspx sql注入漏洞

漏洞描述&#xff1a; 蓝凌EIS智慧协同平台是一个简单、高效的工作方式专为成长型企业打造的沟通、协同、社交的移动办公平台&#xff0c;覆盖OA、沟通、客户、人事、知识等管理需求&#xff0c;集合了非常丰富的模块&#xff0c;满足组织企业在知识、项目管理系统建设等需求的…

[Linux进程(一)] 什么是进程?PCB的底层是什么?以及进程标识符pid与ppid

文章目录 1、前言2、描述进程 — PCB(os怎么管理进程呢)3、查看进程3.1 方法一3.2 方法二 4、系统调用获取进程标示符(PID)4.1 获取进程的ID4.2 获取进程的父进程ID 5、系统调用创建子进程-fork 1、前言 大家经常都在讲进程&#xff0c;而它到底是什么呢&#xff1f; 这里给大…

x-cmd pkg | dua - 磁盘使用分析器

目录 简介首次用户技术特点竞品和相关作品进一步阅读 简介 dua 是 Disk Usage Analyzer 的简写&#xff0c;该工具可以快速查看给定目录的磁盘空间使用情况。 对于想要深入了解磁盘空间使用情况并有效管理存储的用户来说&#xff0c;Dua 是一个很有价值的工具。通过使用 Dua …

Java流程控制的陷阱

文章目录 1. switch中break的作用2. switch支持的数据类型3. else隐含的条件4. 省略花括号的陷阱5. for循环的结构6. 使用标签跳出双层for循环 流程控制三种&#xff1a;顺序结构、分支结构、循环结构 分支机构两种&#xff1a;if语句、switch语句 循环结构&#xff1a;while循…

每日一篇英语文章分享:I have a dream. 争取早日阅读论文自由.

我有一个梦想》&#xff08;英文&#xff1a;I have a dream&#xff09;是美国黑人民权运动领袖马丁路德金于1963年8月28日在华盛顿林肯纪念堂发表的纪念性演讲。 《我有一个梦想》是马丁路德金在美国黑人受种族歧视和迫害由来已久的背景下&#xff0c;为了推动美国国内黑人争…

安达发|APS智能排产软件有哪些条件约束功能

APS智能排产软件是一种基于先进算法和人工智能技术的生产计划与调度系统&#xff0c;它可以帮助企业实现生产资源的优化配置&#xff0c;提高生产效率和降低生产成本。在实际应用中&#xff0c;APS智能排产软件需要满足多种条件约束功能&#xff0c;以满足不同企业的需求。以下…

python 和shell 变量互相传递

嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 主要介绍python和shell变量互相传递方法&#xff0c;使用了环境变量、管道等方法。 python -> shell&#xff1a; 1.环境变量 import os var123或var123 o…

【手搓深度学习算法】用线性回归预测波士顿房价

线性回归 线性回归是一种监督学习方法&#xff0c;用于建立因变量与一个或多个自变量之间的关系。线性回归的目标是找到一条直线&#xff0c;使得所有数据点到这条直线的距离之和最小。 线性回归的基本形式如下&#xff1a; y β 0 β 1 x 1 β 2 x 2 . . . β n x n ϵ…

Linux学习之网络编程(纯理论)

写在前面 刚刚更新完Linux系统编程&#xff0c;特别推荐大家去看的Linux系统编程&#xff0c;总共44个小时&#xff0c;老师讲的非常好&#xff0c;我是十天肝完的&#xff0c;每天大概看20集&#xff0c;每天还要以写blog的形式来写笔记来总结一下&#xff0c;虽然这十天有点…

printk的使用与理解

文章目录 一、理清printk二、printk的使用三、printk的打印级别1、基本解释2、详细解释3、如何修改console_loglevel、default_message_loglevel、minimum_console_loglevel、default_console_loglevel的值 四、printk的输出地方五、其它 一、理清printk printk如何使用&#…

C++ 实现十大排序算法

教你手撕排序&#xff0c;这里有一个概念就是稳定排序。假定在待排序的记录序列中&#xff0c;存在多个具有相同的关键字的记录&#xff0c;若经过排序&#xff0c;这些记录的相对次序保持不变&#xff0c;即在原序列中&#xff0c;r[i]r[j]&#xff0c;且r[i]在r[j]之前&#…

vim升级和配置

vim升级和配置 1、背景2、环境说明3、操作3.1 升级VIM3.2 配置VIM3.2.1、编辑vimrc文件3.2.2、安装插件 1、背景 日常工作跟linux系统打交道比较多&#xff0c;目前主要用到的是Cenots7和Ubuntu18这两个版本的linux系统&#xff0c;其中Centos7主要是服务器端&#xff0c;Ubun…

图神经网络|5.消息传递的计算方法 6.多层GNN的作用

5.消息传递的计算方法 边的存放方式 注意&#xff0c;在实际的边的实现方式中&#xff0c;并不是以邻接矩阵来进行实现的&#xff0c;这是因为在图的更新中&#xff0c;用邻接矩阵进行更新所占用的时间开销相对大&#xff0c;二是因为领接矩阵占用的空间大&#xff08;N方&am…