WebSocket | 背景 概念 原理 使用 优缺点及适用场景

1 背景

在 WebSocket 出现之前,为了实现推送技术,所用的技术都是轮询,轮询是指浏览器每隔一段时间向服务器发出 HTTP 请求,服务器再返回最新的数据给客户端
常见的轮询方式分为轮询与长轮询,它们的区别如下图所示:
在这里插入图片描述

这种传统的模式带来很明显的缺点,即浏览器需要不断向服务器发出请求,然而 HTTP 请求与响应可能会包含较长的头部,其中真正有效的数据可能只是很少的一部分,这样会消耗很多带宽资源。因此,HTML5 定义了 WebSocket 协议,能更好地节省服务器资源和带宽,并且能够更实时地进行通讯

2 什么是WebSocket

WebSocket 是一种网络传输协议,可在单个 TCP 连接上进行全双工通信,它使得客户端和服务器之间的数据交换变得更加简单,只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输,本质上一种计算机网络应用层的协议,用来弥补 HTTP 协议在持久通信能力上的不足
它有以下特点:

  • 建立在 TCP 协议之上
  • 与 HTTP 协议有着良好的兼容性:与 HTTP 和 HTTPS 使用相同的 TCP 端口,可以绕过大多数防火墙的限制,默认端口是 80(ws) 和 443(wss,运行在 TLS 之上),并且握手阶段采用 HTTP 协议
  • 较少的控制开销:连接创建后,ws 客户端、服务端进行数据交换时,协议控制的数据包头部较小,而 HTTP 协议每次通信都需要携带完整的头部
  • 可以发送文本,也可以发送二进制数据
  • 没有同源限制,客户端可以与任意服务器通信
  • 协议标识符是 ws,如果加密则为 wss,服务器网址就是 URL
  • 支持扩展:ws 协议定义了扩展,用户可以扩展协议,或者实现自定义的子协议,比如支持自定义压缩算法

3 原理

3.1 建立连接

在 WebSocket 开始通信之前,通信双方需要先进行握手,WebSocket复用了 HTTP 的握手通道,即客户端通过 HTTP 请求与 WebSocket 服务端协商升级协议,协议升级完成后,后续的数据交换则遵照 WebSocket 的协议
利用 HTTP 完成握手有以下好处:

  • 可以让 WebSocket 和 HTTP 基础设备兼容(运行在 80 端口 或 443 端口)
  • 可以复用 HTTP 的 Upgrade 机制,完成升级协议的协商过程

3.2 交换数据

WebSocket 的每条消息可能会被切分成多个数据帧,发送端会将消息切割成多个帧发送给接收端,接收端接收消息帧,并将关联的帧重新组装成完整的消息
以下是MDN 上的示例:

Client: FIN=1, opcode=0x1, msg="hello"
Server: (process complete message immediately) Hi.Client: FIN=0, opcode=0x1, msg="and a"
Server: (listening, newmessage containing text started)Client: FIN=0, opcode=0x0, msg="happy new"
Server: (listening, payload concatenated to previous message)Client: FIN=1, opcode=0x0, msg="year!"
Server: (process complete message) Happy new year to you too!

在该示例中,客户端向服务器发送了两条消息,第一个消息在单个帧中发送,而第二个消息跨三个帧发送。当 WebSocket 的接收方收到一个数据帧时,会根据 FIN 字段值来判断是否收到消息的最后一个数据帧;利用 FIN 和 Opcode,我们就可以实现跨帧发送消息
其中 Opcode 表示操作码,它的可能值有:

  • 0x1:传输数据是文本
  • 0x2:传输数据是二进制数据
  • 0x0:表示该帧是一个延续帧,这意味着服务器应该将帧的数据连接到从该客户端接收到的最后一个帧
  • 0x3-7:保留的操作代码,用于后续定义的非控制帧
  • 0x8:表示连接断开
  • 0x9:表示这是一个心跳请求ping
  • 0xA:表示这是一个心跳响应pong
  • 0xB-F:保留的操作代码,用于后续定义的控制帧

