当前位置: 首页 > news >正文

Node.js 中的 WebSocket 底层实现

WebSockets 是一种网络通信协议,可实现双向客户端-服务器通信。

WebSockets 通常用于需要即时更新的应用程序,使用 HTTP 之上的持久双工通道来支持实时交互,而无需持续进行连接协商。服务器推送是 WebSockets 的众多常见用例之一。

本文首先从代码角度研究了 JavaScript 中 WebSockets 方程的两边,在服务器上使用 Node.js,在浏览器中使用原始 JavaScript。

WebSocket 协议

以前,在浏览器中通过 HTTP 进行双工通信或服务器推送需要相当多的技巧。如今,WebSockets 已成为 HTTP 的正式组成部分。它充当普通 HTTP 连接的“升级”连接。

WebSockets 可让您在浏览器客户端和后端之间来回发送任意数据。任一端都可以发起新消息,因此您拥有了用于各种需要持续通信或广播的实时应用程序的基础架构。开发人员将 WebSockets 协议用于游戏、聊天应用程序、直播、协作应用程序等。可能性无穷无尽。

为了本文的目的,我们将创建一个简单的服务器和客户端,然后使用它们来深入了解 WebSockets 通信期间发生的情况。

创建一个简单的服务器

首先,您需要一个/server包含两个子目录的目录/client和/server。有了这些之后,您需要一个非常简单的 Node 服务器,该服务器建立 WebSocket 连接并回显发送给它的任何内容。接下来,进入/websockets/server并开始一个新项目:

$ npm init

接下来我们需要ws 项目,我们将使用它来支持 WebSocket:

$ npm install ws

有了这些,我们可以绘制一个简单的回显服务器,如下所示 echo.js:

