WebSocket技术全面解析:从历史到实践

WebSocket作为一种全双工通信协议,彻底改变了Web应用的实时交互模式。它于2011年被IETF正式标准化为RFC 6455,解决了传统HTTP协议在实时通信中的根本缺陷。本文将深入探讨WebSocket的发展历程、技术原理、JavaScript实现方法、实际应用场景、心跳机制设计以及存在的局限性,为开发者提供全面的WebSocket技术指南。

一、WebSocket的历史发展

WebSocket协议的诞生源于对Web应用实时性的迫切需求。在HTTP协议主导的Web 1.0时代,客户端与服务器之间的通信只能通过单向请求-响应模式实现,这导致了实时数据交换的严重瓶颈。开发者不得不采用轮询(Polling)或长轮询(Comet)等变通方案,但这些方案不仅效率低下,还浪费了宝贵的网络带宽和服务器资源。

WebSocket协议最初由Ian Hickson(Google工程师,HTML5规范负责人)和Michael Carter于2008年提出,旨在为Web应用提供真正的双向实时通信能力。这一创新得到了浏览器厂商和开源社区的广泛支持,最终在2011年12月由IETF正式发布为RFC 6455标准,同时W3C也将其纳入HTML5规范,成为浏览器原生支持的技术。

WebSocket的标准化过程经历了多次讨论和改进。早期版本曾面临浏览器兼容性、代理服务器支持等问题。例如,早期的Chrome浏览器在4.0版本才开始支持WebSocket,Firefox在7.0版本实现支持,而IE则从未原生支持WebSocket,需要借助Polyfill等技术手段。这些挑战促使WebSocket协议设计者采用与HTTP/1.1兼容的握手机制,确保能够穿透各种网络环境和代理服务器。

随着技术的成熟,WebSocket已成为现代Web应用不可或缺的通信基础。它不仅支持HTTP/1.1,还通过RFC 8441扩展了在HTTP/2中的实现,利用多路复用和头压缩等特性进一步提升性能。如今,主流浏览器均原生支持WebSocket,使其成为构建实时Web应用的首选协议。

二、WebSocket的技术特点与优势

WebSocket协议的核心价值在于其独特的技术特点和与HTTP协议的显著差异。作为基于TCP的全双工通信协议,WebSocket提供了更高效、更实时的通信方式,解决了HTTP协议在实时场景中的根本缺陷。

全双工通信能力是WebSocket最突出的特点。与HTTP的单向请求-响应模式不同,WebSocket允许客户端和服务器之间建立持久连接,并在连接建立后随时双向传输数据。这意味着服务器可以主动向客户端推送信息,而无需等待客户端的请求,大大降低了通信延迟。在实际应用中,这种能力使聊天应用、实时监控系统等能够实现近乎零延迟的交互体验。

较低的协议开销是WebSocket的另一重要优势。HTTP协议每次请求都需要携带完整的头部信息,即使实际传输的数据很小。相比之下,WebSocket在握手阶段使用HTTP协议后,后续数据传输仅使用极简的帧头(2-10字节),显著减少了网络带宽的浪费。据统计,对于频繁的小数据交换场景,WebSocket可将网络流量减少约75%,大幅提升了通信效率。

WebSocket还支持二进制数据传输,这对于处理图片、音频、视频等媒体数据非常有用。通过二进制帧,WebSocket能够更高效地传输非文本数据,减少了数据转换的开销。此外,WebSocket还具备跨源资源共享能力,允许不同源的客户端与服务器建立连接,这在现代Web应用中尤为重要。

在协议选择上,WebSocket基于TCP而非UDP主要有以下考量:

  1. 可靠性:TCP提供数据包的可靠传输,确保数据按顺序到达且无丢失,而UDP不具备这些特性,可能导致消息乱序或丢失。
  2. 全双工支持:TCP的全双工特性为WebSocket的双向通信提供了底层支持,而UDP虽然也是无连接的,但缺乏内置的流量控制和拥塞控制机制。
  3. 代理兼容性:WebSocket的握手阶段使用HTTP协议,使得它能够轻松通过各种HTTP代理服务器,而基于UDP的协议可能被代理服务器过滤。