具体的数据帧格式大概如下图所示,单位是比特:

  • FIN:1 个比特,值为 1 表示这是消息的最后一帧,为 0 则不是
  • RSV1, RSV2, RSV3:各占 1 个比特,一般情况下全为 0,非零值表示采用 WebSocket 扩展
  • Mask: 1 个比特,表示是否要对数据进行掩码操作
  • Payload length:数据负载的长度,单位是字节,为 7 位,或 7+16 位,或 1+64 位
  • Masking-key:0 或 4 字节(32 位),所有从客户端传送到服务端的数据帧,数据都进行了掩码操作,Mask 为 1,且携带了 4 字节的 Masking-key;如果 Mask 为 0,则没有 Masking-key
  • Payload data:具体数据
    在这里插入图片描述

3.3 维持连接

使用 WebSocket 进行通信,可以通过建立心跳机制来判断连接正常没有断开或者服务是否可用,所谓心跳机制,就是定时发送一个数据包,让对方知道自己在线且正常工作,确保通信有效;如果对方无法响应,便可以弃用旧连接,发起新的连接了
需要重连的场景可能包括:网络问题或者机器故障导致连接断开、连接没断但不可用了或者连接对端的服务不可用

发送方 -> 接收方:ping
接收方 -> 发送方:pong

ping 、pong 的操作,对应的是 WebSocket 的两个控制帧,Opcode 分别是 0x9、0xA。比如说,WebSocket 服务端向客户端发送 ping:

// ping
ws.ping()
// pong
ws.on('pong', () => {console.log('pong received')
})

客户端也可以发送:

// 发送心跳包
ws.send('heart check')
// 接收响应
ws.onmessage = (e) => {const response = e.dataif (response.message === 'connection alive') {// 重置计时器}
}

4 WebSocket使用

以下是一个 Vue + WebSocket 使用的 demo,逐步解释每个部分是如何工作的,将从 建立连接 到 发送消息和接收消息 全面讲解 WebSocket 的使用

4.1 创建 WebSocket 客户端

1)创建 WebSocket 实例
创建一个 WebSocket 连接,并连接到ws://localhost:8080 这个 WebSocket 服务器地址。这个地址指向服务器上的 WebSocket 服务,如果服务器成功启动并运行在 8080 端口上,客户端会建立连接

const socket = new WebSocket('ws://localhost:8080')

2)监听 WebSocket 事件

  • onopen:连接建立成功时触发
  • onmessage:接收到消息时触发
  • onclose:连接关闭时触发
  • onerror:发生错误时触发

onopen触发后,我们将连接状态设置为“连接已建立”,并发送一条初始化消息到服务器

socket.onopen = () => {console.log('WebSocket 连接已建立')connectionStatus.value = '连接已建立'socket.send('客户端已连接,发送初始化消息')
}

onmessage处理函数每次收到服务器消息时都会执行,将接收到的消息显示到页面上

socket.onmessage = (event) => {console.log('从服务器接收到消息:', event.data)receivedMessage.value = event.data // 将收到的消息存储到响应式变量中
}

3)发送消息
通过 WebSocket 的send()方法发送消息,sendMessage()函数会在点击按钮时触发,检查 WebSocket 是否已经连接 (readyState === WebSocket.OPEN),如果连接成功,则发送消息

function sendMessage() {if (socket.readyState === WebSocket.OPEN) {const message = '这是客户端发送的消息'socket.send(message)console.log('已发送消息:', message)} else {console.log('WebSocket 连接未打开,无法发送消息')}
}

4)状态消息
使用 ref 来处理 WebSocket 连接的状态

  • connectionStatus用来显示 WebSocket 连接的状态,如“未连接”、“连接已建立”或“连接已关闭”
  • receivedMessage用来显示从服务器接收到的消息
const connectionStatus = ref('未连接')
const receivedMessage = ref('等待服务器消息...')

整体 vue 代码如下:

