当前位置: 首页 > 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;用于…...

Java 入门基础篇14 - java面向对象思想以及特性

学习目标&#xff1a; 一、目标 面向对象思想类和对象对象的创建和使用属性和方法封装 开始学习&#xff1a; 二、编程思想 2.1 什么是编程思想 做人有做人的原则&#xff0c;编程也有编程的原则。这些编程的原则&#xff0c;就叫做编程思想。 2.2 面向过程和面向对象 二…...

第15篇:网络架构优化与综合案例分析

目录 引言 15.1 网络性能优化的方法与工具 15.1.1 带宽管理与流量控制 15.1.2 负载均衡 15.1.3 缓存优化 15.2 网络故障的排查与解决 15.2.1 常用的网络故障排查工具 15.2.2 网络故障排查案例 15.3 网络安全架构的综合设计案例 15.3.1 企业网络安全架构的要求 15.3.…...

UI自动化测试实战

补充&#xff1a;Selenium主要用于Web页面的自动化测试&#xff0c;它可以模拟用户的各种操作&#xff0c;如点击、输入、滚动等&#xff0c;来测试网页的功能。而Appium是一个开源的移动端自动化测试工具。 一、自动化测试实战章节 自动化测试流程测试用例编写项目自动化测试…...

东方智者颜廷利:以哲学思想促进世界和谐与无私奉献

【本社讯】在全球化的今天,东方智慧与哲学思想正逐渐成为促进世界和谐与理解的重要力量。近日,祖籍齐鲁大地山东济南的东方智者颜廷利以其深邃的哲学思想和对人类社会的深刻洞察,引起了国际社会的广泛关注。 颜廷利,一位致力于哲学研究与实践的智者,他的思想跨越古今,融合了东…...

基于 springboot vue停车场管理系统 设计与实现

博主介绍&#xff1a;专注于Java&#xff08;springboot ssm 等开发框架&#xff09; vue .net php phython node.js uniapp 微信小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设&#xff0c;从业十五余年开发设计教学工作 ☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不…...

如何验证ssl私钥和证书是否匹配?

从证书&#xff08;CRT&#xff09;文件提取公钥 openssl x509 -in server.crt -pubkey -noout | openssl sha256从证书签名请求&#xff08;CSR&#xff09;文件提取公钥 openssl req -in server.csr -pubkey -noout | openssl sha256从私钥&#xff08;KEY&#xff09;文件…...

MongoDB的基本操作

&#x1f337;数据库准备 &#x1f388;Mongoshell 1.在指定目录下创建mongodb文件夹、其子文件log和data以及mongodb.log cd /home/ubuntu mkdir -p mongodb/data mkdir -p mongodb/log touch mongodb/log/mongodb.log 执行mongodb命令启动mongdb服务 mongod --dbpath /h…...

spring mvc后端实现过程

文章目录 一、Spring mvc1、controller1.1、LoginController011.2、LoginController 2、service2.1、LoginService2.1、LoginInimplements 3、dao3.1、LoginMapper3.1、LoginMapper.xml 4、实体类 一、Spring mvc 1、controller 控制器层、处理用户的请求和响应&#xff0c; …...

102005

import os os.environ["CUDA_VISIBLE_DEVICES"] "0" # 设定使用的 GPUimport tensorflow as tf from dataset import generate_data import numpy as np from model import enhancednet# 检查 TensorFlow 是否可以识别 GPU gpus tf.config.list_physica…...

Cisco ACI环境给Leaf配置OOB带外管理IP方法

可以通过GUI 或CLI进行配置 通过CLI更简单&#xff0c;和配置传统交换机差不多&#xff0c; ACI中共有3大组件 APIC 控制器 SPINE 核心 LEAF 接入 下面我们将3种角色的带外IP配置方法都列出来 1 APIC配置带外IP This example shows how to configure out-of-band managemen…...