You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Node.js的net模块实现TCP服务器实时图片传输?

基于Node.js net模块实现TCP实时图片传输方案

要在TCP聊天系统中添加图片传输功能,核心是解决TCP字节流的粘包问题,并定义一套简单的通信协议来区分文本和图片数据。以下是具体实现方案和代码示例:

核心思路

TCP是无边界的字节流,无法直接区分不同数据包的边界,因此需要自定义通信协议:

  1. 每个数据包分为头部和内容两部分
  2. 头部用JSON格式标识数据类型(文本/图片)和内容字节长度
  3. 用固定分隔符(如||)分隔头部与内容,接收端通过解析头部来确定后续要读取的字节数

服务器端代码

const net = require('net');
const fs = require('fs');

const server = net.createServer();
const clients = new Set();

server.on('connection', (socket) => {
  console.log('新客户端已连接');
  clients.add(socket);

  // 维护接收缓冲区,处理粘包
  let receiveBuffer = Buffer.alloc(0);
  socket.on('data', (chunk) => {
    receiveBuffer = Buffer.concat([receiveBuffer, chunk]);

    // 循环解析完整数据包
    while (true) {
      const separatorIndex = receiveBuffer.indexOf('||');
      if (separatorIndex === -1) break;

      // 解析头部
      const headerStr = receiveBuffer.slice(0, separatorIndex).toString();
      let header;
      try {
        header = JSON.parse(headerStr);
      } catch (err) {
        console.error('头部解析失败,丢弃无效数据');
        receiveBuffer = receiveBuffer.slice(separatorIndex + 2);
        continue;
      }

      // 检查是否已接收完整内容
      if (receiveBuffer.length < separatorIndex + 2 + header.length) break;

      // 提取内容并更新缓冲区
      const content = receiveBuffer.slice(separatorIndex + 2, separatorIndex + 2 + header.length);
      receiveBuffer = receiveBuffer.slice(separatorIndex + 2 + header.length);

      // 根据数据类型处理
      if (header.type === 'text') {
        const message = content.toString();
        console.log(`收到文本消息: ${message}`);
        // 广播给其他客户端
        clients.forEach(client => client !== socket && client.write(`${headerStr}||${content}`));
      } else if (header.type === 'image') {
        console.log('收到图片数据');
        // 广播图片给其他客户端
        clients.forEach(client => client !== socket && client.write(`${headerStr}||${content}`));
        // 可选:服务器本地保存图片
        fs.writeFileSync(`server_received_${Date.now()}.png`, content);
      }
    }
  });

  socket.on('end', () => {
    console.log('客户端已断开连接');
    clients.delete(socket);
  });

  socket.on('error', (err) => {
    console.error('客户端连接出错:', err);
    clients.delete(socket);
  });
});

server.listen(3000, () => {
  console.log('TCP聊天服务器运行在端口3000');
});

客户端代码

const net = require('net');
const fs = require('fs');
const readline = require('readline');

const client = net.connect({ port: 3000 }, () => {
  console.log('已连接到服务器');
});

// 处理服务器推送的数据
let receiveBuffer = Buffer.alloc(0);
client.on('data', (chunk) => {
  receiveBuffer = Buffer.concat([receiveBuffer, chunk]);

  while (true) {
    const separatorIndex = receiveBuffer.indexOf('||');
    if (separatorIndex === -1) break;

    const headerStr = receiveBuffer.slice(0, separatorIndex).toString();
    let header;
    try {
      header = JSON.parse(headerStr);
    } catch (err) {
      console.error('头部解析失败');
      receiveBuffer = receiveBuffer.slice(separatorIndex + 2);
      continue;
    }

    if (receiveBuffer.length < separatorIndex + 2 + header.length) break;

    const content = receiveBuffer.slice(separatorIndex + 2, separatorIndex + 2 + header.length);
    receiveBuffer = receiveBuffer.slice(separatorIndex + 2 + header.length);

    if (header.type === 'text') {
      console.log(`收到消息: ${content.toString()}`);
    } else if (header.type === 'image') {
      const filename = `client_received_${Date.now()}.png`;
      fs.writeFileSync(filename, content);
      console.log(`图片已保存为 ${filename}`);
    }
  }
});

client.on('end', () => {
  console.log('与服务器断开连接');
});

// 命令行交互逻辑
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

rl.on('line', (input) => {
  if (input.startsWith('/sendimage ')) {
    // 发送图片命令格式:/sendimage 图片路径
    const filePath = input.slice(11).trim();
    try {
      const imageBuffer = fs.readFileSync(filePath);
      const header = JSON.stringify({
        type: 'image',
        length: imageBuffer.length
      });
      client.write(`${header}||${imageBuffer}`);
      console.log('图片发送成功');
    } catch (err) {
      console.error('读取图片失败:', err.message);
    }
  } else {
    // 发送文本消息
    const textBuffer = Buffer.from(input);
    const header = JSON.stringify({
      type: 'text',
      length: textBuffer.length
    });
    client.write(`${header}||${textBuffer}`);
  }
});

关键细节说明

  1. 粘包处理:通过维护接收缓冲区,循环解析完整数据包,确保不会将多个数据包混在一起处理
  2. 协议扩展性:如果需要支持更多数据类型(如文件、语音),只需在头部的type字段添加新标识即可
  3. 大文件优化:示例适合小图片传输,若需传输大文件,可将图片拆分为多个分块发送,接收端拼接后保存

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 12:12:55