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…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...

页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...