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 实现中需要解决的其他问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/892929.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

pytorch与卷积神经网络实战笔记

课程视频链接 CNN卷积神经网络算法原理 全神经网络的整体结构 输入层&#xff08;x1, x2, x3…&#xff09;->隐藏层&#xff08;全连接&#xff09;->输出层&#xff0c;整体就类似于一个函数&#xff0c;输入x&#xff0c;经过函数module(x)得到输出y的过程&#xf…

QtModel

QModelIndex index1 model->index(row,column,QModelIndex());QModelIndex index2 model->index(row.column,index2); QSqlQuery::size() 仅在使用了 QSqlQuery::exec() 后并且查询结果集的所有行都被读取时才有效。如果结果集很大或在使用游标的情况下&#xff0c;返回…

AVL树的实现

AVL树 1. AVL的概念2. AVL树的实现2.1 AVL树的结构2.2 AVL树的插⼊2.2.1 AVL树插⼊⼀个值的⼤概过程2.2.2 平衡因⼦更新2.2.3 插⼊结点及更新平衡因⼦的代码实现 2.3 旋转2.3.1 旋转的原则2.3.2 右单旋2.3.3 右单旋代码实现2.3.4 左单旋2.3.5 左单旋代码实现2.3.6 左右双旋2.3.…

【排序】——2.快速排序法(含优化)

快速排序法 递归法 霍尔版本(左右指针法) 1.思路 1、选出一个key&#xff0c;一般是最左边或是最右边的。 2、定义一个begin和一个end&#xff0c;begin从左向右走&#xff0c;end从右向左走。&#xff08;需要注意的是&#xff1a;若选择最左边的数据作为key&#xff0c;则…

Arduino配置ESP32环境

Arduino配置ESP32环境 引言一、IDE下载教程操作取巧方法 二、社区安装包三、官方手动安装 引言 最近入手了一款ESP32-C3的开发板&#xff0c;想继续沿用现有Arduino IDE&#xff0c;网上看了很多方法&#xff0c;大致分了三类&#xff1a;IDE下载、社区安装包、github手动配置…

基于SpringBoot+Vue+uniapp的诗词学习系统的详细设计和实现

详细视频演示 请联系我获取更详细的演示视频 项目运行截图 技术框架 后端采用SpringBoot框架 Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的开源框架。它采用约定大于配置的理念&#xff0c;提供了一套默认的配置&#xff0c;让开发者可以更专注于业务逻辑而不…

ROS理论与实践学习笔记——5 ROS机器人系统仿真之URDF(Unified Robot Description Format)语法详解

URDF 文件是一个标准的 XML 文件格式&#xff0c;用于在 ROS 中描述机器人模型的结构。URDF 通过预定义的一系列标签&#xff0c;简洁地表达机器人的组成和运动关系。虽然机器人模型可能非常复杂&#xff0c;但在 URDF 中可以主要简化为两个核心部分&#xff1a; 连杆&#xff…

6.2 遍历重定位表

本节我们将编写一个遍历重定位表的示例程序&#xff0c;打印重定位表。 本节必须掌握的知识点&#xff1a; 遍历重定位表 6.2.1 遍历重定位表 实验四十三&#xff1a;遍历重定位表 以下代码实现打印"c:\\notepad64.exe"进程重定位表的所有信息。 /*--------------…

【详尽-实战篇】使用Springboot生成自带logo或者图片的二维码-扫描二维码可以跳转到指定的页面-Zing-core

先上效果图 项目源码&#xff1a;https://download.csdn.net/download/qq_43055855/89891285 源码地址 手机扫描二维码跳转到指定网页 概述 这个项目是一个基于 Java 的二维码生成与解析工具&#xff0c;主要由 QRCodeUtil 和 QRCodeController 两个类组成。它利用了 Google…

python 爬虫 入门 一、基础工具

目录 一&#xff0c;网页开发者工具的使用 二、通过python发送请求 &#xff08;一&#xff09;、get &#xff08;二&#xff09;、带参数的get &#xff08;三&#xff09;、post 后续&#xff1a;数据解析 一&#xff0c;网页开发者工具的使用 我们可以用 requests 库…

