Websocket解析及用法(封装一个通用订阅发布主题的webSocket类)
1、什么是WebSocket?
websocket的目标是通过一个长连接实现与服务器全双工,双向的通信。是一种在单个TCP连接上进行全双工通信的协议,使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 js中创建websocket时,一个http请求会发送到服务器初始化连接,服务器响应后,连接使用http的upgrade头部从http协议切换到websocket协议,这意味着websocket不能通过标准的http服务器完成,而必须使用支持websocket协议的专有服务器。因为websocket使用了自定义协议,所以url方案稍有变化,不能再使用http://或https://,而要使用我是:ws://和wss://。前者是不安全的连接,后者是安全的。
优点 | 缺点 |
---|---|
基于TCP协议: WebSocket建立在TCP之上,这使得服务器端的实现相对容易。 | 安全性:WebSocket 使用的是持久性连接,连接建立后会长时间保持打开状态。会增加服务器资源的消耗,WebSocket 协议的双向通信机制,它可能会面临安全隐患,例如 XSS 和 CSRF 攻击等。 |
与HTTP兼容性良好: WebSocket与HTTP协议兼容,使用HTTP协议进行握手阶段,因此默认端口与HTTP相同(80和443),且不易被屏蔽。这意味着它可以通过各种HTTP代理服务器,增加了通信的灵活性。 | 浏览器兼容性:旧版浏览器中可能会出现兼容性问题。 |
轻量级数据格式和高效通信: 在连接创建后,持久保存连接状态,并且交换数据时,用于协议控制的数据包头部相对较小 | 数据包大小的限制: WebSocket 协议发送的数据包不能超过 2GB。 |
支持文本和二进制数据:WebSocket不仅可以发送文本数据,还可以发送二进制数据,相对 HTTP,可以更轻松地处理二进制内容。 | 服务器需求: WebSocket 协议需要服务器支持,这意味着需要更高效的服务器硬件和软件。 |
无同源限制:与传统的AJAX请求不同,WebSocket没有同源限制,客户端可以与任意服务器通信,不需要处理跨域 |
2、怎么理解WebSocket,可以用在什么场景?
首先,我们思考一个问题,当前端需要展示实时数据,或实现实时聊天功能,我们怎么设计数据请求方案?
按照传统的解决方法就是利用轮询和长轮询的方案,现在我们来对比一下这三种方案的异同:
轮询 | 长轮询 | websocket | |
---|---|---|---|
方案思路 | 客户端定时向服务器发送Ajax请求,服务器接到请求后马上返回响应信息并关闭连接![]() | 客户端向服务器发送Ajax请求,服务器接到请求后hold住连接,直到有新消息才返回响应信息并关闭连接,客户端处理完响应信息后再向服务器发送新的请求![]() | 客户端向服务器发起websocket请求建立全双工通信连接,一旦服务器有数据更新,服务器可以主动给客户端发送数据,无需客户端询问![]() |
优缺点对比 | 后端程序编写比较容易,但是请求中有大半是无用,浪费带宽和服务器资源 | 在无消息的情况下不会频繁的请求,但是服务器hold连接会消耗资源 | 轻量级数据格式和高效通信,支持文本和二进制数据,无同源限制,但是旧版浏览器中可能会出现兼容性问题, WebSocket 协议发送的数据包不能超过 2GB |
3、WebSocket对象详解
3-1、websocket()
构造函器会返回一个 WebSocket 对象。
var aWebSocket = new WebSocket(url [, protocols]);
参数名称 | 是否必填 | 释义 |
---|---|---|
url | 是 | 要连接的 URL;这应该是 WebSocket 服务器将响应的 URL。 |
protocol | 否 | 一个人类可读的字符串,它解释了连接关闭的原因。这个 UTF-8 编码的字符串不能超过 123 个字节。 |
3-2、api
WebSocket.close() 关闭 WebSocket 连接或连接尝试(如果有的话)。如果连接已经关闭,则此方法不执行任何操作;
参数名称 | 是否必填 | 释义 |
---|---|---|
code | 否 | 一个数字状态码,它解释了连接关闭的原因。如果没有传这个参数,默认使用 1005。 |
reason | 否 | 一个协议字符串或者一个包含协议字符串的数组。这些字符串用于指定子协议,这样单个服务器可以实现多个 WebSocket 子协议(例如,你可能希望一台服务器能够根据指定的协议(protocol)处理不同类型的交互)。如果不指定协议字符串,则假定为空字符串。 |
WebSocket.send() 将需要通过 WebSocket 链接传输至服务器的数据排入队列,并根据所需要传输的 data bytes 的大小来增加 bufferedAmount的值。若数据无法传输(例如数据需要缓存而缓冲区已满)时,套接字会自行关闭。
参数名称 | 是否必填 | 释义 |
---|---|---|
data | 是 | 用于传输至服务器的数据。 |
3-3、属性
WebSocket.binaryType:返回 websocket 连接所传输二进制数据的类型。
WebSocket.bufferedAmount:一个只读属性,用于返回已经被send()方法放入队列中但还没有被发送到网络中的数据的字节数。一旦队列中的所有数据被发送至网络,则该属性值将被重置为 0。但是,若在发送过程中连接被关闭,则属性值不会重置为 0。如果你不断地调用send(),则该属性值会持续增长。
WebSocket.extensions:是只读属性,返回服务器已选择的扩展值。目前,链接可以协定的扩展值只有空字符串或者一个扩展列表。
WebSocket.protocol 是个只读属性,用于返回服务器端选中的子协议的名字;这是一个在创建 WebSocket 对象时,在参数 protocols 中指定的字符串,当没有已建立的链接时为空串。
WebSocket.readyState返回当前 WebSocket 的链接状态,只读。
值 | 释义 |
---|---|
0 | 正在链接中 |
1 | 已经链接并且可以通讯 |
2 | 连接正在关闭 |
3 | 连接已关闭或者没有链接成功 |
WebSocket.url是一个只读属性,返回值为当构造函数创建WebSocket实例对象时 URL 的绝对路径。
3-4、事件
WebSocket.onclose:属性返回一个事件监听器,这个事件监听器将在 WebSocket 连接的readyState 变为 3时被调用,它接收一个名字为“close”的 CloseEvent 事件。
WebSocket.onerror:当websocket的连接由于一些错误事件的发生 (例如无法发送一些数据) 而被关闭时,一个error事件将被引发。
WebSocket.onmessage:message 事件会在 WebSocket 接收到新消息时被触发。
WebSocket.onopen:属性定义一个事件处理程序,当WebSocket 的连接状态readyState 变为1时调用;这意味着当前连接已经准备好发送和接受数据。
4、怎么保持WebSocket长时间连接,不断线?
首先我们来探讨一下为什么websocket断线的原因有哪些,由于websocket连接是基于一个tcp连接上的长时间全双工双向通信,所以断线的原因有下几种可能(个人猜测):
1、网络连接:网络连接不稳定或出现故障,可能会导致WebSocket断线;
2、服务器:WebSocket服务器出现故障或过载,可能会导致WebSocket断线;
3、防火墙问题:防火墙可能会阻止WebSocketClient与服务器之间的通信,导致断线。
4、超时问题:WebSocket在一段时间内没有收到服务器发送的数据,则可能会断开连接。
5、客户端异常中断。
如何解决断线问题?
1、心跳检测机制:通过定期发送心跳消息,可以确保客户端和服务器之间的连接处于活跃状态。如果一段时间内未收到来自客户端的心跳消息,服务器可以认为客户端已经断线,并采取相应的措施,例如关闭连接或重新建立连接。
2、连接状态管理:在客户端代码中维护连接的状态信息,以便及时检测连接的断开和重新连接的状态变化。这样可以使应用程序更容易地处理连接断开和重新连接时的逻辑。
3、异常处理 :及时捕获和处理在连接过程中可能出现的异常情况,例如网络超时、连接被拒绝等,以提高系统的稳定性和可靠性。
心跳检测机制:
之所以称之为心跳检测是因为:它像心跳一样每隔固定时间发一次,以此来告诉服务器,这个客户端还活着。实际上,这种机制是为了维持长连接而设计的。通常情况下,心跳包的内容并没有特别的规定,通常是一个很小的数据包,甚至可能只包含包头的空包。
心跳检测步骤:
1、客户端会定期发送一个探测包(心跳包)给服务器,以确保连接的活跃性。
2、在发送探测包的同时,客户端会启动一个超时定时器,以便在规定的时间内等待服务器的响应。
3、当服务器接收到客户端发送的探测包时,会立即回应一个确认包,以表明服务器正常接收到了探测包。
4、如果客户端收到服务器的应答包,则说明服务器正常:客户端在收到服务器的确认包后,会立即删除之前启动的超时定时器,表明服务器正常运行。
5、如果客户端的超时定时器超时,仍未收到应答包,则说明服务器挂了:如果客户端的超时定时器到期时仍未收到服务器的确认包,则客户端会认为服务器已经挂了,进而采取相应的措施,例如重新连接或者进行错误处理。
5、封装一个通用的推送、订阅主题的webSocket的类
// 先定义一个基础webSocket类
class WebsocketClient {// 连接的URL_host = "";// websocket对象ws = null;// 传入的websocket的方法函数,事件回调函数_insOwner = null;constructor(host: string, ins) {this._host = host;this._insOwner = ins;this.initWebsocket();}// 初始化创建websocket对象initWebsocket() {if (this.ws != null) {this.ws.close();}try {this.ws = new WebSocket(this._host);} catch (error) {console.error("create websocket error: ", error.toString());if (this._insOwner && typeof this._insOwner.onDisconnect == "function") {// websocket连接失败处理函数this._insOwner.onDisconnect();}return;}this.ws.onopen = () => {//开始连接if (this._insOwner && typeof this._insOwner.onConnect == "function") {this._insOwner.onConnect();}};this.ws.onerror = (error) => {console.error("websocket error: ", error);};this.ws.onclose = (e) => {console.error("websocket onclose: ", e);if (this._insOwner && typeof this._insOwner.onDisconnect == "function") {this._insOwner.onDisconnect(e);}};this.ws.onmessage = (message) => {if (this._insOwner && typeof this._insOwner.onMessage == "function") {this._insOwner.onMessage(message.data);}};}// 发送消息sendMessage(message) {console.log(this.ws, this.ws.readyState, message);// WebSocket 是否处于打开状态 readyState === 1//CONNECTING:值为0,表示正在连接。//OPEN:值为1,表示连接成功,可以通信了。//CLOSING:值为2,表示连接正在关闭。//CLOSED:值为3,表示连接已经关闭,或者打开连接失败。if (this.ws && this.ws.readyState == 1) {this.ws.send(message);return true;}return false;}// 关闭连接dispose() {this._host = "";this._insOwner = null;if (this.ws != null) {this.ws.close();this.ws = null;}}
}
在此基础类之上定义一个可发布,订阅消息主题的类
什么叫消息主题?在真实开发中,我们接收websocket信息时,后端会在一个ws连接里推送不同页面的信息,这个时候我们就要判断当前推送的信息是不是当前页面要用的信息,如果是就调用接收信息的回调,不是就忽略。示例如下:
可发布,订阅消息主题的类代码如下
interface UpcWebsocketEvent {type: UpcWebsocketEventType;data: UpcWebsocketEventInfo;
}interface UpcWebsocketEventInfo {isReconnect: boolean; // 是否是重连reconnectable: boolean; // 是否自动重连reconnectDivide: number; // 重连间隔 SreconnectMaxTimes?: number; // 最大重连次数reconnectTimes?: number; // 当前重连次数
}export enum UpcWebsocketEventType {Connected,Disconnect,Reconnect,
}export class UpcWebsocketClient {_client: any = null; // 存储WebSocket客户端实例_url = "";_urlGenerator = null; // url 生成器isConnect = false; //连接状态reConnectNum = 0; //重连次数reConnectMaxNum = 3; // 最大重连次数// 登录相关_logined = false;_loginTimer = null;_loginResolve = null;// 心跳相关_heartTimer = null;_heartBeatResolve = null;_heartBeatResolveTimer = null;// 重连句柄_autoReloginTimer = null;_autoReloginDivide = 10; // 每隔 10 S 发起一次重连// 实例事件, 需全局订阅_instanceEventCallback: ((event: UpcWebsocketEvent) => void) | null = null;// 主题订阅_subIndex = 0;_subMap = new Map<string, Map<number, (data: any) => void>>(); // Map<topic, Map<subIndex, listner>>constructor(urlGenerator) {if (typeof urlGenerator != "function") {throw new Error("urlGenerator is not function");}this._urlGenerator = urlGenerator;}onInstanceEvent(fn: (event: UpcWebsocketEvent) => void) {if (typeof fn == "function") {this._instanceEventCallback = fn;}}emitInstanceEvent(type: UpcWebsocketEventType, data: UpcWebsocketEventInfo) {if (typeof this._instanceEventCallback == "function") {this._instanceEventCallback({ type, data });}}
// 根据基础类创建websocket对象async createWebsocket() {// this._client存储创建出来的websocket对象索引if (this._client != null) {this._client.dispose();this._client = null;}// 拿到websocket连接url,一般都是要携带token的,所以拿url的方法要抽离出类之外,通过构造函数传入const result = await this._urlGenerator();if (result.code != 0) {return { code: result.code, msg: `获取推送服务认证口令失败` };}this._url = result.data; // `${this._host}/ws?token=${this._token}`;return new Promise((resolve: (connected: boolean) => void) => {this._loginResolve = resolve;this._client = new WebsocketClient(this._url, this);});}// 开始连接,如果连接失败,将尝试自动重连,开启连接async start() {const connected = await this.createWebsocket();// 判断ws对象是否创建成功,否就重连创建,是就开启心跳检测机制if (!connected) {this.autoRelogin();} else {this.keepHeartBeat(); // 开始心跳}}// 暂停连接,清理相关资源pause() {// 清理 socketthis._client && this._client.dispose();this._client = null;// 如果有心跳,取消心跳this._heartTimer && clearTimeout(this._heartTimer);this._heartTimer = null;// 如果心跳正在进行, 取消回调this._heartBeatResolveTimer && clearTimeout(this._heartBeatResolveTimer);this._heartBeatResolveTimer = null;this._heartBeatResolve = null;this._loginResolve = null;// 如果在重连, 去掉重连this._autoReloginTimer && clearTimeout(this._autoReloginTimer);this._autoReloginTimer = null;// 清理主题订阅this._subIndex = 0;this._subMap.clear();this.isConnect = false; //连接状态this.reConnectNum = 0;return;}// 重连,每隔一定时间尝试重新连接async autoRelogin() {this._autoReloginTimer = setTimeout(async () => {this._autoReloginTimer = null;const ret = await this.createWebsocket();if (!ret) {return this.autoRelogin();} else {this.keepHeartBeat(); // 开始心跳}this.isConnect = true;// 重连成功this.emitInstanceEvent(UpcWebsocketEventType.Reconnect, {msg: "",relogin_divide: this._autoReloginDivide,});}, this._autoReloginDivide * 1000);}// 建立连接,连接建立成功之后要做的事情,基础类的onopen事件回调函数onConnect() {console.log(`webSocket已连接`);this.isConnect = true;this.reConnectNum = 0;typeof this._loginResolve == "function" && this._loginResolve(true);this._loginResolve = null;this.recoverSubscribe(); // 恢复订阅}// WebSocket连接断开时调用,清理资源,基础类的onclose事件回调函数onDisconnect(e?) {console.log(`webSocket已经关闭 ${e} `);// 清理 socketthis._client && this._client.dispose();this._client = null;// 如果有心跳,取消心跳this._heartTimer && clearTimeout(this._heartTimer);this._heartTimer = null;// 如果心跳正在进行, 取消回调this._heartBeatResolveTimer && clearTimeout(this._heartBeatResolveTimer);this._heartBeatResolveTimer = null;this._heartBeatResolve = null;// 触发掉线通知掉线this.emitInstanceEvent(UpcWebsocketEventType.Disconnect, {msg: "",relogin_divide: this._autoReloginDivide,});//被动断开,重新连接if (e && e?.code) {this.autoRelogin();console.log("websocket连接不上,请重新登录或联系开发人员!");}}// 接收到的 WebSocket消息,解析消息并调用相应的处理函数,基础类的onmessage事件回调函数onMessage(message: string) {const msg = JSON.parse(message);// console.log("onMessage", msg);if (!msg.channel) {console.error("onMessage error ");return;}if (!(typeof msg.text == "string" && JSON.parse(msg.text).action_ans == "subscriber successed!")) {this.handlePublish(msg);}}// 使用nginx代理webSocket链接,客户端和服务器握手成功后,如果在60s时间内没有数据交互,连接就会自动断开// 心跳检测,定期 30s 发送心跳请求并处理心跳响应keepHeartBeat() {this._heartTimer = setTimeout(async () => {this._heartTimer = null;const ret = await this.sendHeartBeat();// if (ret?.code == 1) {// return this.onDisconnect();// }return this.keepHeartBeat();}, 30 * 1000); // }// 发送心跳请求,返回一个Promise,用于处理心跳响应sendHeartBeat() {return new Promise((resolve) => {this._heartBeatResolve = resolve;this._heartBeatResolveTimer = setTimeout(() => {this._heartBeatResolveTimer = null;if (this._heartBeatResolve !== null) {this._heartBeatResolve({ code: 100, ret_msg: "sendHeartBeat timeout", data: null });this._heartBeatResolve = null;}}, 10 * 1000);const reqData = {action: "req",channel: "ping",};this._client && this._client.sendMessage(JSON.stringify(reqData));});}// handlePublish(msg) {const topic = msg.channel;const handleMap = this._subMap.get(topic);if (handleMap != null) {handleMap.forEach((fn) => {try {fn(JSON.parse(msg.text));} catch (e) {console.log(e);}});}}// 恢复所有主题的订阅recoverSubscribe() {this._subMap.forEach((handleMap, topic) => {if (handleMap != null && handleMap.size > 0) {const reqData = {action: "sub",channel: topic,data: {},};this._client && this._client.sendMessage(JSON.stringify(reqData));}});}/*** 订阅指定主题,并注册事件监听器。* @param topic 订阅的主题字符串。* @param listener 当订阅主题触发时执行的回调函数。* @returns 返回一个句柄,用于取消订阅。*/subscribe(topic: string, listener: () => void) {if (typeof listener != "function") return;let handleMap = this._subMap.get(topic);if (handleMap == null) {handleMap = new Map();this._subMap.set(topic, handleMap);}const handle = ++this._subIndex;handleMap.set(handle, listener);if (handleMap.size == 1) {// 初始添加订阅, 需要向后台发送消息const reqData = {action: "sub",channel: topic,data: {},};this._client && this._client.sendMessage(JSON.stringify(reqData));}return handle;}/*** 取消订阅指定主题或删除指定句柄对应的监听器。* @param topic 要取消订阅的主题字符串。* @param handle 可选,取消特定句柄对应的监听器;若未提供,则取消该主题下的所有监听器。*/unsubscribe(topic, handle?) {const handleMap = this._subMap.get(topic); // 获取对应主题的订阅if (handleMap == null || handleMap.size == 0) {// 未订阅,无需退订return;}if (typeof handle == "undefined") {// 全部退订handleMap.clear();} else {handleMap.delete(handle);}if (handleMap.size == 0) {// 向后台取消订阅const reqData = {action: "unsub",channel: topic,data: {},};this._client && this._client.sendMessage(JSON.stringify(reqData));}}/*** 清理资源并释放所有内部状态。*/dispose() {// 清理 socketthis._client && this._client.dispose();this._client = null;// 如果有心跳,取消心跳this._heartTimer && clearTimeout(this._heartTimer);this._heartTimer = null;// 如果心跳正在进行, 取消回调this._heartBeatResolveTimer && clearTimeout(this._heartBeatResolveTimer);this._heartBeatResolveTimer = null;this._heartBeatResolve = null;// 如果在重连, 去掉重连this._autoReloginTimer && clearTimeout(this._autoReloginTimer);this._autoReloginTimer = null;// 清理系统订阅this._instanceEventCallback = null;// 清理主题订阅this._subIndex = 0;this._subMap.clear();}
}
相关文章:

