当前位置: 首页 > news >正文

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 协议完成握手之后,就可以与服务器建立持久的连接,服务器可以在需要的时候主动推送消息给客户端,这样占用的资源最少,同时实时性也最高。


以上。

相关文章:

websocket 介绍

目录 1&#xff0c;前端如何实现即时通讯短轮询长轮询 2&#xff0c;websocket2.1&#xff0c;握手2.2&#xff0c;握手过程举例2.3&#xff0c;socket.io 3&#xff0c;websocket 对比 http 的优势 1&#xff0c;前端如何实现即时通讯 在 websocket 协议出现之前&#xff0c;…...

【IoT网络层】STM32 + ESP8266 +MQTT + 阿里云物联网平台 |开源,附资料|

目标&#xff1a;实现STM32连接阿里云物联网平台发送数据同时接收数据&#xff0c;IOT studio界面显示数据。具体来说&#xff1a;使用ESP8266 ESP-01来连接网络&#xff0c;获取设备数据发送到阿里云物联网平台并显示且oled显示屏当前的设备数据&#xff0c;通过IOT studio界面…...

数据分析工具 Top 8

你能想象一个没有工具箱的水管工吗? 没有,对吧? 数据从业者也是如此。如果没有他们的数据分析工具&#xff0c;数据从业者就无法分析数据、可视化数据、从数据中提取价值&#xff0c;也无法做数据从业者在日常工作中做的许多很酷的事情。 根据你最感兴趣的数据科学职业——数…...

AI 换脸的新时代:没有显卡也可以使用的AI换脸工具

大家好&#xff01;今天&#xff0c;我要为大家介绍一个即使没有显卡&#xff0c;也能体验AI换脸的工具&#xff01;是的&#xff0c;您没听错&#xff0c;无论您的电脑配置如何&#xff0c;只要运行在Windows 10或Windows 11上&#xff0c;都可以轻松使用这一神奇工具。这就是…...

3.Python中的循环结构

Python中的循环结构 一、回顾分支练习题 1、判断是否为一个合法三角形 需求:输入三角形的3边,如果两边的长度大于第三条边,则代表是一个合法三角形 # 1、提示用户输入三角形的三边长度 a = int(input(请输入第一条边的长度:)) b = int(input(请输入第二条边的长度:)) …...

机器学习之BP神经网络精讲(Backpropagation Neural Network(附案例代码))