// echo.js
const WebSocket = require('ws');const wss = new WebSocket.Server({ port: 3000 });wss.on('connection', (ws) => {console.log('Client connected');ws.on('message', (message) => {console.log('Received message:', message);   ws.send(message); // Echo the message back to the client});ws.on('close', () => {console.log('Client disconnected');});
});console.log(‘server started’);

这里,我们监听端口 3000,然后监听WebSocket.server对象上的连接事件。一旦connection发生,我们就会获取套接字对象 ( ws) 作为回调的参数。使用它,我们监听另外两个事件:message和close。

每当客户端发送消息时,它都会调用onMessage处理程序并将消息传递给我们。在该处理程序中,我们使用ws.send()方法发送回显响应。

请注意 ws.send() 还允许我们在需要时发送消息,因此我们可以根据其他事件将更新推送到客户端,例如来自服务的更新或来自另一个客户端的消息。

处理程序onClose让我们在客户端断开连接时执行工作。在本例中,我们只需记录它即可。

测试套接字服务器

如果能有一种简单的方法从命令行测试套接字服务器就好了,Websocat 工具非常适合此目的。它的安装过程很简单,如这里所述,并且有许多使用它的示例。

现在启动服务器:

/websockets/server $ node echo.js

使用Ctrl-z和将其置于背景状态$ bg,然后运行以下命令:

$ ./websocat.x86_64-unknown-linux-musl -t --ws-c-uri=wss://localhost:3000/ - ws-c:cmd:'socat - ssl:echo.websocket.org:443,verify=0'

这将建立一个开放的 WebSocket 连接,让您可以输入到控制台并查看响应。您将获得如下交互:

$ node echo.js 
Server started
^Z
[1]+  Stopped                 node echo.js
matthewcarltyson@dev3:~/websockets/server$ bg
[1]+ node echo.js &
matthewcarltyson@dev3:~/websockets/server$ ./websocat.x86_64-unknown-linux-musl -t --ws-c-uri=wss://localhost:3000/ - ws-c:cmd:'socat - ssl:echo.websocket.org:443,verify=0'
Request served by 7811941c69e658
An echo test
An echo test
Works
Works
^C
matthewcarltyson@dev3:~/websockets/server$ fg
node echo.js
^C

创建客户端

现在,让我们进入/websockets/client目录并创建一个可用于与服务器交互的网页。让服务器在后台运行,我们将从客户端访问它。

首先,创建一个index.html如下文件:

// index.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>WebSocket Client</title>
</head>
<body><h1>WebSocket Client</h1><input type="text" id="message" placeholder="Enter message"><button id="send-btn">Send</button><div id="output"></div><script src="script.js"></script>
</body>
</html>

这仅提供了一个文本输入和一个提交按钮。它本身不做任何事情,仅提供我们在包含的脚本文件中需要的 DOM 元素:

// script.js
const wsUri = "ws://localhost:3000";
const outputDiv = document.getElementById("output");
const messageInput = document.getElementById("message");
const sendButton = document.getElementById("send-btn");let websocket;function connect() {websocket = new WebSocket(wsUri);websocket.onopen = function (event) {outputDiv.innerHTML += "
Connected to server!";};websocket.onmessage = function (event) {const receivedMessage = event.data;outputDiv.innerHTML += "
Received: " + receivedMessage + "";};websocket.onerror = function (event) {outputDiv.innerHTML += "
Error: " + event.error + "";};websocket.onclose = function (event) {outputDiv.innerHTML += "
Connection closed.";};
}sendButton.addEventListener("click", function () {const message = messageInput.value;if (websocket && websocket.readyState === WebSocket.OPEN) {websocket.send(message);messageInput.value = "";} else {outputDiv.innerHTML += "
Error: Connection not open.";}
});connect(); // Connect immediately

此脚本使用浏览器原生 API 设置了多个事件处理程序。脚本加载后,我们立即启动 WebSocket,并监视open、onclose、onmessage和onerror事件。

每个事件都会将其更新附加到 DOM。最重要的是onmessage,我们从服务器接受消息并显示它。

按钮本身的 Click 处理程序接收用户输入的输入(messageInput.value),并使用 WebSocket 对象通过函数将其发送到服务器send()。然后我们将输入的值重置为空字符串。

假设后端仍在运行且可在ws://localhost:3000处使用,我们现在可以运行前端。我们可以使用http-server作为运行前端的简单方法。

这是一种在 Web 服务器中托管静态文件的简单方法,类似于 Python 的 http 模块或Java 的简单 Web 服务器,但适用于 Node。

它可以作为全局 NPM 包安装,也可以简单地npx从客户端目录使用运行:

/websockets/client/ $ npx http-server -o

 当我们运行上一个命令并访问该页面时,我们会得到应有的表单。但是当我们在输入框中输入消息并点击发送时,它显示:

Received: [object Blob]

 

ws如果您查看浏览器开发控制台,所有内容都通过 WebSocket 通道(选项卡中的选项卡)进行network。问题是,为什么它会以 blob 的形式返回?

如果你查看服务器控制台,它会显示:

Client connected
Received message: <Buffer 6f 6d 20 6d 61 6e 69 20 70 61 64 6d 65 20 68 75 6d>

所以现在我们知道问题出在服务器上。问题是ws模块的较新版本不会自动将消息解码为字符串,而是只提供二进制缓冲区。这是echo.js onmessage处理程序中的快速修复: 

ws.on('message', (message, isBinary) => {message = isBinary ? message : message.toString();console.log('Received message:', message);   ws.send(message); 
});

我们对回调使用第二个参数isBinary,如果处理程序接收到一个字符串,我们会使用快速将其转换为字符串message.toString()。

这篇快速指南阐明了 WebSocket 客户端-服务器通信的底层机制,没有框架的混淆。

如您所见,使用 WebSocket 高级功能的基础非常简单。只需使用简单的回调和消息发送,您就可以使用浏览器标准 API 和流行的 Node 库进行全双工和异步通信。

当然,在许多项目中,你会希望在前端使用React之类的东西,在后端使用Node或类似的运行时。幸运的是,一旦你了解了基础知识,这些框架就很容易集成。

此处的讨论和示例有意忽略了安全性,就像 Web 开发的每个领域一样,安全性增加了一层复杂性,需要在堆栈的两侧进行管理。

可扩展性和错误处理是我们在实际 WebSockets 实现中需要解决的其他问题。

相关文章:

Node.js 中的 WebSocket 底层实现

WebSockets 是一种网络通信协议&#xff0c;可实现双向客户端-服务器通信。 WebSockets 通常用于需要即时更新的应用程序&#xff0c;使用 HTTP 之上的持久双工通道来支持实时交互&#xff0c;而无需持续进行连接协商。服务器推送是 WebSockets 的众多常见用例之一。 本文首先…...

MySQl数据库的基本操作

1.1创建数据库 使用CREATE DATABASE语句可以轻松创建MySQL数据库&#xff0c;语法如下&#xff1a; CREATE DATABASE 数据库名; 例&#xff1a;创建fruitsales数据库 CREATE DATABASE fruitsales;1.2 查看数据库 使用SHOW语句查看当前服务器下所有已经存在的数据库 SHOW DAT…...

Egg.js 项目的合理 ESLint 配置文件模板

Egg.js 项目的合理 ESLint 配置文件模板 安装依赖 npm install eslint babel/eslint-parser eslint-plugin-import eslint-plugin-promise eslint-plugin-node --save-dev extends: 扩展了 eslint-config-egg 以及其他一些常用的插件配置。 parser: 使用 babel/eslint-parse…...

算法专题七: 分治归并

目录 1. 排序数组2. 交易逆序对的总数3. 计算右侧小于当前元素的个数4. 翻转对 1. 排序数组 算法思路: 本道题使用归并的思路进行排序, 先讲数组分为左右两个区间, 然后合并两个有序数组. class Solution {vector<int> tmp; public:vector<int> sortArray(vector&…...

一个基于vue功能强大的表格组件--vxe-table的二次封装

基础使用 一个基于 vue 的 PC 端表格组件&#xff0c;支持增删改查、虚拟滚动、懒加载、快捷菜单、数据校验、树形结构、打印导出、表单渲染、数据分页、虚拟列表、模态窗口、自定义模板、渲染器、贼灵活的配置项、扩展接口等… <vxe-grid v-bind"gridOptions1"…...

CSS网页布局(重塑网页布局)

一、实现两列布局 许多网站有一些特点&#xff0c;如页面顶部放置一个大的导航或广告条&#xff0c;右侧是链接或图片&#xff0c;左侧放置主要内容&#xff0c;页面底部放置版权信息等。 一般情况&#xff0c;此类网页布局的两列都有固定的宽度&#xff0c;而且从内容上很容易…...

计算机网络:数据链路层 —— 以太网(Ethernet)

文章目录 局域网局域网的主要特征 以太网以太网的发展100BASE-T 以太网物理层标准 吉比特以太网载波延伸物理层标准 10吉比特以太网汇聚层交换机物理层标准 40/100吉比特以太网传输媒体 局域网 局域网&#xff08;Local Area Network, LAN&#xff09;是一种计算机网络&#x…...

考研前所学c语言02(2024/10/16)

1.一个十进制的数转化为二进制的就是不断除二取余&#xff0c;得到的余数从下到上取 比如123&#xff1a; 结果为&#xff1a; 同理其他的十进制转八进制&#xff0c;十六进制就除八&#xff0c;除十六即可 再比如123转十六进制&#xff1a; 因为余数是11&#xff0c;十六进…...

R语言绘图——坐标轴及图例

掌握坐标轴与图例的设置与调整&#xff0c;对于提升数据可视化的清晰度和可读性至关重要。通过这些工具&#xff0c;可以有效地传达数据背后的故事&#xff0c;提高图表的表现力。 0x01 坐标轴 一、坐标轴的设置 1、修改坐标轴的标签 在ggplot2中&#xff0c;坐标轴是根据数…...

JDK中socket源码解析

目录 1、Java.net包 1. Socket通信相关类 2. URL和URI处理类 3. 网络地址和主机名解析类 4. 代理和认证相关类 5. 网络缓存和Cookie管理类 6. 其他网络相关工具类 2、什么是socket&#xff1f; 3、JDK中socket核心Api 4、核心源码 1、核心方法 2、本地方法 3、lin…...

Ansible自动化运维项目实战指南

Ansible自动化运维项目实战指南 在当今快速发展的IT环境中&#xff0c;运维工作的复杂性和规模性日益增加&#xff0c;传统的手动运维方式已难以满足高效、可靠、可重复性的需求。Ansible作为一款开源的自动化运维工具&#xff0c;凭借其简单易用、无需代理、基于SSH的架构特性…...

MySQL【知识改变命运】10

联合查询 0.前言1.联合查询在MySQL里面的原理2.练习一个完整的联合查询2.1.构造练习案例数据2.2 案例&#xff1a;⼀个完整的联合查询的过程2.2.1. 确定参与查询的表&#xff0c;学⽣表和班级表2.2.2. 确定连接条件&#xff0c;student表中的class_id与class表中id列的值相等2.…...

Java学习教程,从入门到精通, Java 基础语法(4)

1、Java 基础语法 一、Java 简介与开发环境搭建 Java 简介&#xff1a;Java 是一种面向对象的编程语言&#xff0c;具有跨平台、安全、稳定等特点。Java 主要应用于企业级应用、Android 应用开发、大数据处理等领域。开发环境搭建&#xff1a;搭建 Java 开发环境需要安装 JDK…...

反编译工具-Jclasslib的使用,与Java方法调用的探索

这里写目录标题 前言IDEA下查看字节码的两种方法使用idea自带的插件工具安装插件 为什么没有看出方法调用关系原因分析工厂举例 知识补充语言java可移植性 总结 前言 画时序图的时候&#xff0c;我想验证下方法的调用是否写的正确。方法调用不仅涉及到程序的基本逻辑流程&#…...

力扣 简单 876.快慢指针

文章目录 题目介绍题解 题目介绍 题解 class Solution {public ListNode middleNode(ListNode head) {ListNode slow head, fast head;while(fast ! null && fast.next ! null){slow slow.next;fast fast.next.next;}return slow;} }...

FineReport 计算同比增长

1、数据库查询 SELECTt1.年,t1.月,t1.总金额 AS 同期金额,t1.仓库名称,t2.总金额 AS 上期金额 FROMtest t1LEFT JOIN test t2 ON ( t1.年 t2.年 1 ) AND t1.月 t2.月 AND t1.仓库名称 t2.仓库名称2、配置字段 月份字段加后缀 月 数据列加后缀 计算同比增长率 if(LEN(B3)0 …...

从0开始深度学习(12)——多层感知机的逐步实现

依然以Fashion-MNIST图像分类数据集为例&#xff0c;手动实现多层感知机和激活函数的编写&#xff0c;大部分代码均在从0开始深度学习&#xff08;9&#xff09;——softmax回归的逐步实现中实现过 1 读取数据 import torch from torchvision import transforms import torchv…...

如何利用OpenCV和yolo实现人脸检测

在之前的blog里面&#xff0c;我们有介绍OpenCV和yolo的区别&#xff0c;本文就人脸检测为例&#xff0c;分别介绍下OpenCV和yolo的实现方式。 OpenCV实现人脸检测 一、安装 OpenCV 首先确保你已经安装了 OpenCV 库。可以通过以下方式安装&#xff1a; 使用包管理工具安装&…...

015集——c# 实现CAD excel交互(CAD—C#二次开发入门)

第一步&#xff1a;添加引用 程序集—>扩展 namespace WindowsFormsApp2 {public partial class Form1 : Form{public Form1(){InitializeComponent();}private void Form1_Load(object sender, EventArgs e){}private void 获取当前excel_Click(object sender, EventArgs e…...

【计网笔记】以太网

经典以太网 总线拓扑 物理层 Manchester编码 数据链路层 MAC子层 MAC帧 DIX格式与IEEE802.3格式 IEEE802.3格式兼容DIX格式 前导码&#xff08;帧开始定界符SOF&#xff09; 8字节 前7字节均为0xAA第8字节为0xAB前7字节的Manchester编码将产生稳定方波&#xff0c;用于…...

KubeSphere 容器平台高可用:环境搭建与可视化操作指南

Linux_k8s篇 欢迎来到Linux的世界&#xff0c;看笔记好好学多敲多打&#xff0c;每个人都是大神&#xff01; 题目&#xff1a;KubeSphere 容器平台高可用&#xff1a;环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…...

网络六边形受到攻击

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 抽象 现代智能交通系统 &#xff08;ITS&#xff09; 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 &#xff08;…...

CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型

CVPR 2025 | MIMO&#xff1a;支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题&#xff1a;MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者&#xff1a;Yanyuan Chen, Dexuan Xu, Yu Hu…...

Zustand 状态管理库:极简而强大的解决方案

Zustand 是一个轻量级、快速和可扩展的状态管理库&#xff0c;特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

跨链模式:多链互操作架构与性能扩展方案

跨链模式&#xff1a;多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈&#xff1a;模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展&#xff08;H2Cross架构&#xff09;&#xff1a; 适配层&#xf…...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...

Caliper 配置文件解析:config.yaml

Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...

初学 pytest 记录

安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...