与HTTP协议相比,WebSocket的主要区别如下表所示:

特性HTTP协议WebSocket协议
连接模式短连接,每次请求新建连接长连接,建立一次后持续使用
通信方向客户端主动请求,服务器被动响应双向通信,客户端和服务器均可主动发送
协议开销每次请求携带完整头部,开销大握手后仅使用极简帧头,开销小
实时性低,依赖客户端轮询高,支持服务器主动推送
适用场景适合请求-响应模式的应用适合需要实时双向通信的应用

这些技术特点使WebSocket成为构建现代实时Web应用的理想选择,从在线聊天到实时数据监控,再到协同编辑等场景,WebSocket都展现出显著的优势。

三、JavaScript中使用WebSocket的方法

在JavaScript中,使用WebSocket API非常简单直观。通过WebSocket构造函数创建实例后,开发者可以通过事件监听器处理连接状态变化和数据传输。以下是WebSocket API的核心组成部分及其使用方法:

WebSocket对象的创建与配置是使用WebSocket的第一步。通过指定URL(ws://或wss://)创建WebSocket实例,可以选择使用默认端口(80/443)或自定义端口:

// 创建WebSocket连接
const socket = new WebSocket('wss://example.com/realtime');// 设置二进制数据类型(可选)
socket.binaryType = 'arraybuffer'; // 或 'blob'

连接状态管理通过readyState属性和四个状态事件实现:

// 监听连接建立事件
socket.addEventListener('open', function (event) {console.log('WebSocket连接已建立');// 连接建立后可发送初始消息socket.send('客户端已就绪');
});// 监听消息接收事件
socket.addEventListener('message', function (event) {console.log('收到服务器消息:', event.data);// 处理接收到的数据const receivedData = JSON.parse(event.data);updateUI(receivedData);
});// 监听连接关闭事件
socket.addEventListener('close', function (event) {console.log('WebSocket连接已关闭,代码:', event.code);// 可在此处实现重连逻辑reconnect();
});// 监听错误事件
socket.addEventListener('error', function (error) {console.error('WebSocket发生错误:', error);// 处理连接错误socket.close();
});

数据发送与接收是WebSocket通信的核心:

// 发送文本数据
socket.send('Hello, Server!');// 发送二进制数据(如ArrayBuffer)
const arrayBuffer = new Uint8Array([80, 79, 78, 71]).buffer;
socket.send(arrayBuffer);// 接收二进制数据
socket.addEventListener('message', function (event) {if (typeof event.data === 'string') {// 处理文本数据} else {// 处理二进制数据(如ArrayBuffer或Blob)const binaryData = event.data;}
});

缓冲区监控通过bufferedAmount属性实现,该属性返回未发送至服务器的字节数,可用于优化数据发送策略:

// 检查缓冲区状态
if (socket.bufferedAmount > 1024) {console.log('数据缓冲过多,暂停发送');// 暂停发送新数据sending Paused = true;
}

高级功能如子协议协商和扩展支持,可通过握手阶段的HTTP头实现:

// 创建带有自定义头的WebSocket连接(需浏览器支持)
const socket = new WebSocket('ws://example.com/realtime', {headers: {'Sec-WebSocket-Protocol': 'chat, binary','X-Custom-Header': 'value'}
});

错误处理与状态码是确保应用稳定的关键:

socket.addEventListener('error', function (error) {console.error('WebSocket错误:', error.message);// 根据错误类型采取不同措施if (error.type === '锤子') {// 处理网络错误} else {// 处理其他错误}
});socket.addEventListener('close', function (event) {console.log('连接关闭,状态码:', event.code);// 根据状态码判断关闭原因if (event.code === 1000) {console.log('正常关闭');} else if (event.code === 1001) {console.log('协议错误导致关闭');} else {console.log('其他原因导致关闭');}
});

这些API提供了构建实时Web应用所需的基本功能。对于更复杂的场景,开发者可以使用Socket.IO等库,它们封装了WebSocket并提供了自动回退机制(当WebSocket不可用时自动切换到其他传输方式),以及更高级的心跳、重连等功能。

四、WebSocket的实际应用场景案例

WebSocket的全双工通信特性使其在多种场景中展现出独特优势。以下是几个典型的应用场景及其实现方式:

实时聊天应用是WebSocket最常见的应用场景。通过WebSocket,服务器可以主动将消息推送给所有在线用户,实现近乎即时的通信体验。以下是一个简化版的实时聊天实现:

// 客户端代码
const chatSocket = new WebSocket('wss://chat.example.com');chatSocket.addEventListener('open', () => {console.log('聊天连接已建立');
});chatSocket.addEventListener('message', (event) => {const message = JSON.parse(event.data);displayMessage(message.from, message.content);
});chatSocket.addEventListener('close', () => {console.log('聊天连接已关闭');
});// 发送消息
function sendMessage(to, content) {const payload = {type: 'message',to: to,content: content};chatSocket.send(JSON.stringify(payload));
}

实时数据监控系统利用WebSocket的高效数据传输能力,可以实现设备状态的实时更新。例如,一个车间设备监控系统通过WebSocket推送设备运行状态:

// 客户端监控代码
const monitorSocket = new WebSocket('wss://monitor.example.com');monitorSocket.addEventListener('message', (event) => {const data = JSON.parse(event.data);update status(data.deviceId, data.status);
});// 服务端(Node.js)代码片段
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });// 监听数据库变化并推送
function listenToDatabaseChanges() {// 假设使用Redis监听变化redis订阅('device_status', (message) => {const data = JSON.parse(message);wss广播(data);});
}

