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…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...

无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...

CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...

Mac软件卸载指南,简单易懂!
刚和Adobe分手,它却总在Library里给你写"回忆录"?卸载的Final Cut Pro像电子幽灵般阴魂不散?总是会有残留文件,别慌!这份Mac软件卸载指南,将用最硬核的方式教你"数字分手术"࿰…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...

分布式增量爬虫实现方案
之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...

HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...