人脸识别-特征算法

文章目录 一、LBPH算法1.基本原理2.实现步骤3.代码实现 二、Eigenfaces算法1.特点2.代码实习 三、FisherFaces算法1.算法原理2.算法特点3.代码实现 四、总结 人脸识别特征识别器是数字信息发展中的一种生物特征识别技术&#xff0c;其核心在于通过特定的算法和技术手段&#xf…

leader必备技能——编写高质量测试计划

前言 作为一个想成为leader(不论是整个测试部门还是小项目组的leader&#xff09;的人&#xff0c;测试计划编写是必备技能。 接下来我们先了解一下测试计划的一些基础知识再进一步了解。 什么是测试计划&#xff1f; 测试计划是对测试过程的整体设计&#xff0c;测试计划确…

Spring Boot知识管理:智能搜索与分析

3系统分析 3.1可行性分析 通过对本知识管理系统实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本知识管理系统采用JAVA作为开发语言&#xff0c;Spring Boot框…

c#中多态的实例应用说明

在C#中&#xff0c;多态性是通过继承和实现接口来实现的&#xff0c;允许编写可以使用基类型的代码&#xff0c;然后使用派生类型的特定行为。 一.实例界面显示 二.源码界面显示 //定义的基类abstract class Shape{public abstract int Area();//基类中的抽象方法}//定义矩形的…

【前端】如何制作一个自己的网页(6)

接上文 网络中的图片 我们也可以在百度等网站搜索自己喜欢的图片。 此时对图片点击右键&#xff0c;选择【复制图片地址】&#xff0c;即可获得该图片的网络地址。 其实在HTML中&#xff0c;除了图片以外&#xff0c;我们还可以利用地址找到另一个网页。 如右图所示&#…

第一次排查 Java 内存泄漏,别人觉得惊险为什么我觉得脸红害羞呢

今天前端一直在群里说&#xff0c;服务是不是又挂了&#xff1f;一直返回 503。我一听这不对劲&#xff0c;赶紧看了一眼 K8S 的 pod 状态&#xff0c;居然重启了4次。测试环境只有一个副本&#xff0c;所以赶紧把副本数给上调到了3个。 堵住前端的嘴&#xff0c;免得破坏我在…

【C语言】一维数组应用Fibonacci数列

Fibonacci数&#xff08;斐波那契数列&#xff09; 前两项为1&#xff0c;从第三项开始&#xff0c;每一项为前两项的和。可以知道连续三项的关系&#xff1a;f[i]f[i-1]f[i-2] 使用数组进行存储&#xff0c;十分方便。可以知道前n项的fibonacci数。 #include <stdio.h>…

数据治理(2)-数据标准

前言 在建模前规划制定数据标准&#xff0c;或在建模使用过程中根据业务情况沉淀企业业务的数据标准。通过规范约束标准代码、度量单位、字段标准、命名词典&#xff0c;来保障数据处理的一致性&#xff0c;从源头上保障数据的标准化生产&#xff0c;节约后续数据应用和处理的…

什么是 C/2023 A3(紫金山-阿特拉斯)彗星?让我们用 Python 来绘制它的路径

彗星的基本概念 彗星&#xff08;Comet&#xff09;&#xff0c;是指进入太阳系内亮度和形状会随日距变化而变化的绕日运动的天体&#xff0c;呈云雾状的独特外貌&#xff0c;也是中国神话传说的扫帚星&#xff08;星官名&#xff09;。彗星分为彗核、彗发、彗尾三部分。彗核由…

一起体验AI动手实验,OceanBase 2024 年度发布会精彩预告

2024年OceanBase年度发布会将于10月23日在北京望京凯悦酒店举行。此次大会围绕“不止于记录”的主题&#xff0c;共同探讨当前数据库领域的前沿话题&#xff0c;包含主论坛、分论坛、AI 动手实训营、开源技术交流会等多个环节&#xff0c;诚邀全国各地的企业和开发者共同参与&a…