在线协同编辑是WebSocket的另一个典型应用场景。通过WebSocket,多个用户对同一文档的编辑可以实时同步:

// 客户端协同编辑代码
const editSocket = new WebSocket('wss://edit.example.com');// 记录本地操作并发送
function recordAndSendOperation(op) {// 将操作记录到本地历史clientHistory.push(op);// 发送操作到服务器editSocket.send(JSON.stringify(op));
}// 接收并应用远程操作
editSocket.addEventListener('message', (event) => {const remoteOp = JSON.parse(event.data);// 应用远程操作到本地文档applyOperation(document, remoteOp);// 确保操作顺序正确(可能需要冲突解决机制)
});

实时地图与位置共享应用中,WebSocket可以用于推送位置更新:

// 客户端地图位置更新
const mapSocket = new WebSocket('wss://map.example.com');// 发送位置更新
function sendPositionUpdate(x, y) {mapSocket.send(JSON.stringify({ type: 'position', x, y }));
}// 接收并渲染其他用户位置
mapSocket.addEventListener('message', (event) => {const update = JSON.parse(event.data);if (update.type === 'position') {renderUserPosition(update.user, update.x, update.y);}
});

在线游戏利用WebSocket的低延迟特性实现实时玩家交互:

// 游戏客户端代码
const gameSocket = new WebSocket('wss://game.example.com');// 发送玩家动作
function sendPlayerAction(action) {gameSocket.send(JSON.stringify({ action }));
}// 接收并更新游戏状态
gameSocket.addEventListener('message', (event) => {const gameUpdate = JSON.parse(event.data);updateGameWorld(gameUpdate);
});

这些案例展示了WebSocket在不同领域的应用潜力。值得注意的是,WebSocket的握手阶段使用HTTP协议,这使得它能够轻松通过各种网络代理和防火墙,解决了早期实时通信技术(如Comet)面临的兼容性问题。

五、WebSocket的心跳机制设计

心跳机制是维持WebSocket连接活跃状态的关键技术。由于网络环境复杂,NAT设备、代理服务器等可能会在长时间无活动时关闭连接。心跳机制通过定期发送小数据包,确保连接不被意外断开,同时也能检测连接是否仍然有效。

