node.js 使用 net 模块模拟 websocket 握手进行数据传递操作示例

下面我将详细讲解“node.js 使用 net 模块模拟 WebSocket 握手进行数据传递操作示例”的完整攻略。

简介

WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议。在 WebSocket 连接被建立后,数据可以双向流动。WebSocket 协议使用的默认端口是 80 和 443,其中 80 是非安全连接,443 是安全连接。

Node.js 中可以使用 net 模块模拟 WebSocket 连接。net 模块是 Node.js 的核心模块之一,用于创建基于流的 TCP 服务器和客户端。

下面将演示如何使用 net 模块模拟 WebSocket 握手并进行数据传递。

WebSocket 握手

WebSocket 握手是客户端与服务器端建立 WebSocket 连接的第一步。在进行 WebSocket 握手时,客户端发送一条 HTTP 请求给服务器,请求头中包含 UpgradeConnection 字段,告诉服务器要升级协议并使用 WebSocket 协议进行通信。服务器收到客户端的请求之后,会发送一条 HTTP 响应,响应头中同样包含 UpgradeConnection 字段,并且也会包含一个 Sec-WebSocket-Accept 字段,该字段的值是经过计算的客户端的 Sec-WebSocket-Key 的哈希值,以此确认客户端使用 WebSocket 协议进行通信。

下面是一个示例的 WebSocket 握手请求和响应:

请求:

GET /chat HTTP/1.1
Host: example.com:8000
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

响应:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

WebSocket 数据帧

WebSocket 协议通过数据帧传递数据。WebSocket 数据帧由 2 ~ 14 个字节的帧头和帧体组成。帧头中包含了数据的类型、长度和掩码等信息。

帧类型包括文本帧和二进制帧。对于文本帧,帧体中是 UTF-8 编码的字符串;对于二进制帧,帧体中可以是任何二进制数据。

对于 WebSocket 帧,如果数据长度小于 126,则帧头的第二个字节值为数据的长度;如果数据长度在 126 个字节和 65535 个字节之间,则帧头的第二个字节值为 126,后续两个字节为数据的长度;如果数据长度超过 65535 个字节,则帧头的第二个字节值为 127,后续八个字节为数据的长度。

帧头第九个字节到第十二个字节为掩码,用于对数据进行编解码。在发送数据时,数据经过异或运算和掩码进行编码;在接收数据时,数据经过异或运算和掩码进行解码。

下面是一个示例的 WebSocket 数据帧的格式:

0 1 2 3 4 5 6 7
+-+-+-+-+-------+---------+
|F|R|R|R| opcode|M| Payload |
|I|S|S|S|       |A|     len |
|N|V|V|V|       |S|         |
| |1|2|3|       |K|         |
+-+-+-+-+-------+---------+

示例 1

下面是一个使用 net 模块模拟 WebSocket 握手的示例。在该示例中,客户端发起一条 GET 请求,服务器返回一条 101 切换协议的响应。

const net = require('net');

const server = net.createServer((socket) => {
  console.log('Client connected');

  socket.on('data', (data) => {
    console.log(`Received data: ${data}`);

    // 解析 HTTP 请求数据
    const request = data.toString();
    const [method, path, version] = request.trim().split('\r\n')[0].split(' ');

    // 发送 HTTP 响应数据
    socket.write(`HTTP/${version} 101 Switching Protocols\r\n`);
    socket.write('Upgrade: websocket\r\n');
    socket.write('Connection: Upgrade\r\n');
    socket.write('Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n');
    socket.write('\r\n');
  });

  socket.on('end', () => {
    console.log('Client disconnected');
  });
});

server.on('error', (error) => {
  console.error(`Server error: ${error}`);
});

server.listen(3000, () => {
  console.log('Server started');
});

上面的代码创建了一个服务器,并监听 3000 端口。当客户端连接到该服务器时,服务器会在控制台输出 Client connected 消息,在客户端向服务器发送数据时,服务器会在控制台输出 Received data 消息并解析 HTTP 请求数据,然后向客户端发送 101 切换协议的 HTTP 响应。

运行该示例后,可以使用 WebSocket 客户端工具向服务器发送一条 GET 请求:

GET / HTTP/1.1
Host: localhost:3000
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

正在监听 3000 端口的服务器将输出以下内容:

Client connected
Received data: GET / HTTP/1.1
Host: localhost:3000
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

示例 2

下面是一个使用 Buffer 对象编解码 WebSocket 数据帧的示例。该示例中,客户端向服务器发送一条文本帧,服务器将该文本帧返回给客户端。

const net = require('net');

const FIN = 0x80;
const TEXT_FRAME = 0x01;

function createWebSocketFrame(payload) {
  const length = Buffer.byteLength(payload);

  if (length < 126) {
    // 数据长度小于 126 字节
    const buffer = Buffer.alloc(2 + length);
    buffer.writeUInt8(FIN | TEXT_FRAME, 0);
    buffer.writeUInt8(length, 1);
    buffer.write(payload, 2);
    return buffer;
  } else if (length < 0xFFFF) {
    // 数据长度在 126 字节和 65535 字节之间
    const buffer = Buffer.alloc(2 + 2 + length);
    buffer.writeUInt8(FIN | TEXT_FRAME, 0);
    buffer.writeUInt8(126, 1);
    buffer.writeUInt16BE(length, 2);
    buffer.write(payload, 4);
    return buffer;
  } else {
    // 数据长度超过 65535 字节
    const buffer = Buffer.alloc(2 + 8 + length);
    buffer.writeUInt8(FIN | TEXT_FRAME, 0);
    buffer.writeUInt8(127, 1);
    buffer.writeBigUInt64BE(BigInt(length), 2);
    buffer.write(payload, 10);
    return buffer;
  }
}

