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日

相关文章

  • 浅谈TypeScript 用 Webpack/ts-node 运行的配置记录

    下面我将详细讲解“浅谈TypeScript 用 Webpack/ts-node 运行的配置记录”的完整攻略。 1. 准备工作 在开始配置前,我们需要做一些准备工作: 安装 Node.js 我们需要在本地安装 Node.js,来运行和打包 TypeScript 代码。 初始化项目 在项目的根目录下运行以下命令,初始化项目并创建一个 package.json 文…

    node js 2023年6月8日
    00
  • nodejs 使用http进行post或get请求的实例(携带cookie)

    下面我将为你讲解“nodejs 使用http进行post或get请求的实例(携带cookie)”的完整攻略。 一、前置知识 在了解如何使用nodejs进行post或get请求之前,你需要了解以下前置知识: http协议和http请求 url模块:用于解析和格式化URL querystring模块:用于解析和格式化查询字符串 http模块:用于创建客户端和服务…

    node js 2023年6月8日
    00
  • nodejs个人博客开发第七步 后台登陆

    下面我将详细讲解“nodejs个人博客开发第七步 后台登陆”的完整攻略。 1. 确定需求和设计页面 在开始开发后台登录功能之前,需要先确定需求和设计登录页面。在设计登录页面时,需要考虑以下几个方面: 登录页面应该有输入账号和密码的表单,以及登录按钮。 检查用户输入的账号和密码是否合法,如果不合法,则需要提示用户重新输入。 如果用户输入的账号和密码正确,则跳转…

    node js 2023年6月8日
    00
  • 详解Node.js:events事件模块

    下面来详细讲解一下“详解Node.js:events事件模块”的完整攻略。 什么是事件模块 在 Node.js 中,events 模块是实现事件驱动的核心模块,提供了 EventEmitter 类用于事件的注册和触发。使用 events 模块的程序可以通过事件的方式触发回调函数,从而实现异步编程。 常用的事件模块方法 常用的 events 模块方法包括: E…

    node js 2023年6月8日
    00
  • nodejs如何获取时间戳与时间差

    获取时间戳可以使用JavaScript内置的Date对象。该对象的getTime()方法可以用来获取当前时间距离1970年1月1日00:00:00 UTC的毫秒数,也就是时间戳。在Node.js环境中使用Date.now()方法可以快捷地获取当前时间戳。以下是一个获取当前时间戳的示例代码: const timestamp = Date.now(); cons…

    node js 2023年6月8日
    00
  • js fill函数填充数组或对象的解决方法

    当我们需要用特定值填充JavaScript数组或对象时,可以使用fill()函数来快速完成。fill()函数可以接受两个参数,第一个参数代表要填充的值,第二个参数代表要开始填充的索引位置。如果省略第二个参数,默认从索引0开始填充。下面是fill()函数的语法: arr.fill(value[, start[, end]]) 这里的arr可以是数组或对象,va…

    node js 2023年6月8日
    00
  • vue-element-admin中node-sass换成dart-sass,安装依赖报code 128多种问题的解决方法

    下面是详细讲解: 概述 vue-element-admin是基于Vue.js开发的后台管理系统模板,其中使用了node-sass作为样式预处理器。然而,由于node-sass的维护状态不佳,建议将其替换为dart-sass,以避免bug和性能问题。在替换过程中,可能会出现一些依赖安装问题,本文将为您提供完整的解决方案。 步骤 1. 卸载node-sass 在…

    node js 2023年6月9日
    00
  • nodejs文件操作模块FS(File System)常用函数简明总结

    下面是关于Node.js文件操作模块FS常用函数的简明总结攻略。 FS模块 Node.js中的File System模块,简称FS模块,提供了完整的文件系统访问功能,包括文件读取、创建等常用操作。在使用FS模块时需要先引入: const fs = require(‘fs’); 常用函数 下面我们来看几个常用函数。 fs.writeFile fs.writeF…

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