Websocket解析及用法(封装一个通用订阅发布主题的webSocket类)
1、什么是WebSocket? websocket的目标是通过一个长连接实现与服务器全双工,双向的通信。是一种在单个TCP连接上进行全双工通信的协议,使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 js中创建websocket…...
Foxit Reader(福昕阅读器)详细安装和使用教程
第一部分:Foxit Reader简介和基本信息 1.1 什么是Foxit Reader? Foxit Reader(福昕阅读器)是一款功能强大的PDF阅读和编辑软件,以其快速、轻巧和丰富的功能而闻名。它不仅支持常规的PDF阅读功能,还提供了…...

c++静态成员变量和静态成员函数
1)C入门级小知识,分享给将要学习或者正在学习C开发的同学。 2)内容属于原创,若转载,请说明出处。 3)提供相关问题有偿答疑和支持。 我们可以使用 static 关键字来把类成员定义为静态的。当我们声明类的成…...

视频共享融合赋能平台LntonCVS统一视频接入平台数字化升级医疗体系
医疗健康事关国计民生,然而,当前我国医疗水平的地区发展不平衡、医疗资源分布不均和医疗信息系统老化等问题,制约了整体服务能力和水平的提升。视频融合云平台作为推动数字医疗的关键工具,在医疗领域的广泛应用和普及,…...

