You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于Promise的net.createServer版本及TCP服务器data事件异步实现问询

适配async/await的TCP服务器data事件处理方案

嘿,这个问题问得很实用!Node.js原生的net模块确实是基于事件驱动的设计,没有直接提供Promise风格的API来处理data事件,但我们完全可以自己动手封装,或者用一些小技巧来适配async/await,不一定非得依赖第三方库。下面给你几种可行的方案:

1. 封装单次data事件的Promise版本

如果你只需要等待客户端发送的一次数据,可以把conn.on('data')封装成一个返回Promise的函数,同时处理错误和连接关闭的边界情况,避免内存泄漏:

const net = require('net');

// 封装等待data事件的Promise函数
function waitForData(conn) {
  return new Promise((resolve, reject) => {
    // 定义事件处理函数
    const handleData = (data) => {
      cleanup();
      resolve(data);
    };
    const handleError = (err) => {
      cleanup();
      reject(err);
    };
    const handleClose = () => {
      cleanup();
      reject(new Error('连接已关闭,未收到数据'));
    };

    // 绑定一次性事件监听
    conn.once('data', handleData);
    conn.once('error', handleError);
    conn.once('close', handleClose);

    // 清理函数:移除所有事件监听
    function cleanup() {
      conn.off('data', handleData);
      conn.off('error', handleError);
      conn.off('close', handleClose);
    }
  });
}

// 使用async/await创建服务器
const server = net.createServer(async (conn) => {
  console.log('客户端已连接');
  try {
    // 等待并接收数据
    const data = await waitForData(conn);
    console.log('收到数据:', data.toString());
    
    // 可以继续等待下一次数据(如果需要)
    // const moreData = await waitForData(conn);
  } catch (err) {
    console.error('处理出错:', err.message);
  } finally {
    conn.end();
    console.log('连接已关闭');
  }
});

server.listen(3000, () => {
  console.log('TCP服务器监听在端口3000');
});

2. 封装流式数据的异步迭代器版本

TCP是流式传输,客户端可能会分多次发送数据。这种情况下,把连接对象转换成异步迭代器,就能用for await...of循环来优雅处理所有数据块:

const net = require('net');

// 将流转换为异步迭代器
async function* streamToAsyncIterator(stream) {
  const dataQueue = [];
  let resolveNext;
  let rejectNext;

  // 监听流事件,把数据推入队列
  stream.on('data', (chunk) => {
    dataQueue.push(chunk);
    resolveNext?.(); // 唤醒等待的迭代器
  });
  stream.on('error', (err) => {
    rejectNext?.(err);
  });
  stream.on('end', () => {
    resolveNext?.(); // 流结束时唤醒迭代器
  });

  // 迭代队列中的数据
  while (!stream.destroyed) {
    if (dataQueue.length > 0) {
      yield dataQueue.shift();
    } else {
      // 等待新数据或流结束
      await new Promise((resolve, reject) => {
        resolveNext = resolve;
        rejectNext = reject;
      });
    }
  }
}

// 使用异步迭代器处理流式数据
const server = net.createServer(async (conn) => {
  console.log('客户端已连接');
  try {
    // 遍历所有收到的数据块
    for await (const chunk of streamToAsyncIterator(conn)) {
      console.log('收到数据块:', chunk.toString());
      // 在这里处理每个数据块
    }
  } catch (err) {
    console.error('流处理出错:', err.message);
  } finally {
    console.log('客户端已断开连接');
  }
});

server.listen(3000, () => {
  console.log('TCP服务器监听在端口3000');
});

3. 第三方库选项(可选)

如果不想自己写封装代码,也有一些第三方库可以直接用,比如promise-netnet-promise,它们把原生net模块的API包装成了Promise风格。不过说实话,上面的封装代码已经足够灵活可控,大部分场景下自己实现反而更适配业务需求。

内容的提问来源于stack exchange,提问作者zlatanski

火山引擎 最新活动