data是什么数据结构?TCP是一个字节流,如果我想在这个字节流中拆分出不同的独立消息,该怎么做?

const net = require('net');

const clients = [];

// index.js
console.log('Aggregation Engine Agent is running.............................................................!');

// // 持续运行进程
// setInterval(() => {
//   console.log('>> Aggregation Engine Agent << running...');
// }, 1000);

// 创建TCP服务器
const server = net.createServer((socket) => {
  // the function will be run when a client connects to the server
  console.log('>>>>> >>>> >>> >> > ----------------- New client connected');
  
  // 将客户端连接保存到数组中
  clients.push(socket);

  // // 监听客户端发送的数据
  // socket.on('connect', (data) => {
  //   console.log('>>>>> >>>> >>> >> > connected data:', data.toString());
    
  //   // 你可以在这里处理客户端发来的数据
  // });

  // 监听客户端发送的数据
  socket.on('data', (data) => {
    console.log('Received data ######################################:', data.toString());
    
    // 你可以在这里处理客户端发来的数据
  });

  // 监听客户端关闭连接
  socket.on('end', () => {
    console.log('Client disconnected');
    
    // 从数组中删除已断开连接的客户端
    const index = clients.indexOf(socket);
    if (index !== -1) {
      clients.splice(index, 1);
    }
  });

  // 监听错误事件
  socket.on('error', (err) => {
    console.error('Socket error:', err);
  });
});

// 设置服务器监听的端口和主机
const PORT = 18760;
// const HOST = '127.0.0.1';
const HOST = '0.0.0.0'; // 局域网内允许所有客户端连接

server.listen(PORT, HOST, () => {
  console.log(`TCP server listening on ${HOST}:${PORT}`);
});


上面的这个nodejs项目代码文件,关注 socket.on('data', (data) => { /* ... */ }); 部分。
data是什么数据结构?TCP是一个字节流,如果我想在这个字节流中拆分出不同的独立消息,该怎么做?

eeeeeeeeeeeeeeeeeeeeeeeeeeee

你对socket.on('data', ...)部分的理解是正确的。这里,data 是一个 Buffer 对象,它表示从客户端接收到的原始字节流。

1. data 的数据结构

在 Node.js 中,socket.on('data', ...) 中的 data 是一个 Buffer,这是 Node.js 用来处理原始二进制数据的对象。你可以将它看作一个字节数组,可以通过 Buffer 的方法来操作。

socket.on('data', (data) => {
  console.log('Received data:', data);  // data是一个Buffer对象
  console.log('Data as string:', data.toString());  // 转换成字符串
});

2. 如何拆分字节流中的消息

TCP 是基于字节流的协议,客户端发送的数据不会被自动拆分成消息。要拆分字节流,你需要自己定义一种消息边界标识。这可以通过多种方式完成,以下是一些常见的方法:

方法 1:使用固定长度的消息

假设每个消息的大小是固定的,你可以每次读取固定长度的字节来获取完整的消息。

例如,假设每个消息是 10 字节:

socket.on('data', (data) => {
  // 假设每条消息是10字节
  let messageLength = 10;
  let offset = 0;

  while (offset + messageLength <= data.length) {
    const message = data.slice(offset, offset + messageLength);
    console.log('Received message:', message.toString());
    offset += messageLength;
  }
});

方法 2:使用消息长度作为前缀

你可以定义每条消息的前几个字节作为消息长度的标识。比如,每个消息前面加上 4 个字节表示消息的长度,然后按照这个长度来拆分消息。

示例:

假设消息的前 4 个字节表示消息的长度:

socket.on('data', (data) => {
  let offset = 0;
  
  // 每次读取 4 个字节的长度字段
  while (offset + 4 <= data.length) {
    const messageLength = data.readUInt32BE(offset);  // 读取消息长度(大端模式)
    offset += 4;

    if (offset + messageLength <= data.length) {
      const message = data.slice(offset, offset + messageLength);
      console.log('Received message:', message.toString());
      offset += messageLength;
    } else {
      // 如果剩余的数据不足以完成一条消息,等待更多数据
      break;
    }
  }
});

方法 3:使用特定的分隔符

如果消息是文本数据,你可以使用特定的分隔符来分隔每条消息。例如,可以使用换行符(\n)作为分隔符,将字节流按照换行符拆分成独立的消息。

socket.on('data', (data) => {
  let message = data.toString();
  
  // 使用换行符分隔消息
  let messages = message.split('\n');
  
  messages.forEach((msg) => {
    if (msg.trim()) {  // 排除空消息
      console.log('Received message:', msg);
    }
  });
});

3. 如何选择方法

  • 固定长度:适用于消息长度固定的情况。
  • 消息长度前缀:适用于你知道每条消息长度的情况,或者长度是动态变化的,但有前缀指示长度。
  • 分隔符:适用于文本数据,并且每条消息由某种明确的分隔符(如换行符)分隔的情况。

4. 如何确保完整的消息

TCP是流式的,这意味着你不能保证每次接收到的数据都是完整的消息。如果你正在拆分数据,确保处理以下情况:

  • 数据碎片:一条消息可能会被拆成多个数据块(例如,一次data事件可能只接收到部分消息)。
  • 消息重组:你可能需要缓冲不完整的消息,等待剩余的数据到来,直到你能将整个消息拼凑出来。

5. 示例代码:使用长度前缀来拆分消息

这是一个综合的示例,使用前缀来表示消息的长度,并处理流式数据:

let buffer = Buffer.alloc(0);

socket.on('data', (data) => {
  buffer = Buffer.concat([buffer, data]);  // 将新接收到的数据附加到缓冲区

  // 处理所有完整的消息
  while (buffer.length >= 4) {  // 至少需要4个字节来读取长度
    const messageLength = buffer.readUInt32BE(0);  // 读取消息长度(大端模式)
    
    // 如果缓冲区的长度大于等于消息长度 + 4字节(消息长度字段)
    if (buffer.length >= messageLength + 4) {
      const message = buffer.slice(4, 4 + messageLength);  // 提取消息内容
      console.log('Received message:', message.toString());
      
      // 更新缓冲区,移除已处理的消息
      buffer = buffer.slice(4 + messageLength);
    } else {
      // 如果数据不完整,等待更多数据
      break;
    }
  }
});

这样,你可以确保消息在流中被正确拆分,并且处理过程中不会丢失数据。

如果你有其他问题或想深入探讨,请随时告诉我!

posted on   及途又八  阅读(2)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示