Gin框架基础
1、一个简单的Gin示例 下载并安装Gin: go get -u github.com/gin-gonic/gin1.1 一个简单的例子 package mainimport ("net/http""github.com/gin-gonic/gin" )func main() {// 创建一个默认的路由引擎r : gin.Default()// 当客户端以GET方式访问 /hello…...

用GPT-4纠错GPT-4 OpenAI推出CriticGPT模型
根据OpenAI周四(6月27日)发布的新闻稿,该公司新推出了一个基于GPT-4的模型——CriticGPT,用于捕获ChatGPT代码输出中的错误。CriticGPT的作用相当于让人们用GPT-4来查找GPT-4的错误。该模型可以对ChatGPT响应结果做出批评评论&…...
SQL CASE WHEN语句的使用技巧
SQL CASE WHEN语句的使用技巧 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿! 在SQL查询中,经常需要根据不同的条件进行分支处理,这时就…...

虹科技术丨跨越距离障碍:PCAN系列网关在远程CAN网络通信的应用潜力
来源:虹科技术丨跨越距离障碍:PCAN系列网关在远程CAN网络通信的应用潜力 原文链接:虹科技术 | 跨越距离障碍:PCAN系列网关在远程CAN网络通信的应用潜力 欢迎关注虹科,为您提供最新资讯! #PCAN #网关 #CA…...

【UE 网络】RPC远程过程调用 入门篇
目录 0 引言1 RPC基本概念1.1 定义1.2 分类 2 RPC的使用2.1 Client RPC2.2 Server RPC2.3 Multicast RPC 🙋♂️ 作者:海码007📜 专栏:UE虚幻引擎专栏💥 标题:【UE 网络】RPC远程过程调用 入门篇❣️ 寄语…...

安装maven与nexus
安装maven与nexus Maven官网下载地址:http://maven.apache.org cd /data/software/wget https://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/3.8.1/binaries/apache-maven-3.8.8-bin.tar.gz# 解压 tar xf apache-maven-3.8.1-bin.tar.gz -C /opt/[rooth…...

如何用DCA1000持续采集雷达数据
摘要:本文介绍一下如何通过mmwave studio软件,搭配DCA1000数据采集卡,对AWR1843BOOST进行不间断的数据采集。本文要求读者已经掌握了有关基础知识。 本文开放获取,无需关注。 到SensorConfig页面下,一步步操作…...
怎么用JavaScript写爬虫
随着互联网技术的不断发展,爬虫(web crawler)已经成为当前最热门的爬取信息方式之一。通过爬虫技术,我们可以轻松地获取互联网上的数据,并用于数据分析、挖掘、建模等多个领域。而javascript语言则因其强大的前端开发工…...
Leetcode 3203. Find Minimum Diameter After Merging Two Trees
Leetcode 3203. Find Minimum Diameter After Merging Two Trees 1. 解题思路2. 代码实现 题目链接:3203. Find Minimum Diameter After Merging Two Trees 1. 解题思路 这一题的话算是一个拓扑树的题目?总之就是从树的叶子节点不断向上遍历ÿ…...
【抽代复习笔记】24-群(十八):循环群的两道例题
例1:证明: (1)三次交错群A3是循环群,它与(Z3,)同构,其中Z3 {[0],[1],[2]}; (2)G {1,i,-1,-i},G上的代数运算是数的乘法,则G是一个循环群&…...

Linux常见操作问题
1、登录刚创建的用户,无法操作。 注:etc/passwd文件是Linux操作系统中存储用户账户信息的文本文件,包含了系统中所有用户的基本信息,比如用户名、用户ID、用户组ID、用户家目录路径。 注:etc: 这个目录存放所有的系统…...

鲁工小装载机-前后桥传动轴油封更换记录
鲁工装载机 因前后桥大量漏齿轮油,故拆开查看、更换油封 一: 如图圈起来的地方是螺丝和钢板相别,用200的焊接电流用电焊机点开一个豁口后拆除螺丝。 转轴是拆除传动轴后的样子。 这就是拆下来的样子,这玩意插上边那图&…...

商城自动化测试实战 —— 登录+滑块验证
hello大家好,我是你们的小编! 本商城测试项目采取PO模型和数据分离式架构,采用pytestseleniumjenkins结合的方式进行脚本编写与运行,项目架构如下: 1、创建项目名称:code_shopping,创建所需项目…...

8.计算机视觉—增广和迁移
目录 1.数据增广数据增强数据增强的操作代码实现2.微调 迁移学习 Transfer learning(重要的技术)网络结构微调:当目标数据集比源数据集小得多时,微调有助于提高模型的泛化能力。训练固定一些层总结代码实现1.数据增广 CES上的真实故事 有一家做智能售货机的公司,发现他们…...

【Matlab】-- BP反向传播算法
文章目录 文章目录 00 写在前面01 BP算法介绍02 基于Matlab的BP算法03 代码解释 00 写在前面 BP算法可以结合鲸鱼算法、飞蛾扑火算法、粒子群算法、灰狼算法、蝙蝠算法等等各种优化算法一起,进行回归预测或者分类预测。 01 BP算法介绍 BP(Backpropag…...
【Python】 数据分析中的常见统计量:众数
那年夏天我和你躲在 这一大片宁静的海 直到后来我们都还在 对这个世界充满期待 今年冬天你已经不在 我的心空出了一块 很高兴遇见你 让我终究明白 回忆比真实精彩 🎵 王心凌《那年夏天宁静的海》 众数(Mode)是统计学中另…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...

React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制
在数字化浪潮席卷全球的今天,数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具,在大规模数据获取中发挥着关键作用。然而,传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时,常出现数据质…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...
SQL慢可能是触发了ring buffer
简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...

GitFlow 工作模式(详解)
今天再学项目的过程中遇到使用gitflow模式管理代码,因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存,无论是github还是gittee,都是一种基于git去保存代码的形式,这样保存代码…...