websocket 介绍

目录

  • 1,前端如何实现即时通讯
    • 短轮询
    • 长轮询
  • 2,websocket
    • 2.1,握手
    • 2.2,握手过程举例
    • 2.3,socket.io
  • 3,websocket 对比 http 的优势

1,前端如何实现即时通讯

websocket 协议出现之前,前端想实现即时通讯,只能通过下面2种方式:

  • 短轮询 short polling
  • 长轮询 long polling

短轮询

客户端每隔一小段时间,就向服务器请求一次,询问有没有新消息。

实现起来很简单,只需要开启一个计时器不断发送请求即可。但缺点比较明显:

  • 会产生大量无意义的请求。
  • 会频繁打开关闭 TCP 连接。
  • 实时性并不高。

长轮询

为了解决短轮询的问题,出现了长轮询。原理如下图:

在这里插入图片描述

虽然长轮询让每次请求和响应都变的有意义,但依然存在一些问题:

  • 客户端长时间收不到响应会导致超时,从而主动断开和服务器的连接。

    可以在 ajax 请求因为超时而结束时,立即重新发送请求到服务器。虽然会让之前的请求无意义,但比短轮询好多了。

  • 因为客户端可能【过早的】请求了服务器,所以服务器不得不挂起这个请求,直到新消息出现。
    这会让服务器长时间占用资源却没有做任何事情。

2,websocket

websocket 协议 HTML5 带来的新协议,相对于 http,它是一个持久连接的协议,它利用 http 协议完成握手,然后通过 TCP 连接通道发送消息,使用 websocket 协议可以实现服务器主动推送消息的能力

从上图可以看出:

  • websocket 也是建立在 TCP 协议上的,利用的是 TCP 的全双工通信能力。
  • 使用时会经过2个阶段,握手阶段通信阶段
  • 维持 TCP 连接也是需要耗费资源的,所以看实际需求。

2.1,握手

websocket 协议内容比较复杂,这里只介绍下握手协议。(下面会有例子说明)

当客户端需要和服务器使用 websocket 进行通信时,首先会使用HTTP协议完成一次特殊的请求-响应,这一次的请求-响应就是websocket握手

在握手阶段,首先由客户端向服务器发送一个请求,请求地址格式如下:

# 使用 HTTP
ws://mysite.com/path
# 使用 HTTPS
wss://mysite.com/path

请求头:

Connection: Upgrade /* 协议需要升级,不使用 HTTP了 */
Upgrade: websocket /* 协议升级为 websocket */
Sec-WebSocket-Version: 13 /* websocket协议版本为 13 */
Sec-WebSocket-Key: YWJzZmFkZmFzZmRhYw== /* 连接的 key */

服务器如果同意,响应如下消息:

HTTP/1.1 101 Switching Protocols /* 切换协议,101表示切换协议 */
Connection: Upgrade /* 协议升级 */
Upgrade: websocket /* 升级到 websocket */
Sec-WebSocket-Accept: ZzIzMzQ1Z2V3NDUyMzIzNGVy /* 重新编码后的 key */

Sec-WebSocket-Accept 是将 Sec-WebSocket-Key 使用特殊的算法重新编码生成的。浏览器使用它来确保响应与请求相对应。

握手完成后,后续的消息收发不再使用 HTTP,任何一方都可以主动发消息给对方。

2.2,握手过程举例

客户端

<button>发送数据到服务器</button>
<script>// 创建一个websocket,同时,发送连接到服务器const ws = new WebSocket("ws://localhost:3002"); ws.onopen = function () {// http 握手完成console.log("连接已建立");};ws.onclose = function () {console.log("通道关闭");};document.querySelector("button").onclick = function () {ws.send("客户端数据123");};// ws.close(); //客户端主动断开连接
</script>

服务器

