JavaScript 第23章:WebSocket 与实时通讯
在JavaScript中使用WebSocket进行实时通信是一个非常实用且强大的功能。下面我们将详细介绍WebSocket协议的基础知识、如何使用WebSocket对象以及如何构建一个简单的实时通信应用。
WebSocket 协议
WebSocket是一个在单个TCP连接上进行全双工通信的协议。WebSocket使得数据可以在浏览器和服务器之间双向流动,这对于需要频繁更新或即时通讯的应用来说是一个巨大的进步。传统的HTTP请求是短连接,即客户端请求服务器后,服务器响应并关闭连接;而WebSocket则保持连接开放,允许服务器主动向客户端推送数据。
WebSocket 对象
在JavaScript中,通过WebSocket
构造函数可以创建WebSocket对象。这个对象提供了几个关键的方法和事件来处理连接、发送消息以及接收消息。
创建WebSocket对象
var socket = new WebSocket('ws://example.com/socket');
这里的URL前缀是ws:
(对于加密连接则是wss:
)。
WebSocket 对象的事件
open
: 当连接建立时触发。message
: 当从服务器接收到消息时触发。error
: 当发生错误时触发。close
: 当连接关闭时触发。
WebSocket 对象的方法
send(data)
: 向服务器发送数据。close()
: 关闭连接。
实时通讯应用示例
让我们来看一个简单的实时聊天应用的例子。这里将包括客户端和服务端的部分。
客户端(HTML + JavaScript)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebSocket Chat</title>
<script>var socket = new WebSocket('ws://localhost:8080');socket.onopen = function(e) {console.log("Connection opened");};socket.onmessage = function(event) {var msg = document.createElement('p');msg.innerText = event.data;document.getElementById('messages').appendChild(msg);};socket.onerror = function(error) {console.log('Error:', error);};socket.onclose = function(e) {console.log('Socket closed');};function sendMessage() {var input = document.getElementById('input');socket.send(input.value);input.value = '';}
</script>
</head>
<body>
<div id="messages"></div>
<input type="text" id="input">
<button onclick="sendMessage()">Send</button>
</body>
</html>
服务端(Node.js)
为了简化起见,这里使用Node.js作为服务端实现。实际应用中可能需要更复杂的逻辑来处理多个连接等。
const WebSocket = require('ws');const wss = new WebSocket.Server({ port: 8080 });wss.on('connection', function connection(ws) {ws.on('message', function incoming(message) {console.log('received: %s', message);// 这里可以选择广播给所有连接的客户端wss.clients.forEach(function each(client) {if (client !== ws && client.readyState === WebSocket.OPEN) {client.send(message);}});});ws.on('close', function close() {console.log('Client disconnected');});
});console.log('Server is running on ws://localhost:8080');
这个例子中,客户端会显示来自其他客户端的消息,并允许用户输入并发送信息。服务端则负责接收消息并将消息广播给所有已连接的客户端。
以上就是关于WebSocket的一个基础介绍和简单的应用实例。在实际开发中,可能还需要考虑更多的细节如安全性、错误处理等。
当然,让我们继续深入探讨WebSocket在实际应用中的注意事项和技术细节。
WebSocket 在实际应用中的注意事项
安全性
- TLS/SSL 加密:使用
wss:
而不是ws:
来确保通信的安全性。 - 身份验证:确保只有经过认证的用户才能连接到你的WebSocket服务器。
- 授权:根据用户的权限控制他们可以访问的数据类型。
- 防止XSS攻击:对从WebSocket接收的数据进行适当的编码处理,避免跨站脚本攻击。
错误处理
- 断线重连:当连接意外中断时,客户端应该能够自动尝试重新连接。
- 心跳机制:为了避免因为长时间无活动而导致的连接超时,可以定期发送心跳包来维持连接活跃状态。
性能优化
- 压缩数据:使用压缩算法减少传输的数据量。
- 合理设计协议:定义清晰简洁的消息格式可以提高解析速度。
跨域支持
- CORS:WebSocket同样支持CORS(Cross-Origin Resource Sharing),这意味着服务器可以通过设置适当的头部来允许来自不同源的连接。
更多高级用法
广播消息
在之前的服务端示例中,我们展示了如何向所有连接的客户端广播消息。但在实际应用中,可能需要更加精细的控制,比如仅向特定房间内的用户发送消息。
存储会话状态
WebSocket本身并不保存会话状态,因此需要借助其他技术(如数据库、Redis等)来存储客户端的状态信息。
消息队列
在高并发场景下,直接将消息发送到客户端可能会导致性能瓶颈。使用消息队列(如RabbitMQ、Kafka等)可以帮助管理消息的发送,减轻服务器的压力。
示例:增加心跳检测和自动重连
接下来,我们在之前的客户端示例基础上增加心跳检测和自动重连的功能。
var socket = new WebSocket('ws://localhost:8080');
var heartbeat = null;socket.onopen = function(e) {console.log("Connection opened");heartbeat = setInterval(sendHeartbeat, 5000); // 每5秒发送一次心跳
};socket.onmessage = function(event) {var msg = document.createElement('p');msg.innerText = event.data;document.getElementById('messages').appendChild(msg);// 如果接收到的是心跳消息,则回复心跳if (event.data === 'ping') {socket.send('pong');}
};socket.onerror = function(error) {console.log('Error:', error);reconnect();
};socket.onclose = function(e) {console.log('Socket closed');clearInterval(heartbeat);reconnect();
};function sendHeartbeat() {socket.send('ping');
}function reconnect() {setTimeout(() => {socket = new WebSocket('ws://localhost:8080');}, 5000); // 失败后5秒重试连接
}function sendMessage() {var input = document.getElementById('input');socket.send(input.value);input.value = '';
}
此代码段增加了心跳检测功能,并在连接失败时自动尝试重新连接。服务端也需要相应地添加对心跳的支持。
以上就是有关WebSocket的一些扩展知识点及其在实际项目中的应用技巧。希望这些信息能帮助你在开发中更好地利用WebSocket。
并发连接处理
在WebSocket应用中,随着用户的增加,服务器需要同时处理大量的并发连接。这要求服务器具有良好的可扩展性和资源管理能力。
使用集群和负载均衡
- Node.js 集群模块:可以使用Node.js内置的
cluster
模块来创建工作进程,从而利用多核CPU的性能。 - 负载均衡器:使用外部负载均衡器(如Nginx)来分散请求到不同的WebSocket服务器节点上。
分布式会话管理
- 共享会话状态:使用Redis或其他分布式缓存解决方案来存储会话状态,确保即使在节点故障的情况下也能恢复会话。
优化WebSocket服务器性能
随着连接数的增长,服务器资源(如内存和CPU)的消耗也会增加。以下是一些性能优化策略:
内存管理
- 对象池:预先创建和复用WebSocket对象,以减少垃圾回收的压力。
- 缓存管理:合理使用缓存,减少不必要的数据交换。
CPU使用
- 非阻塞IO:使用异步非阻塞IO操作,避免阻塞主线程。
- 事件驱动:采用事件驱动的设计模式,如Node.js的EventEmitter,以减少不必要的计算。
构建复杂应用逻辑
随着应用程序变得越来越复杂,仅仅使用WebSocket可能不足以满足所有的需求。以下是几种扩展WebSocket应用的方式:
用户认证与权限控制
- JWT(JSON Web Tokens):用于认证用户,提供一种安全的方式来传递信息。
- OAuth2:适用于需要第三方认证的应用场景。
消息队列与持久化
- 消息队列:使用如RabbitMQ或Kafka来处理消息的持久化和顺序保证。
- 数据库集成:将消息记录到数据库中,以备后续分析或恢复使用。
实时通知系统
- Webhooks:可以用来通知外部系统有新的事件发生。
- 推送通知:通过WebSocket可以实现实时的推送通知,如邮件提醒、聊天消息等。
实时协作工具
- 同步编辑:像Google Docs这样的实时文档编辑工具,需要在多个客户端之间同步变化。
- 在线游戏:实时的游戏数据交换,要求低延迟和高可靠性。
实现示例:用户认证与消息转发
假设我们要构建一个带有用户认证的聊天室应用,用户必须登录后才能发送和接收消息。我们可以使用JWT来进行认证,并在服务端验证每个请求。
服务端实现(Node.js)
const WebSocket = require('ws');
const jwt = require('jsonwebtoken');
const secret = 'your-secret';// 假设有一个数据库模型来获取用户信息
async function getUser(username) {// 返回用户信息或错误
}const wss = new WebSocket.Server({ port: 8080 });wss.on('connection', async function connection(ws, req) {const token = req.headers.authorization;try {const decoded = jwt.verify(token, secret);const user = await getUser(decoded.username);if (!user) throw new Error('Invalid user');// 用户已认证,可以加入聊天室ws.user = user;ws.send(JSON.stringify({ type: 'welcome', message: 'Welcome to the chat!' }));ws.on('message', function incoming(message) {let data = {};try {data = JSON.parse(message);} catch (e) {console.error('Error parsing message:', e);return;}if (data.type === 'chat') {data.user = ws.user.username;wss.clients.forEach(function each(client) {if (client !== ws && client.readyState === WebSocket.OPEN) {client.send(JSON.stringify(data));}});}});} catch (e) {console.error('Authentication failed:', e);ws.close();}ws.on('close', function close() {console.log('User disconnected');});
});console.log('Server is running on ws://localhost:8080');
在这个示例中,我们首先验证了WebSocket连接请求中的JWT令牌。如果验证成功,允许用户加入聊天室,并监听他们的消息。如果接收到的是聊天消息,则将消息转发给所有已连接的客户端。
客户端实现
客户端需要先获取JWT令牌,然后使用该令牌建立WebSocket连接。
let token = 'your-token-here'; // 假设已经获取到了token
var socket = new WebSocket('ws://localhost:8080');socket.onopen = function(e) {console.log("Connection opened");socket.send(JSON.stringify({ type: 'auth', token }));
};socket.onmessage = function(event) {let data = JSON.parse(event.data);switch (data.type) {case 'welcome':console.log(data.message);break;case 'chat':var msg = document.createElement('p');msg.innerText = `${data.user}: ${data.message}`;document.getElementById('messages').appendChild(msg);break;}
};socket.onerror = function(error) {console.log('Error:', error);
};socket.onclose = function(e) {console.log('Socket closed');
};function sendMessage() {var input = document.getElementById('input');socket.send(JSON.stringify({ type: 'chat', message: input.value }));input.value = '';
}
这个客户端示例在连接打开后发送一个认证消息,并监听来自服务器的消息。当接收到聊天消息时,将其展示在页面上。
通过上述方法,您可以构建出更加健壮和安全的WebSocket应用。
相关文章:

JavaScript 第23章:WebSocket 与实时通讯
在JavaScript中使用WebSocket进行实时通信是一个非常实用且强大的功能。下面我们将详细介绍WebSocket协议的基础知识、如何使用WebSocket对象以及如何构建一个简单的实时通信应用。 WebSocket 协议 WebSocket是一个在单个TCP连接上进行全双工通信的协议。WebSocket使得数据可…...

简单汇编教程10 数组
目录 实践:相加连续的数 数组是在内存中连续的一串变量。我这样说,可能你已经想到的大致的定义了: NUMBERS DW 34, 45, 56, 67, 75, 89 现在我们就定义了一个Number数组,里面存放的连续的六个数字:34, 45, 56, …...

Jsoup在Java中:解析京东网站数据
对于电商网站如京东来说,其页面上的数据包含了丰富的商业洞察。对于开发者而言,能够从这些网站中提取有价值的信息,进行分析和应用,无疑是一项重要的技能。本文将介绍如何使用Java中的Jsoup库来解析京东网站的数据。 Jsoup简介 …...

SQL 干货 | SQL 反连接
最强大的 SQL 功能之一是 JOIN 操作,它提供了一种优雅而简单的方法,将一个表中的每一条记录与另一个表中的每一条记录结合起来。不过,有时我们可能想从一个表中找到另一个表中没有的值。正如我们将在今天的博客文章中看到的,通过包…...

JSON 反对序列化 public final class LocalDateTime 日期格式错误
错误日志为: java.lang.RuntimeException: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of java.time.LocalDateTime (no Creators, like default construct, exist): no String-argument constructor/factory meth…...

Java 集合
1. 集合框架概述 集合框架(Collection Framework) 是 Java 中为处理一组对象而设计的一套标准化 API,它包括一组通用的接口、实现类和算法。这些接口和类为各种数据结构和操作方法提供了统一的实现方式,使得开发者可以轻松地对数…...

爬虫日常实战
爬取美团新闻信息,此处采用两种方法实现: 注意点:因为此处的数据都是动态数据,所以一定要考虑好向下滑动数据包会更新的情况,不然就只能读取当前页即第一页数据,方法一通过更新ajax数据包网址页数…...

复写零--双指针
一:题目描述 题目链接:. - 力扣(LeetCode) 二:算法原理分析 三:代码编写 void duplicateZeros3(vector<int>& arr) {int dest -1, cur 0, n arr.size();//1.找到要复写的最后一个数字while …...

跟着小土堆学习pytorch(二)——TensorBoard和Transform
文章目录 一、TensorBoard1.1 add_scalar()1.1,1 报错:TypeError: MessageToJson() got an unexpected keyword argument including_default_value_fields1.1.2 图像重叠1.1.3 代码展示 1.2 add_image()1.2.1 代码 二、transform2.1 介绍——对图片进行一些变化2.2 …...

自由学习记录(10)
Sprite Packer ~Mode & 图集 packer Project Setting经常是金屋藏娇 创建的项目如果不是2d项目,则默认disable打包 编辑模式就是你没点运行看游戏效果,在狼狈敲码创对象写逻辑的那个状态, 运行模式从点了|>之后,就一直…...

Redis提供了专门的命令来实现自增操作
Redis中的自增操作并不是直接通过CAS(Compare and Set)操作实现的。Redis提供了专门的命令来实现自增操作,这些命令能够确保操作的原子性,而不需要显式地使用CAS机制。 Redis中的自增操作 Redis中的自增操作主要依赖于以下几个命…...

uniapp修改input中placeholder样式
Uniapp官方提供了两种修改的属性方法,但经过测试,只有 placeholder-class 属性能够生效 <input placeholder"请输入手机验证码" placeholder-class"input-placeholder"/><!-- css --> <style lang"scss" s…...

GenerativeU:生成式开放目标检测
论文:https://arxiv.org/abs/2403.10191 代码:https://github.com/FoundationVision/GenerateU 感想 目标检测任务已经逐渐从闭集场景专项开集场景,在LLM加持下,速读越来越快。该方法仍然依赖于预先定义的类别,这意味着…...

element plus e-table表格中使用多选,当翻页时已选中的数据丢失
摘要: 点击第一页选中两个,再选择第二页,选中,回到第一页,之前选中的要保留! element ui table 解决办法: :row-key“getRowKeys” (写在el-table中) methods中声明 ge…...

CentOS 7 网络连接显示“以太网(ens33)不可用”
1.创建linux虚拟机,配置网络和主机名显示" 以太网(ens33,被拔出)" 2.桌面右键此电脑,管理,找到“服务和应用程序”,点击“服务”,找到下图两个服务,点击圈起来…...

qt QNetworkProxy详解
一、概述 QNetworkProxy通过设置代理类型、主机、端口和认证信息,可以使应用程序的所有网络请求通过代理服务器进行。它支持为Qt网络类(如QAbstractSocket、QTcpSocket、QUdpSocket、QTcpServer、QNetworkAccessManager等)配置网络层代理支持…...

推荐IDE中实用AI编程插件,目前无限次使用
插件介绍 一款字节跳动推出的“基于豆包大模型的智能开发工具” 以vscode介绍【pycharm等都可以啊】,这个插件提供智能补全、智能预测、智能问答等能力,节省开发时间 直接在IDE中使用,就不用在网页中来回切换了 感觉还可以,响应速…...

【华为HCIP实战课程十五】OSPF的环路避免及虚链路,网络工程师
一、避免域间路由环路 1、区域内部的防环:区域内同步了LSA,SPF就决定了区域内部没有环路 2、区间的防环机制:非正常的ABR不更新3类LSA 为防止区域间的环路OSPF定义了骨干区域和非骨干区域和三类LSA的传递规则 1)、OSPF划分了骨干区域和非骨干区域,所有非骨干区域均直接…...

【编程语言】正则表达式:POSIX 与 PCRE 的全面比较及应用
目录 正则表达式:POSIX 与 PCRE 的全面比较及应用1. 正则表达式的基本概念1.1 基本元素1.2 正则表达式的历史 2. POSIX 正则表达式2.1 POSIX 正则表达式的语法2.1.1 基本正则表达式 (BRE)2.1.2 扩展正则表达式 (ERE) 2.2 POSIX 正则表达式的使用场景2.3 使用 POSIX …...

Spark Streaming 数据流处理
一、创建Spark Streaming 环境 二、读取数据(监听端口) 三、任务处理 四、启动程序 我这里写的是简单的单词数量统计 import org.apache.spark.streaming.dstream.{DStream, ReceiverInputDStream} import org.apache.spark.{SparkConf, SparkConte…...

高效规划神器 markmap:一键将 Markdown 变思维导图!
❤️ 如果你也关注大模型与 AI 的发展现状,且对大模型应用开发非常感兴趣,我会快速跟你分享最新的感兴趣的 AI 应用和热点信息,也会不定期分享自己的想法和开源实例,欢迎关注我哦! 微信公众号|搜一搜&…...

微服务基础架构(图)
微服务基础架构是一种现代化的软件架构模式,旨在将大型复杂的应用程序拆分为多个小型、独立的服务。每个微服务专注于特定的业务功能,可独立开发、部署和扩展。 在微服务基础架构中,通常会使用轻量级的通信机制,如 RESTful API 或…...

中电金信:大模型时代 金融机构企业架构转型如何更智能化?
随着人工智能技术的不断进步,AI大模型在金融行业已经广泛应用,推动金融机构实现更高效、智能化的服务,同时也为金融科技领域的发展带来新的挑战。中电金信基于业务建模的企业架构转型解决方案也顺势而动,关注大模型在具体场景上的…...

基于CRNN模型的多位数字序列识别的应用【代码+数据集+python环境+GUI系统】
基于CRNN模型的多位数字序列识别的应用【代码数据集python环境GUI系统】 基于CRNN模型的多位数字序列识别的应用【代码数据集python环境GUI系统】 背景意义 多位手写数字识别,即计算机从纸张文档、照片、触摸屏等来源接收并解释可理解的手写数字输入的能力。 随着…...

windows中命令行批处理脚本学习
目录 一 基础知识二 常见命令1. 输出 echo2. 注释 rem .... %...% :: goto if (10) ()3. 变量 set4. 获取参数 %数字 %*5. 退出 exit6. 复制 copy7.读取输出文件内容 type8. 帮助 命令xxx /?9.等待当前命令运行结束后,才执行下一条命令 call10. 修改字体编码 chcp11. 特殊变量…...

版本工具报错:Error Unity Version Control
NotConfiguredClientException: Unity VCS client is not correctly configured for the current user:Client config file....

ECharts饼图-饼图标签对齐,附视频讲解与代码下载
一、图表效果预览 引言: 在数据可视化的世界里,ECharts凭借其丰富的图表类型和强大的配置能力,成为了众多开发者的首选。今天,我将带大家一起实现一个饼图图表,通过该图表我们可以直观地展示和分析数据。此外&#…...

Python实现基于WebSocket的stomp协议调试助手工具分享
stomp协议很简单,但是搜遍网络竟没找到一款合适的客户端工具。大多数提供的都是客户端库的使用。可能是太简单了吧!可是即便这样,假如有一可视化的工具,将方便的对stomp协议进行抓包调试。网上类似MQTT的客户端工具有很多…...

《语音识别方案选型研究》
《语音识别方案选型研究》 一、引言二、语音识别技术概述(一)语音识别的基本原理(二)语音识别技术的发展历程 三、语音识别方案的分类(一)基于云端的语音识别方案(二)基于本地的语音…...

解决关于HTML+JS + Servlet 实现前后端请求Session不一致的问题
1、前后端不分离情况 在处理session过程中,如果前后端项目在一个容器中,session是可以被获取的。例如如下项目结构: 结构 后端的代码是基本的设置值、获取值、销毁值的内容: 运行结果 由此可见,在前后统一的项目中&a…...