<template><div><h2>WebSocket 使用示例</h2><!-- 显示当前连接状态 --><p>连接状态: {{ connectionStatus }}</p><!-- 按钮触发发送消息 --><button @click="sendMessage">发送消息</button><!-- 显示从服务器接收到的消息 --><p><strong>从服务器接收到的消息:</strong> {{ receivedMessage }}</p></div>
</template><script setup>
import { ref } from 'vue'// 设置 WebSocket 相关状态
const connectionStatus = ref('未连接')
const receivedMessage = ref('等待服务器消息...')// 创建 WebSocket 实例,连接到 WebSocket 服务器
// 确保 WebSocket 服务器已经在 8080 端口上运行
const socket = new WebSocket('ws://localhost:8080')// 监听 WebSocket 连接打开事件
socket.onopen = () => {console.log('WebSocket 连接已建立')connectionStatus.value = '连接已建立'socket.send('客户端已连接,发送初始化消息')
};// 监听 WebSocket 接收到消息事件
socket.onmessage = (event) => {console.log('从服务器接收到消息:', event.data)receivedMessage.value = event.data // 将收到的消息存储到响应式变量中
};// 监听 WebSocket 关闭事件
socket.onclose = () => {console.log('WebSocket 连接已关闭')connectionStatus.value = '连接已关闭'
};// 监听 WebSocket 错误事件
socket.onerror = (error) => {console.log('WebSocket 出现错误:', error)connectionStatus.value = '连接错误'
};// 发送消息的函数
function sendMessage() {if (socket.readyState === WebSocket.OPEN) {const message = '这是客户端发送的消息'socket.send(message)console.log('已发送消息:', message)} else {console.log('WebSocket 连接未打开,无法发送消息')}
}
</script><style scoped>
h2 {color: #42b983;
}button {margin-top: 10px;padding: 10px;background-color: #42b983;color: white;border: none;cursor: pointer
}button:hover {background-color: #36a372;
}
</style>

4.2 运行 WebSocket 服务器

以下是一个简单的 WebSocket 服务器,使用 Node.js 和 ws 库
1)安装 ws 包

npm install ws

2)WebSocket 服务器代码

// server.js
const WebSocket = require('ws')// 创建 WebSocket 服务器,监听 8080 端口
const wss = new WebSocket.Server({ port: 8080 })// 当有客户端连接时触发
wss.on('connection', (ws) => {console.log('客户端已连接')// 向客户端发送一条消息ws.send('欢迎连接 WebSocket 服务器!')// 监听客户端发送的消息ws.on('message', (message) => {console.log('收到来自客户端的消息:', message)// 将收到的消息原封不动地返回给客户端ws.send(`服务器已收到: ${message}`)});// 处理连接关闭ws.on('close', () => {console.log('客户端已断开连接')});
});console.log('WebSocket 服务器正在监听端口 8080...')

3)启动 WebSocket 服务器
在 Node.js 项目对应的目录中,运行以下命令来启动服务器,xxx是该 js 文件名

node xxx.js

4.3 运行 Vue 项目

运行 Vue 项目后,在浏览器中打开 Vue 页面后,大致连接流程如下:
1)WebSocket 连接的状态:未连接 → 连接已建立
2)点击按钮可以发送消息到 WebSocket 服务器
3)从 WebSocket 服务器接收到的响应消息,展示到页面中
在这里插入图片描述

5 优缺点及适用场景

优点:

  • 实时性: WebSocket 提供了双向通信,服务器可以主动向客户端推送数据,实时性非常高,适用于实时聊天、在线协作等应用
  • 减少网络延迟: 与轮询和长轮询相比,WebSocket 可以显著减少网络延迟,因为不需要在每个请求之间建立和关闭连接
  • 较小的数据传输开销: WebSocket 的数据帧相比于 HTTP 请求报文较小,减少了在每个请求中传输的开销,特别适用于需要频繁通信的应用
  • 较低的服务器资源占用: 由于 WebSocket 的长连接特性,服务器可以处理更多的并发连接,相较于短连接有更低的资源占用
  • 跨域通信: 与一些其他跨域通信方法相比,WebSocket 更容易实现跨域通信

缺点:

  • 连接状态保持: 长时间保持连接可能会导致服务器和客户端都需要维护连接状态,可能增加一些负担
  • 不适用于所有场景: 对于一些请求-响应模式较为简单的场景,WebSocket 的实时特性可能并不是必要的,使用 HTTP 请求可能更为合适
  • 复杂性: 与传统的 HTTP 请求相比,WebSocket 的实现和管理可能稍显复杂,尤其是在处理连接状态、异常等方面