const net = require("net");const server = net.createServer((socket) => {console.log("收到客户端的连接");socket.once("data", (chunk) => {// 解析请求报文,const httpContent = chunk.toString("utf-8");let parts = httpContent.split("\r\n");parts.shift();parts = parts.filter((s) => s).map((m) => {const i = m.indexOf(":");return [m.slice(0, i), m.slice(i + 1).trim()];});// 变成对象的形式,为了取出请求头 Sec-WebSocket-Keyconst headers = Object.fromEntries(parts);const crypto = require("crypto"); // 加密模块const hash = crypto.createHash("sha1");// 创建 Sec-WebSocket-Accept,后面是一个随机的 guid。const key = hash.update(headers["Sec-WebSocket-Key"] + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").digest("base64");// 响应,注意格式。socket.write(`HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: ${key}`);// 接收客户端的消息socket.on("data", (chunk) => {console.log(chunk.toString("utf-8"));});});
});server.listen(3002);

注意数据格式为 Buffer 需要转码,因为 websocket 的消息需要特定的格式,数据量较大时会切片传输。但每个切片到达的顺序可能不一样,所以为了保证将接收到的数据,能按照顺序拼接,所以数据格式为 Buffer 二进制的形式。

2.3,socket.io

一般使用 websocket 大多都会使用它 socket.io

测试使用版本 v4.7.2,消息格式都是字符串而不是 Buffer,所以不用转码了。

浏览器:访问地址 http://localhost:5500/index.html

<button>发送数据到服务器</button>
<script src="https://cdn.bootcdn.net/ajax/libs/socket.io/4.7.2/socket.io.js"></script>
<script>const socket = io("http://localhost:3002");document.querySelector("button").onclick = function () {socket.emit("to-server", "来自浏览器的消息");};// 监听服务器的消息,约定事件名 to-clientsocket.on("to-client", (chunk) => {console.log(chunk);});// 服务器断开连接时触发socket.on("disconnect", () => {console.log("closed");});
</script>

服务器:写法参考

启动后的服务器地址:http://localhost:3002,所以会发生跨域。解决

const Koa = require("koa");
const { createServer } = require("http");
const { Server } = require("socket.io");const app = new Koa();
const httpServer = createServer(app.callback());
const io = new Server(httpServer, {cors: {origin: "http://localhost:5500",},
});io.on("connection", (socket) => {// 当有一个新的客户端连接到服务器成功之后,触发的事件console.log("新的客户端连接进来了");// 监听客户端发送的消息,约定事件为 to-serversocket.on("to-server", (chunk) => {// 监听客户端的msg消息console.log(chunk);});let count = 0;const timer = setInterval(function () {// 每隔两秒钟,发送一个消息给客户端,约定事件为 to-clientsocket.emit("to-client", `来自服务器的第${count++}次消息`);}, 2000);socket.on("disconnect", () => {clearInterval(timer);console.log("closed");});
});// 监听端口
httpServer.listen(3002, () => {console.log("server listening on 3002");
});

效果展示:

在这里插入图片描述

3,websocket 对比 http 的优势

当页面中需要观察实时数据的变化(比如聊天、k 线图)时,过去我们往往使用两种方式完成(短轮询,长轮询)

无论是哪一种方式,都暴露了 http 协议的弱点,即响应必须在请求之后发生,服务器是被动的,无法主动推送消息。而让客户端不断的发起请求又会占用了资源。

websocket 的出现就是为了解决短轮询,长轮询的缺点,它利用 http 协议完成握手之后,就可以与服务器建立持久的连接,服务器可以在需要的时候主动推送消息给客户端,这样占用的资源最少,同时实时性也最高。


以上。

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

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

相关文章

助力智能车损计算,基于高精度YOLOv8开发构建智能化车辆受损区域分割检测识别分析系统

车辆受损评估本身有专业的评估流程&#xff0c;本文并不是要探究这块的内容&#xff0c;而是想要通过技术手段来对车辆受损区域的面积做自动化的计算&#xff0c;在前面的博文中我们已经有了相关的开发实践了&#xff0c;感兴趣的话可以自行移步阅读即可&#xff1a; 《基于yo…

数据结构入门到入土——List的介绍

目录 一&#xff0c;什么是List&#xff1f; 二&#xff0c;常见接口介绍 三&#xff0c;List的使用 一&#xff0c;什么是List&#xff1f; 在集合框架中&#xff0c;List是一个接口&#xff0c;继承自Collection。 Collection也是一个接口&#xff0c;该接口中规范了后序容…

【智慧门店】东胜物联蓝牙网关助力解决方案商,推动汽车后市场企业智能化升级

截至2023年9月底&#xff0c;我国汽车保有量达3.3亿辆&#xff0c;后市场前景广阔。 随着人工智能、5G、物联网等新技术的普及&#xff0c;汽车后市场企业希望向智能化迈进&#xff0c;借助新兴科技的力量提升汽车维修、车辆保养等服务质量&#xff0c;满足消费者日益增长的需…

算法模板之单调栈和单调队列图文详解

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;算法模板、数据结构 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言一. ⛳️单调栈讲解1.1 &#x1f514;单调栈的定义1.2 &#x1f514;如何维护一个单…

关于SQL时间盲注(基于sleep函数)的手动测试、burpsuite爆破、sqlmap全自动化注入

SQL时间注入是一种常见的SQL注入攻击方式&#xff0c;攻击者通过在SQL语句中注入时间相关的代码&#xff0c;来获取敏感信息或者执行非法操作。其基本原理如下&#xff1a; 攻击者向Web应用程序中输入一段恶意代码&#xff0c;通过SQL语句查询数据库&#xff0c;并注入时间相关…

纸质版表格怎么用扫描仪转换成电子版表格

要将纸质版表格转换成电子版表格&#xff0c;可以使用以下步骤&#xff1a; 1、准备一台物理扫描仪并与电脑连接好&#xff0c;并安装好驱动。 2、打开安装好的金鸣表格文字识别电脑客户端。 3、点击“扫描文件”&#xff0c;在弹出的对话框中选中需要使用的扫描仪。 4、点击“…

Springboot+vue的医疗报销系统(有报告),Javaee项目,springboot vue前后端分离项目

演示视频&#xff1a; Springbootvue的医疗报销系统&#xff08;有报告&#xff09;&#xff0c;Javaee项目&#xff0c;springboot vue前后端分离项目 项目介绍&#xff1a; 本文设计了一个基于Springbootvue的前后端分离的医疗报销系统&#xff0c;采用M&#xff08;model&a…

【网络技术】【Kali Linux】Wireshark嗅探(一)ping和ICMP

一、实验目的 本次实验使用wireshark流量分析工具进行网络嗅探&#xff0c;旨在了解ping命令的原理及过程。 二、网络环境设置 本系列实验均使用虚拟机完成&#xff0c;主机操作系统为Windows 11&#xff0c;虚拟化平台选择Oracle VM VirtualBox&#xff0c;组网模式选择“N…

手动创建idea SpringBoot 项目

步骤一&#xff1a; 步骤二&#xff1a; 选择Spring initializer -> Project SDK 选择自己的JDK版本 ->Next 步骤三&#xff1a; Maven POM ->Next 步骤四&#xff1a; 根据JDK版本选择Spring Boot版本 11版本及以上JDK建议选用3.2版本&#xff0c;JDK为11版本…

ArcGIS Pro中Conda环境的Scripts文件解读

Scripts中包含的文件如下 1. propy.bat 用于在 ArcGIS Pro 外部运行 Python 脚本&#xff08;扩展名为 .py 的文件&#xff09;。使用的conda环境是与ArcGIS pro环境同步。propy.bat原理是代替各自python环境下的python.exe&#xff0c;主要区别是propy.bat使用的是与Pro同的…

OCP NVME SSD规范解读-2.复位与控制器配置要求-part2

Maximum Data Transfer Size (MDTS)&#xff1a;设备应支持至少256KB的最大数据传输大小。 CSTS.CFS Reporting: 设备固件应支持报告CSTS.CFS&#xff08;Controller Status and Capabilities Field in the Status Register&#xff09;。 Queue Depths: 每个提交队列的SQ最小…

C++11 lambda函数和包装器

目录 前言 一.lambda的引入 二、lambda函数的使用 1.一般使用 2.引用 三、包装器 1.包装普通对象 2.包装类成员对象 3.bind 前言 学习过python的同学应该对lambda函数不陌生&#xff0c;这是一个匿名函数&#xff0c;不需要写函数的名字。在不会多地方调用某个简单函数…

【OpenCV】告别人工目检:深度学习技术引领工业品缺陷检测新时代

目录 前言 机器视觉 缺陷检测 工业上常见缺陷检测方法 内容简介 作者简介 目录 读者对象 如何阅读本书 获取方式 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。 点击跳转到网站 机器视觉…

裁员+失恋或许不能比这更遭了,敬小明一塌糊涂与充满感动的2023,也敬曾经的挚爱与寒冬的冰霜

~ 随机抽取评论区的 3位 小伙伴送上精美礼品 ~ 参与方式&#xff1a;关注、点赞、收藏&#xff0c;评论 "2024&#xff0c;一天当做两天卷&#xff01;" 活动时间&#xff1a;截止到 2024-01-21 00:00:00 礼品清单&#xff1a;CSDN活动周边、自选图书 本文目录 序 …

go语言,ent库与gorm库,插入一条null值的time数据

情景介绍 使用go语言&#xff0c;我需要保存xxxTime的字段至数据库中&#xff0c;这个字段可能为空&#xff0c;也可能是一段时间。我采取的是统一先赋值为空&#xff0c;若有需要&#xff0c;则再进行插入&#xff08;需要根据另一个字段判断是否插入&#xff09; 在我的数据…

StackOverflowError的JVM处理方式

背景&#xff1a; 事情来源于生产的一个异常日志 Caused by: java.lang.StackOverflowError: null at java.util.stream.Collectors.lambda$groupingBy$45(Collectors.java:908) at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169) at java.util.ArrayL…

IDEA 开发中常用的快捷键

目录 Ctrl 的快捷键 Alt 的快捷键 Shift 的快捷键 Ctrl Alt 的快捷键 Ctrl Shift 的快捷键 其他的快捷键 Ctrl 的快捷键 Ctrl F 在当前文件进行文本查找 &#xff08;必备&#xff09; Ctrl R 在当前文件进行文本替换 &#xff08;必备&#xff09; Ctrl Z 撤…

MySql 中的锁详解 —— 共享锁、排他锁、全局锁、表级锁、页级锁、行级锁、意向锁、记录锁、间隙锁等

目录 一. 前言 二. 锁的分类 三. 共享锁&#xff08;读锁&#xff09;和排他锁&#xff08;写锁&#xff09; 3.1. 共享锁&#xff08;Shared Lock&#xff09; 3.2. 排他锁&#xff08;Exclusive Lock&#xff09; 四. 全局锁、表级锁、页级锁和行级锁 4.1. 全局锁 4.…

Vue axios Post请求 403 解决之道

前言&#xff1a; 刚开始请求的时候报 CORS 错误&#xff0c;通过前端项目配置后算是解决了&#xff0c;然后&#xff0c;又开始了新的报错 403 ERR_BAD_REQUEST。但是 GET 请求是正常的。 后端的 Controller 接口代码如下&#xff1a; PostMapping(value "/login2&qu…

JVM GC 算法原理概述

对于JVM的垃圾收集&#xff08;GC&#xff09;&#xff0c;这是一个作为Java开发者必须了解的内容&#xff0c;那么&#xff0c;我们需要去了解哪些内容呢&#xff0c;其实&#xff0c;GC主要是解决下面的三个问题&#xff1a; 哪些内存需要回收&#xff1f; 什么时候回收&…