const server = net.createServer((socket) => {
  console.log('Client connected');

  socket.on('data', (data) => {
    console.log(`Received data: ${data}`);

    const length = data.readUInt8(1) & 0x7F;
    const payload = data.toString('utf8', 2, 2 + length);

    console.log(`Received payload: ${payload}`);

    const frame = createWebSocketFrame(payload);
    socket.write(frame);
  });

  socket.on('end', () => {
    console.log('Client disconnected');
  });
});

server.on('error', (error) => {
  console.error(`Server error: ${error}`);
});

server.listen(3000, () => {
  console.log('Server started');
});

上面的代码创建了一个服务器,并监听 3000 端口。当客户端连接到该服务器时,服务器会在控制台输出 Client connected 消息,在客户端向服务器发送数据时,服务器会在控制台输出 Received payload 消息并解码 WebSocket 数据帧的数据,然后将该数据作为 WebSocket 帧的负载写回客户端。

运行该示例后,可以使用 WebSocket 客户端工具向服务器发送一条文本帧,例如:

81 85 37 ea b2 12 df a2 6f aa 58

其中 81 是帧头,85 是帧长度,以及负载数据 37 ea b2 12 df a2 6f aa 58。正在监听 3000 端口的服务器将输出以下内容:

Client connected
Received data: <Buffer 81 85 37 ea b2 12 df a2 6f aa 58>
Received payload: Hello, world!

结论

通过使用 net 模块模拟 WebSocket 握手并进行数据传递的示例,我们了解了 WebSocket 握手和数据帧的格式,并学会了如何使用 Node.js 编写 WebSocket 服务器和客户端代码。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:node.js 使用 net 模块模拟 websocket 握手进行数据传递操作示例 - Python技术站

(0)
上一篇 2023年6月8日
下一篇 2023年6月8日

相关文章

  • NodeJS实现自定义流的方法

    Node.js是一个基于Chrome V8引擎的JavaScript运行环境,可以使用它来开发服务器和命令行工具。流(Stream)是Node.js中非常重要的概念,是对提高数据读取和写入效率的一种机制。Node.js提供了一些内置的文件流和网络流,同时还提供了API来自定义流。 自定义流的原理 在Node.js中,流是一种基于事件的API,它具有以下几个特…

    node js 2023年6月8日
    00
  • 用Electron写个带界面的nodejs爬虫的实现方法

    Electron是一个开源框架,它能够让开发者使用Web技术(如HTML、CSS和JavaScript)创建跨平台应用程序。这里将介绍如何使用Electron构建一个带界面的nodejs爬虫应用程序的实现方法: 1. 安装Electron 首先需要安装和设置Electron,可参考Electron官方文档进行安装。 2. 创建项目 在本地创建爬虫项目,新建一…

    node js 2023年6月8日
    00
  • 关于vue的npm run dev和npm run build的区别介绍

    下面是关于 Vue 的 npm run dev 和 npm run build 的区别介绍的完整攻略。 一、npm run dev 和 npm run build 的作用 npm run dev 和 npm run build 都是 Vue CLI 项目中的常用命令,它们各自有着不同的作用: npm run dev:启动本地开发服务器,实时编译和热更新代码,…

    node js 2023年6月9日
    00
  • window通过vbs+bat实现自动在后台运行nodejs application

    首先,需要明确一点:该技术只适用于Windows环境。 1. 准备vbs和bat文件 在项目根目录下创建两个文件,一个是vbs文件,一个是bat文件。分别命名为run.vbs和start.bat。 run.vbs vbs文件是用来调用bat文件的,它需要同时在后台运行,因此我们需要使用以下的代码: Set WinScriptHost = CreateObje…

    node js 2023年6月8日
    00
  • node.js 全局变量的具体使用

    当我们编写Node.js代码时,我们经常需要在多个模块之间共享数据或者函数,这时候就需要用到Node.js的全局变量。 Node.js中的全局变量包括:__dirname、__filename、exports、module、process等。 下面将详细讲解全局变量的具体使用: 1. __dirname和__filename变量 __dirname和__fi…

    node js 2023年6月8日
    00
  • 详解nodejs实现本地上传图片并预览功能(express4.0+)

    以下是详解“详解nodejs实现本地上传图片并预览功能(express4.0+)”的完整攻略。 1. 确定目标 本文将讲解如何使用 Node.js 和 Express4.0+ 实现本地上传图片并预览功能。具体来说,我们要实现以下功能: 用户可以在网页上选择一张本地图片,并将其上传至服务器; 上传完成后,网页上会立即显示上传的图片以供用户预览。 2. 编写服务…

    node js 2023年6月8日
    00
  • 使用node.JS中的url模块解析URL信息

    使用node.js中的url模块可以方便地解析URL信息,以下是解析URL信息的完整攻略: 引入url模块 要使用url模块,首先需要在代码中引入该模块,可以使用require函数来实现: const url = require(‘url’); 使用url.parse()方法解析URL url模块提供了url.parse()方法,该方法可以接收一个URL字符…

    node js 2023年6月8日
    00
  • javascript实现双端队列

    下面是详细讲解 JavaScript 实现双端队列的完整攻略,包含以下内容: 双端队列的介绍 实现双端队列的方法 示例说明 1. 双端队列的介绍 双端队列是一种特殊的队列,它允许从两端进行数据的插入和删除操作。与普通队列相比,双端队列拥有更加丰富的操作,可以满足更多的需求。 2. 实现双端队列的方法 实现双端队列的方法有多种,其中最常见的方法是使用数组来实现…

    node js 2023年6月8日
    00
合作推广
合作推广
分享本页
返回顶部