适用场景:

  • 实时聊天应用: WebSocket 是实现实时聊天室、即时通讯应用的理想选择,因为它能够提供低延迟和高实时性
  • 在线协作和协同编辑: 对于需要多用户协同工作的应用,如协同编辑文档或绘图,WebSocket 的实时性使得用户能够看到其他用户的操作
  • 实时数据展示: 对于需要实时展示数据变化的应用,例如股票行情、实时监控系统等,WebSocket 提供了一种高效的通信方式
  • 在线游戏: 在线游戏通常需要快速、实时的通信,WebSocket 能够提供低延迟和高并发的通信能力
  • 推送服务: 用于实现消息推送服务,向客户端主动推送更新或通知

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

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

相关文章

硬件设计-传输线匹配

目录 简介&#xff1a; 主题&#xff1a; 终端匹配 始端匹配 始端匹配的阻值 始端匹配的输出驱动电流 中间匹配 电阻阻值的选择 简介&#xff1a; 系统何时需要匹配电阻&#xff1f;按照第四章的内容来看有两种情况&#xff1a;长线传输造成信号反射的情况和短线传输造成…

设计模式的主要分类是什么?请简要介绍每个分类的特点。

大家好&#xff0c;我是锋哥。今天分享关于【设计模式的主要分类是什么&#xff1f;请简要介绍每个分类的特点。】面试题。希望对大家有帮助&#xff1b; 设计模式的主要分类是什么&#xff1f;请简要介绍每个分类的特点。 1000道 互联网大厂Java工程师 精选面试题-Java资源分…

基于微信小程序的校园访客登记系统

基于微信小程序的校园访客登记系统 功能列表 用户端功能 注册与登录 &#xff1a;支持用户通过手机号短信验证码注册和登录。个人资料管理 &#xff1a;允许用户编辑和更新个人信息及其密码。站内信消息通知&#xff1a;通知公告。来访预约&#xff1a;提交来访预约支持车牌…

重温设计模式--观察者模式

文章目录 观察者模式&#xff08;Observer Pattern&#xff09;概述观察者模式UML图作用&#xff1a;实现对象间的解耦支持一对多的依赖关系易于维护和扩展 观察者模式的结构抽象主题&#xff08;Subject&#xff09;&#xff1a;具体主题&#xff08;Concrete Subject&#xf…

CH32V307VCT6---工程template创建

一、硬件&#xff1a;沁恒官网申请的CH32V307VCT6开发板 二、开发环境&#xff1a;Mounriver 三、最终效果 1.PB9连接LED1&#xff0c;使其闪烁 2.OLED屏幕显示&#xff1a;软件IIC&#xff0c;PB10----SDA&#xff0c;PB11---SCL 3.工程链接&#xff1a;CH32V307VCT6 lo…

分布式协同 - 分布式事务_2PC 3PC解决方案

文章目录 导图Pre2PC&#xff08;Two-Phase Commit&#xff09;协议准备阶段提交阶段情况 1&#xff1a;只要有一个事务参与者反馈未就绪&#xff08;no ready&#xff09;&#xff0c;事务协调者就会回滚事务情况 2&#xff1a;当所有事务参与者均反馈就绪&#xff08;ready&a…

【软考高级】系统架构设计师复习笔记-精华版

文章目录 前言0 系统架构设计师0.1 考架构还是考系分0.2 架构核心知识0.3 架构教材变化 1 计算机操作系统1.1 cpu 组成1.2 内核的五大功能1.3 流水线技术1.4 段页式存储1.5 I/O 软件1.6 文件管理1.7 系统工程相关 2 嵌入式2.1 嵌入式技术2.2 板级支持包&#xff08;BSP&#xf…

图解HTTP-HTTP报文

参考资料&#xff1a;图解HTTP HTTP报文 用于HTTP协议交互的信息被称为HTTP报文。请求端的HTTP请求报文&#xff0c;响应端&#xff08;服务器端&#xff09;的叫做响应报文。HTTP报文本身是由多行&#xff08;CR LF作为换行符&#xff09;数据行构成的文本。 请求报文及响…

Linux -- 同步与条件变量

目录 同步 条件变量 pthread_cond_t pthread_cond_init&#xff08;初始化条件变量&#xff09; pthread_cond_destroy&#xff08;销毁条件变量&#xff09; pthread_cond_wait&#xff08;线程等待条件变量&#xff09; 重要提醒 pthread_cond_boardcast&#xff08…

【源码编译】windows下mingw64安装以及cmake调用