概念 BP神经网络(Backpropagation Neural Network)是一种常见的人工神经网络,它通过反向传播算法来训练网络,调整连接权重以最小化预测输出与实际输出之间的误差。这种网络结构包含输入层、隐藏层和输出层,使用梯度下降算法来优化权重。 结构: BP神经网络(Backpropag…...

安全生产人员定位系统助企业实现智能化管理,提高生产安全性和效率

安全生产人员定位系统是基于物联网技术的系统&#xff0c;通过集成各种传感器和通信技术&#xff0c;实时监测员工的位置和活动状态。该系统可以帮助企业管理者了解员工的工作状态&#xff0c;及时发现潜在的安全隐患&#xff0c;从而采取相应的措施&#xff0c;保障员工的安全…...

动态规划 多源路径 字典树 LeetCode2977:转换字符串的最小成本

涉及知识点 动态规划 多源最短路径 字典树 题目 给你两个下标从 0 开始的字符串 source 和 target &#xff0c;它们的长度均为 n 并且由 小写 英文字母组成。 另给你两个下标从 0 开始的字符串数组 original 和 changed &#xff0c;以及一个整数数组 cost &#xff0c;其中…...

Hadoop集群找不到native-hadoop

1.问题描述 hive 运行中的问题&#xff0c;需要把把native复制进去 /usr/lib 2023-02-15 19:59:42,165 WARN scheduler.TaskSetManager: Lost task 11.0 in stage 1.0 (TID 3, common4, executor 2): java.lang.RuntimeException: Hive Runtime Error while closing operators…...

解决阿里云远程连接yum无法安装问题(Ubuntu 22.04)

解决阿里云远程连接yum无法安装问题&#xff08;Ubuntu 22.04&#xff09; 第一步 进入阿里云远程连接后&#xff0c;尝试安装宝塔面包第二步&#xff1a;尝试更新软件包等一些列操作第三步&#xff1a;完成上述操作之后&#xff0c;尝试安装yum第四步&#xff1a;尝试更换清华…...

springboot 查询

ServiceImpl中 getBaseMapper()的使用 public IPage<ProductPageVO> getProductPage(Integer regionOrCityCode, Integer brandId, LocalDate usedDate, Page<ProductPageVO> page) {return getBaseMapper().getProductPage(regionOrCityCode, brandId, usedDate, …...

【分布式链路追踪技术】sleuth+zipkin

目录 1.概述 2.搭建演示工程 3.sleuth 4.zipkin 5.插拔式存储 5.1.存储到MySQL中 5.2.用MQ来流量削峰 6.联系作者 1.概述 当采用分布式架构后&#xff0c;一次请求会在多个服务之间流转&#xff0c;组成单次调用链的服务往往都分散在不同的服务器上。这就会带来一个问…...

Windows 源码编译 MariaDB

环境 Win11, vs2022, git, cmake, Bison from GnuWin32, perl, Gnu Diff. 默认都安装好。 perl 看之前博客教程。perl Bison from GnuWin32 默认安装到 C:\GnuWin32 Add C:\GnuWin32\bin to your system PATH after installation. 下载mariadb源码 地址&#xff1a;MariaD…...

【动画视频生成】

转自&#xff1a;机器之心 动画视频生成这几天火了&#xff0c;这次 NUS、字节的新框架不仅效果自然流畅&#xff0c;还在视频保真度方面比其他方法强了一大截。 最近&#xff0c;阿里研究团队构建了一种名为 Animate Anyone 的方法&#xff0c;只需要一张人物照片&#xff0…...

《Spring Cloud学习笔记:微服务保护Sentinel》

Review 解决了服务拆分之后的服务治理问题&#xff1a;Nacos解决了服务治理问题OpenFeign解决了服务之间的远程调用问题网关与前端进行交互&#xff0c;基于网关的过滤器解决了登录校验的问题 流量控制&#xff1a;避免因为突发流量而导致的服务宕机。 隔离和降级&#xff1a…...

解密负载均衡:如何平衡系统负载(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…...

go 源码解读 - sync.Mutex

sync.Mutex mutex简介mutex 方法源码标志位获取锁LocklockSlowUnlock怎么 调度 goroutineruntime 方法 mutex简介 mutex 是 一种实现互斥的同步原语。&#xff08;go-version 1.21&#xff09; &#xff08;还涉及到Go运行时的内部机制&#xff09;mutex 方法 Lock() 方法用于…...

机器学习系列--R语言随机森林进行生存分析(1)

随机森林&#xff08;Breiman 2001a&#xff09;&#xff08;RF&#xff09;是一种非参数统计方法&#xff0c;需要没有关于响应的协变关系的分布假设。RF是一种强大的、非线性的技术&#xff0c;通过拟合一组树来稳定预测精度模型估计。随机生存森林&#xff08;RSF&#xff0…...

<JavaEE> TCP 的通信机制(四) -- 流量控制 和 拥塞控制

目录 TCP的通信机制的核心特性 五、流量控制 1&#xff09;什么是“流量控制”&#xff1f; 2&#xff09;如何做到“流量控制”&#xff1f; 3&#xff09;“流量控制”的作用 六、拥塞控制 1&#xff09;什么是“拥塞控制”&#xff1f; 2&#xff09;如何做到“拥塞…...

智慧监控平台/AI智能视频EasyCVR接口调用编辑通道详细步骤

视频监控TSINGSEE青犀视频平台EasyCVR能在复杂的网络环境中&#xff0c;将分散的各类视频资源进行统一汇聚、整合、集中管理&#xff0c;在视频监控播放上&#xff0c;GB28181视频安防监控汇聚平台可支持1、4、9、16个画面窗口播放&#xff0c;可同时播放多路视频流&#xff0c…...

Java如何权衡是使用无序的数组还是有序的数组

在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括&#xff1a;采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中&#xff0c;设置任务排序规则尤其重要&#xff0c;因为它让看板视觉上直观地体…...

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器

第一章 引言&#xff1a;语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域&#xff0c;文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量&#xff0c;支撑着搜索引擎、推荐系统、…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)

宇树机器人多姿态起立控制强化学习框架论文解析 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架&#xff08;一&#xff09; 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...

爬虫基础学习day2

# 爬虫设计领域 工商&#xff1a;企查查、天眼查短视频&#xff1a;抖音、快手、西瓜 ---> 飞瓜电商&#xff1a;京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空&#xff1a;抓取所有航空公司价格 ---> 去哪儿自媒体&#xff1a;采集自媒体数据进…...

Spring数据访问模块设计

前面我们已经完成了IoC和web模块的设计&#xff0c;聪明的码友立马就知道了&#xff0c;该到数据访问模块了&#xff0c;要不就这俩玩个6啊&#xff0c;查库势在必行&#xff0c;至此&#xff0c;它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据&#xff08;数据库、No…...

算法笔记2

1.字符串拼接最好用StringBuilder&#xff0c;不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...

return this;返回的是谁

一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请&#xff0c;不同级别的经理有不同的审批权限&#xff1a; // 抽象处理者&#xff1a;审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...