WebSocket协议本身提供了专门的控制帧用于心跳机制:Ping帧(0x9)和Pong帧(0xA)。客户端发送Ping帧后,服务端必须回复Pong帧,这为心跳机制提供了协议层面的支持。

在JavaScript中,实现心跳机制主要有两种方式:

方式一:客户端发送Ping,服务端回复Pong

// 客户端心跳实现
let heartbeatTimer = null;function startHeartbeat() {heartbeatTimer = setInterval(() => {if (socket.readyState === WebSocket.OPEN) {// 发送自定义心跳包(文本或二进制)socket.send(JSON.stringify({ type: 'ping' }));}}, 30000); // 每30秒发送一次
}// 监听Pong响应
function handlePong() {console.log('收到心跳响应,连接正常');// 重置超时计时器clearPongTimeout();setupPongTimeout();
}// 心跳超时处理
let pingTimeoutId = null;function setupPongTimeout() {pingTimeoutId = setTimeout(() => {console.error('心跳超时,连接可能已断开');// 关闭连接并尝试重连socket.close();reconnect();}, 45000); // 如果45秒内未收到Pong,则认为连接已断开
}// 监听消息
socket.addEventListener('message', (event) => {const data = JSON.parse(event.data);if (data.type === 'pong') {handlePong();}
});// 监听连接建立
socket.addEventListener('open', () => {startHeartbeat();setupPongTimeout();
});// 监听连接关闭
socket.addEventListener('close', () => {clearInterval( heartbeatTimer );clearTimeout(pingTimeoutId);
});

方式二:服务端发送心跳,客户端响应

// 服务端(Node.js)心跳实现
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });// 服务端心跳发送
setInterval(() => {wss clients. foreach( client => {if (client. readyState === WebSocket.OPEN) {client. send( JSON.stringify({ type: 'server_heart' }));}});
}, 30000); // 每30秒发送一次心跳// 客户端响应
socket.addEventListener('message', (event) => {const data = JSON.parse(event.data);if (data.type === 'server_heart') {// 发送响应socket.send(JSON.stringify({ type: 'client_heart' }));}
});

心跳机制的优化策略包括:

  1. 动态调整心跳间隔:根据网络状况和应用需求自适应调整心跳间隔,避免固定间隔导致的资源浪费或检测延迟。
  2. 合并心跳与业务数据:在有业务数据传输时,可以省略心跳包,只在空闲时发送,进一步降低开销。
  3. 服务端主动检测:服务端可以记录每个连接的最后活跃时间,如果长时间未收到任何消息(包括心跳),则主动关闭连接。

心跳机制的缺陷与限制主要包括:

  1. 增加网络开销:频繁的心跳包会占用额外的带宽,对于大规模应用(如微信)可能导致信令风暴,增加服务器负担。
  2. 无法检测所有连接问题:心跳机制只能检测连接是否活跃,无法解决网络分区(Split-Brain)等问题。
  3. 代理服务器兼容性:某些代理服务器可能丢弃控制帧,导致心跳机制失效,需要使用数据帧模拟心跳。
  4. 弱网环境问题:在高延迟或不稳定网络中,心跳机制可能误判连接状态,需要结合其他机制提高可靠性。

在实际应用中,心跳间隔通常设置为30-60秒,略小于NAT设备的典型超时时间(通常为2-5分钟)。对于要求极高实时性的应用,可以适当缩短间隔,但需权衡资源消耗。

六、WebSocket的缺陷与限制

尽管WebSocket在实时通信方面表现出色,但它也存在一些固有的缺陷和限制,开发者在选择使用时需要充分了解。

资源消耗问题是WebSocket的主要限制之一。每个WebSocket连接都会占用服务器资源(如内存、线程等),在大规模应用中可能导致资源紧张。例如,一个拥有百万用户的聊天应用需要维护大量持久连接,这对服务器性能提出了较高要求。解决方案包括使用连接池、限制最大连接数或采用消息队列广播机制。

网络环境兼容性是另一个需要考虑的问题。虽然现代浏览器普遍支持WebSocket,但在某些特殊网络环境中(如企业内网、移动运营商网络)可能遇到问题。例如,一些代理服务器可能无法正确处理WebSocket的升级机制,导致连接失败。解决方法包括使用wss://(加密WebSocket)提高穿透性,或采用回退机制(如长轮询)。

安全风险也是WebSocket需要面对的挑战。由于WebSocket不遵循同源策略,任何域的客户端都可以连接到服务器,这增加了跨站请求伪造(CSRF)等攻击的风险。解决方案包括实施严格的身份验证机制(如JWT令牌)、权限校验和频率限制等。

协议局限性方面,WebSocket虽然支持二进制数据传输,但在处理大规模数据时仍有一定限制。例如,单个WebSocket帧的最大有效载荷为65536字节,对于更大文件需要分片传输。此外,WebSocket缺乏内置的消息优先级和QoS(服务质量)机制,需要开发者自行实现。

分布式架构挑战在多节点部署时尤为明显。由于WebSocket连接是点对点的,无法跨节点共享,这使得在分布式环境下实现会话保持变得复杂。解决方案包括使用会话粘性(Session Affinity)、共享消息通道(如Redis发布订阅)或集中式WebSocket服务等。

与HTTP/2的整合虽然RFC 8441已定义,但仍处于早期阶段,尚未被广泛支持。HTTP/2的多路复用和头压缩特性理论上可以进一步提升WebSocket性能,但实际应用中仍需依赖HTTP/1.1的握手机制。

性能优化建议包括:

  1. 消息压缩:启用permessage-deflate扩展减少传输数据量。
  2. 异步处理:使用线程池或队列处理消息,避免阻塞主线程。
  3. 连接管理:实施连接数限制,防止资源耗尽。
  4. 安全加固:强制使用WSS(加密WebSocket),实施严格的身份验证和权限控制。

这些缺陷和限制提醒开发者在使用WebSocket时需要根据具体场景进行权衡和优化,确保应用的稳定性和性能。

七、WebSocket的未来发展趋势

随着Web技术的不断发展,WebSocket也在持续演进。未来,我们可以期待以下几方面的发展:

与HTTP/3的整合将为WebSocket带来新的性能提升。HTTP/3基于QUIC协议,能够在不依赖TCP的情况下实现低延迟通信,这可能为WebSocket提供更高效的基础传输层。目前,这一方向仍处于研究阶段,但有望在未来几年内取得突破。

边缘计算与WebSocket的结合将扩展其应用场景。随着边缘计算的发展,WebSocket可以用于连接边缘设备和云服务,实现更高效的数据传输和处理。例如,在物联网场景中,边缘设备可以通过WebSocket直接与浏览器前端通信,减少中间层开销。

协议扩展与自定义帧将为WebSocket提供更多可能性。RFC 6455允许通过扩展定义自定义帧类型,这为特定领域的应用提供了更多灵活性。未来可能出现更多针对特定场景的WebSocket扩展,如低延迟视频传输、高吞吐量数据推送等。

安全性增强将是WebSocket持续发展的重点。随着Web应用安全需求的提高,WebSocket的安全机制也将不断完善。例如,更严格的认证机制、端到端加密支持等,将使WebSocket在安全敏感场景中更具竞争力。

与WebAssembly的融合将提升WebSocket应用的性能。WebAssembly允许在浏览器中运行高性能代码,结合WebSocket的实时通信能力,可以构建更复杂的客户端应用,如实时数据分析、游戏渲染等。

标准化与普及将继续推动WebSocket的发展。随着更多浏览器和服务器实现对WebSocket的支持,以及相关标准的完善,WebSocket将变得更加普及和稳定。例如,服务端实现库的成熟、客户端API的标准化等,都将降低开发难度。

总之,WebSocket作为一种革命性的Web通信协议,不仅解决了传统HTTP在实时场景中的根本缺陷,还为现代Web应用提供了更多可能性。随着技术的不断进步和应用场景的扩展,WebSocket将继续在Web通信领域发挥重要作用,推动Web应用向更实时、更交互的方向发展。

在实际开发中,开发者应根据具体需求选择合适的通信方式,WebSocket适合需要双向实时通信的场景,而HTTP更适合请求-响应模式的应用。通过深入理解WebSocket的工作原理和局限性,开发者可以更好地利用这一技术构建高性能、低延迟的现代Web应用。

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

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

相关文章

单用户模式、紧急模式、救援模式有什么区别

文章目录 **一、单用户模式(Single User Mode)****功能与用途****启动特点****进入方式** **二、紧急模式(Emergency Mode)****功能与用途****启动特点****进入方式** **三、救援模式(Rescue Mode)****功能…

【大模型入门】访问GPT的API

目录 0 前言 免费访问GPT的API Windows下环境变量的设置 1 非流式输出 1.1 使用requests库 1.2 使用OpenAI库 2 流式输出 2.1 使用requests库 2.2 使用OpenAI库 3 使用OpenAI库与GPT聊天(存储对话历史版) 4 嵌入向量embeddings 4.1 创建嵌入向…

Jenkins 部署脚本

java版 #!/bin/bashAPP_NAME"springboot-demo-0.0.1-SNAPSHOT" JAR_PATH"/home/package/target/${APP_NAME}.jar" LOG_PATH"/home/package/logs/app.log"# 查找并停止旧进程 PID$(ps aux | grep "$APP_NAME.jar" | grep -v grep | awk…

NV183NV185美光固态闪存NV196NV201

美光固态闪存技术深度解析:NV183、NV185、NV196与NV201系列 一、技术架构与核心参数对比 1. 制程工艺与容量布局 美光NV183/NV185/NV196/NV201系列采用176层3D NAND技术,通过垂直堆叠提升存储密度。其中: NV183:主打256GB容量段…

基于单片机PWM控制逆变电源设计资料集:全面掌握逆变电源设计精髓

基于单片机PWM控制逆变电源设计资料集:全面掌握逆变电源设计精髓 去发现同类优质开源项目:https://gitcode.com/ 项目介绍 在现代电子技术中,逆变电源的设计与应用占据了至关重要的位置。今天,我将为您介绍一个优秀的开源项目——基于单片…

Docker Buildx 构建多架构镜像(Redis、Mysql、Java8、Nginx)

目标 ARM64 麒麟电脑无法直接拉取 Redis 镜像,需在 Windows x86 电脑上通过 多架构构建工具(如 Docker Buildx) 生成适配 ARM64 的 Redis 镜像,再打包传输到目标设备。 一、核心问题:跨架构镜像兼容性 直接保存的 redi…

代理IP的安全陷阱:如何避免中间人攻击与IP池污染?

在跨境业务、数据采集等场景中,代理IP已成为刚需工具。然而,其隐藏的安全风险却常被忽视——轻则泄露隐私,重则导致账号封禁、数据劫持甚至金融损失。本文将深入剖析两大核心风险(中间人攻击与IP池污染),并…

深入理解大语言模型中的超参数:Temperature、Top-p 与更多

随着大语言模型(LLM)如 GPT、Claude、Gemini 的广泛应用,调优生成文本的质量与风格成为开发者和研究人员的重要课题。其中,超参数(Hyperparameters)如 temperature 和 top_p 扮演了核心角色,影响…

译码器Multisim电路仿真汇总——硬件工程师笔记

目录 74LS实现二线三线译码器 1 74LS139D和74LS138D基础知识 1.1 74LS139D 二线四线译码器 1.1.1 功能特点 1.1.2 引脚功能 1.1.3 工作原理 1.1.4 应用场景 1.1.5 使用方法 1.1.6 注意事项 1.6.7 逻辑真值表 1.2 74LS138D 3线8线译码器 1.2.1 功能特点 1.2.2 引脚…

国产 OFD 标准公文软件数科 OFD 阅读器:OFD/PDF 双格式支持,公务办公必备

各位办公软件小达人们,今天咱来聊聊数科OFD阅读器! 软件下载地址安装包 这软件啊,是基于咱国家自主OFD标准的版式阅读软件,主要用来阅读和处理OFD/PDF电子文件,还能用于公务应用扩展。它支持打开和浏览OFD/PDF格式的文…

vue中的toRef

在 Vue 中, toRef 函数用于将响应式对象的属性转换为一个独立的 ref 对象,同时保持与原始属性的关联。其参数格式及用法如下: toRef 的参数说明 1. 参数 1:源对象(必须) - 类型: Object &…

暖通锅炉的智能管控:物联网实现节能又舒适​

暖通锅炉系统在建筑供暖、工业供热等领域扮演着重要角色,其运行效率和能源消耗直接关系到用户的使用体验和成本支出。传统的暖通锅炉管理方式往往依赖人工操作和经验判断,存在能耗高、调节不灵活、舒适性差等问题。随着物联网技术的发展,暖通…

PHP:从入门到进阶的全面指南

PHP(Hypertext Preprocessor)作为一种广泛使用的开源脚本语言,尤其适用于 Web 开发并可嵌入 HTML 中。自诞生以来,PHP 凭借其简单易学、功能强大以及丰富的生态系统,成为了众多网站和 Web 应用程序开发的首选语言之一。…

EXCEL 基础函数

1、绝对引用、相对引用 1.1相对引用 相对引用,这是最常见的引用方式。复制单元格公式时,公式随着引用单元格的位置变化而变化 例如在单元格内输入sum(B2:C2),然后下拉填充柄复制公式。 可以看到每个单元格的公式不会保持sum(B2:C2)&#…

升级到MySQL 8.4,MySQL启动报错:io_setup() failed with EAGAIN

在升级到MySQL 8.4后,许多用户在启动数据库时遇到了一种常见错误:“io_setup() failed with EAGAIN”。本文将深入探讨该错误的原因,并提供详细的解决方案。错误分析错误描述当你启动MySQL时,可能会在日志文件中看到以下错误信息&…

【ECharts Y 轴标签优化实战:从密集到稀疏的美观之路】

ECharts Y 轴标签优化实战:从密集到稀疏的美观之路 📋 文章背景 在开发数据可视化模块时,我们遇到了一个常见但棘手的图表显示问题:ECharts 图表的 Y 轴标签过于密集,影响了用户体验和数据的可读性。本文将详细记录整个…

【锂电池剩余寿命预测】GRU门控循环单元锂电池剩余寿命预测(Pytorch完整源码和数据)

目录 效果一览程序获取程序内容代码分享效果一览 程序获取 获取方式一:文章顶部资源处直接下载:【锂电池剩余寿命预测】GRU门控循环单元锂电池剩余寿命预测(Pytorch完整源码和数据) 获取方式二:订阅电池建模专栏获取电池系列更多文件。 程序内容 1.【锂电池剩余寿命预测…

Minstrel:多智能体协作生成结构化 LangGPT 提示词

一、项目概述 Minstrel 是一个基于 LangGPT 框架的多智能体系统,自动生成结构化、人格化的提示词。它通过多个协作代理,提升提示词的准确性、多样性和灵活性,适合非 AI 专家使用 (github.com)。 二、问题动机 当前 LLM 提示设计经验依赖强…

Golang发送定时邮件

前面尝试过用Python来发送邮件,下面测试一下Golang如何发送邮件 需要使用三方库如下 1.安装github.com/jordan-wright/email go get github.com/jordan-wright/email安装完成之后代码如下 package mainimport ("github.com/jordan-wright/email""log""…

CodeSys的软PLC忘记了用户名和密码怎么办

Codesys的win v3 x64软PLC忘记用户名和密码怎么办 概述检查文件成功 概述 我曾经多次在运行了软PLC后忘记了自己的用户名和密码。有些是回忆起来了,但有些真的想不起来了。没有办法后来是重新装的CodeSys。这次从网上看到大佬写的文章,试了一下&#xf…