最近因为安装MIRTK库&#xff0c;太多第三方依赖了&#xff0c;太折磨了&#xff0c;学习了使用Cmake&#xff0c;有些库又需要Fortran编译器&#xff0c;VS2022里面装了但又调用不了&#xff0c;也不知道为什么&#xff0c;最后装的mingw64&#xff0c;记录一下。 1、mingw64安…

6、mysql的MHA故障切换

MHA的含义 MHA&#xff1a;master high availability&#xff0c;建立在主从复制基础上的故障切换的软件系统。 主从复制的单点问题&#xff1a; 当主从复制当中&#xff0c;主服务器发生故障&#xff0c;会自动切换到一台从服务器&#xff0c;然后把从服务器升格成主&…

LeetCode:104.二叉树的最大深度

跟着carl学算法&#xff0c;本系列博客仅做个人记录&#xff0c;建议大家都去看carl本人的博客&#xff0c;写的真的很好的&#xff01; 代码随想录 LeetCode&#xff1a;104.二叉树的最大深度 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节…

WebRTC服务质量(12)- Pacer机制(04) 向Pacer中插入数据

WebRTC服务质量&#xff08;01&#xff09;- Qos概述 WebRTC服务质量&#xff08;02&#xff09;- RTP协议 WebRTC服务质量&#xff08;03&#xff09;- RTCP协议 WebRTC服务质量&#xff08;04&#xff09;- 重传机制&#xff08;01) RTX NACK概述 WebRTC服务质量&#xff08;…

双指针——快乐数

一.题目描述 202. 快乐数 - 力扣&#xff08;LeetCode&#xff09; 二.题目解析 我们要判断一个数是不是快乐数要通过它的三个性质来进行判断。这个数会一直变化&#xff0c;由它的各个位的平方和重新构成这个数。如果这个数在变化的过程中变成了1&#xff0c;那么就是快乐数…

【玩转OCR】 | 腾讯云智能结构化OCR在多场景的实际应用与体验

文章目录 引言产品简介产品功能产品优势 API调用与场景实践图像增强API调用实例发票API调用实例其他场景 结语相关链接 引言 在数字化信息处理的时代&#xff0c;如何高效、精准地提取和结构化各类文档数据成为了企业和政府部门的重要需求。尤其是在面对海量票据、证件、表单和…

nginx-rtmp服务器搭建

音视频服务器搭建 本文采用 nginx/1.18.0和nginx-rtmp-module模块源代码搭建RTMP流媒体服务器 流程 查看当前服务器的nginx版本下载nginx和nginx-rtmp-module源代码重新编译nginx&#xff0c;并进行相关配置&#xff08;nginx.conf、防火墙等&#xff09;客户端测试连接测试搭…

借助Aspose.html控件, 使用 Java 编程将 HTML 转换为 BMP

Aspose.HTML for .NET 不仅提供超文本标记语言 ( HTML ) 文件处理&#xff0c;还提供流行图像文件格式之间的转换。您可以利用丰富的渲染和转换功能将SVG文件渲染为PNG、JPG或其他广泛使用的文件格式。但是&#xff0c;我们将使用此C# 图像处理库以编程方式在 C# 中将 SVG 转换…

区块链期末复习1.1:密码学哈希函数

一、哈希函数应该具备的三个特性 1.输入可以为任意长度的字符串 2.产生固定大小输出&#xff08;比如256位&#xff09; 3.能进行有效计算。对于n位字符串&#xff0c;可以在O(n)的时间内计算出哈希值。 二.加密哈希函数的三个特性 1.collision-resistance(碰撞阻力&#x…

华为:数字化转型只有“起点”,没有“终点”

上个月&#xff0c;我收到了一位朋友的私信&#xff0c;他询问我是否有关于华为数字化转型的资料。幸运的是&#xff0c;我手头正好收藏了一些&#xff0c;于是我便分享给他。 然后在昨天&#xff0c;他又再次联系我&#xff0c;并感慨&#xff1a;“如果当初我在进行企业数字…

Android基于Path的addRoundRect,Canvas剪切clipPath简洁的圆形图实现,Kotlin(2)

Android基于Path的addRoundRect&#xff0c;Canvas剪切clipPath简洁的圆形图实现&#xff0c;Kotlin&#xff08;2&#xff09; import android.content.Context import android.graphics.BitmapFactory import android.graphics.Canvas import android.graphics.Path import a…