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,前端如何实现即时通讯短轮询长轮询 2,websocket2.1,握手2.2,握手过程举例2.3,socket.io 3,websocket 对比 http 的优势 1,前端如何实现即时通讯 在 websocket 协议出现之前,…...
【IoT网络层】STM32 + ESP8266 +MQTT + 阿里云物联网平台 |开源,附资料|
目标:实现STM32连接阿里云物联网平台发送数据同时接收数据,IOT studio界面显示数据。具体来说:使用ESP8266 ESP-01来连接网络,获取设备数据发送到阿里云物联网平台并显示且oled显示屏当前的设备数据,通过IOT studio界面…...
数据分析工具 Top 8
你能想象一个没有工具箱的水管工吗? 没有,对吧? 数据从业者也是如此。如果没有他们的数据分析工具,数据从业者就无法分析数据、可视化数据、从数据中提取价值,也无法做数据从业者在日常工作中做的许多很酷的事情。 根据你最感兴趣的数据科学职业——数…...
AI 换脸的新时代:没有显卡也可以使用的AI换脸工具
大家好!今天,我要为大家介绍一个即使没有显卡,也能体验AI换脸的工具!是的,您没听错,无论您的电脑配置如何,只要运行在Windows 10或Windows 11上,都可以轻松使用这一神奇工具。这就是…...
3.Python中的循环结构
Python中的循环结构 一、回顾分支练习题 1、判断是否为一个合法三角形 需求:输入三角形的3边,如果两边的长度大于第三条边,则代表是一个合法三角形 # 1、提示用户输入三角形的三边长度 a = int(input(请输入第一条边的长度:)) b = int(input(请输入第二条边的长度:)) …...
机器学习之BP神经网络精讲(Backpropagation Neural Network(附案例代码))
概念 BP神经网络(Backpropagation Neural Network)是一种常见的人工神经网络,它通过反向传播算法来训练网络,调整连接权重以最小化预测输出与实际输出之间的误差。这种网络结构包含输入层、隐藏层和输出层,使用梯度下降算法来优化权重。 结构: BP神经网络(Backpropag…...
安全生产人员定位系统助企业实现智能化管理,提高生产安全性和效率
安全生产人员定位系统是基于物联网技术的系统,通过集成各种传感器和通信技术,实时监测员工的位置和活动状态。该系统可以帮助企业管理者了解员工的工作状态,及时发现潜在的安全隐患,从而采取相应的措施,保障员工的安全…...
动态规划 多源路径 字典树 LeetCode2977:转换字符串的最小成本
涉及知识点 动态规划 多源最短路径 字典树 题目 给你两个下标从 0 开始的字符串 source 和 target ,它们的长度均为 n 并且由 小写 英文字母组成。 另给你两个下标从 0 开始的字符串数组 original 和 changed ,以及一个整数数组 cost ,其中…...
Hadoop集群找不到native-hadoop
1.问题描述 hive 运行中的问题,需要把把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无法安装问题(Ubuntu 22.04) 第一步 进入阿里云远程连接后,尝试安装宝塔面包第二步:尝试更新软件包等一些列操作第三步:完成上述操作之后,尝试安装yum第四步:尝试更换清华…...
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.概述 当采用分布式架构后,一次请求会在多个服务之间流转,组成单次调用链的服务往往都分散在不同的服务器上。这就会带来一个问…...
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源码 地址:MariaD…...
【动画视频生成】
转自:机器之心 动画视频生成这几天火了,这次 NUS、字节的新框架不仅效果自然流畅,还在视频保真度方面比其他方法强了一大截。 最近,阿里研究团队构建了一种名为 Animate Anyone 的方法,只需要一张人物照片࿰…...
《Spring Cloud学习笔记:微服务保护Sentinel》
Review 解决了服务拆分之后的服务治理问题:Nacos解决了服务治理问题OpenFeign解决了服务之间的远程调用问题网关与前端进行交互,基于网关的过滤器解决了登录校验的问题 流量控制:避免因为突发流量而导致的服务宕机。 隔离和降级:…...
解密负载均衡:如何平衡系统负载(下)
🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云…...
go 源码解读 - sync.Mutex
sync.Mutex mutex简介mutex 方法源码标志位获取锁LocklockSlowUnlock怎么 调度 goroutineruntime 方法 mutex简介 mutex 是 一种实现互斥的同步原语。(go-version 1.21) (还涉及到Go运行时的内部机制)mutex 方法 Lock() 方法用于…...
机器学习系列--R语言随机森林进行生存分析(1)
随机森林(Breiman 2001a)(RF)是一种非参数统计方法,需要没有关于响应的协变关系的分布假设。RF是一种强大的、非线性的技术,通过拟合一组树来稳定预测精度模型估计。随机生存森林(RSF࿰…...
<JavaEE> TCP 的通信机制(四) -- 流量控制 和 拥塞控制
目录 TCP的通信机制的核心特性 五、流量控制 1)什么是“流量控制”? 2)如何做到“流量控制”? 3)“流量控制”的作用 六、拥塞控制 1)什么是“拥塞控制”? 2)如何做到“拥塞…...
智慧监控平台/AI智能视频EasyCVR接口调用编辑通道详细步骤
视频监控TSINGSEE青犀视频平台EasyCVR能在复杂的网络环境中,将分散的各类视频资源进行统一汇聚、整合、集中管理,在视频监控播放上,GB28181视频安防监控汇聚平台可支持1、4、9、16个画面窗口播放,可同时播放多路视频流,…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
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深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...
Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...
算法笔记2
1.字符串拼接最好用StringBuilder,不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
