You need to enable JavaScript to run this app. Node.js TCP Socket如何将接收的数据包推送至网页端展示
优惠活动
大模型
产品
解决方案
定价
更多

Node.js TCP Socket如何将接收的数据包推送至网页端展示

实现TCP数据包结构化解析 + 网页端实时推送方案

核心实现思路

整体基于单Node进程完成三件事,不拆分独立服务:

  • 保留原有9898端口TCP监听能力,优化数据包解析逻辑,解决TCP流传输的粘包/拆包问题
  • 基于Node内置http模块启动8080端口Web服务,返回前端展示页面
  • 实现轻量WebSocket通道,将解析完成的结构化数据包主动推送给所有在线的网页端,不需要前端轮询

1. 优化数据包解析逻辑

原有ExtractMessage函数仅简单split后打印数组长度,针对给定的典型数据包格式,需要做两点修正:

  • 为每个TCP连接维护独立接收缓冲区,直到匹配到完整的包格式(以$$开头、*结尾的完整段)再触发解析,避免流拆分导致的字段错位
  • 将拆分后的数组字段映射为可读的结构化对象,替代无意义的数组长度打印

修正后的解析逻辑参考:

// 字段映射规则,和数据包顺序一一对应
const FIELD_MAP = [
  'packetType',
  'deviceModel',
  'deviceImei',
  'firmwareVersion',
  'protocolVersion',
  'eventDate',
  'eventTime',
  'latitudeWithDirection',
  'longitudeValue',
  'longitudeDirection',
  'signalStrength',
  'emptyTail'
]

function parseCompletePacket(rawPacketStr) {
  // 去掉首尾标记,按分隔符拆分
  const cleanContent = rawPacketStr.replace(/^\$\$/, '').replace(/\*$/, '')
  const fields = cleanContent.split(',,')
  // 生成结构化对象
  const parsedResult = {}
  FIELD_MAP.forEach((key, index) => {
    parsedResult[key] = fields[index] || 'N/A'
  })
  return parsedResult
}

2. 打通网页端实时推送通道

不需要引入外部第三方依赖,直接在进程内实现轻量WebSocket交互能力:

  • 所有网页端打开8080端口页面时,自动和当前Node进程建立WebSocket连接,将连接实例存入全局在线客户端列表
  • 当TCP服务完成一个数据包的解析后,遍历所有在线客户端列表,将结构化的解析结果序列化后逐一分发

3. 整合后的完整运行代码

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

// 全局存储在线网页端的WebSocket连接实例
const webClients = new Set();

// 字段映射规则
const FIELD_MAP = [
  'packetType',
  'deviceModel',
  'deviceImei',
  'firmwareVersion',
  'protocolVersion',
  'eventDate',
  'eventTime',
  'latitudeWithDirection',
  'longitudeValue',
  'longitudeDirection',
  'signalStrength',
  'emptyTail'
]

// 解析完整数据包
function parseCompletePacket(rawPacketStr) {
  const cleanContent = rawPacketStr.replace(/^\$\$/, '').replace(/\*$/, '')
  const fields = cleanContent.split(',,')
  const parsedResult = {}
  FIELD_MAP.forEach((key, index) => {
    parsedResult[key] = fields[index] || 'N/A'
  })
  return parsedResult
}

// 原有日志写入逻辑
function WriteDataToLog(data) {
  fs.appendFile('Log.txt', '\n This is my message : ' + data.toString(), function(err) {
    if (err) throw err;
    console.log('Logged!');
  });
}

// TCP服务创建
const server = net.createServer((socket) => {
  // 为每个TCP连接初始化接收缓冲区,解决粘包问题
  socket.receiveBuffer = Buffer.alloc(0);
  console.log('Connected.');

  socket.on('data', (data) => {
    // 把新收到的数据拼到缓冲区
    socket.receiveBuffer = Buffer.concat([socket.receiveBuffer, data]);
    const bufferStr = socket.receiveBuffer.toString();
    
    // 循环从缓冲区提取完整的数据包
    let startIndex = bufferStr.indexOf('$$');
    let endIndex = bufferStr.indexOf('*', startIndex);
    while (startIndex !== -1 && endIndex !== -1) {
      // 切出完整数据包
      const completePacket = bufferStr.slice(startIndex, endIndex + 1);
      // 解析数据包
      const parsedData = parseCompletePacket(completePacket);
      console.log('解析完成的数据包:', parsedData);
      WriteDataToLog(completePacket);

      // 把解析后的数据推给所有网页端
      webClients.forEach(client => {
        // 构造WebSocket文本帧发送数据
        const payload = JSON.stringify(parsedData);
        const frame = Buffer.from([0x81, 0x80 | payload.length, ...crypto.randomBytes(4), ...Buffer.from(payload)]);
        client.write(frame);
      })

      // 把已经处理完的内容从缓冲区移除
      const remainStr = bufferStr.slice(endIndex + 1);
      socket.receiveBuffer = Buffer.from(remainStr);
      // 重新查找下一个包的位置
      startIndex = remainStr.indexOf('$$');
      endIndex = remainStr.indexOf('*', startIndex);
    }
  });

  socket.write('SERVER: Hello! This is server speaking.\n');
}).on('error', (err) => {
  console.error(err);
});

server.listen(9898, () => {
  console.log('opened TCP server on', server.address().port);
});

// 启动Web服务,提供前端页面
const webServer = http.createServer((req, res) => {
  // 返回前端展示页面
  res.writeHead(200, {'Content-Type': 'text/html; charset=utf-8'});
  res.end(`
    <!DOCTYPE html>
    <html>
    <head>
      <title>TCP数据包实时监控</title>
      <style>table {border-collapse: collapse; width: 100%; margin-top: 20px;} td, th {border: 1px solid #ddd; padding: 8px;}</style>
    </head>
    <body>
      <h3>TCP接收数据包实时列表</h3>
      <table>
        <thead><tr><th>接收时间</th><th>设备型号</th><th>IMEI</th><th>纬度</th><th>经度</th><th>信号强度</th></tr></thead>
        <tbody id="dataList"></tbody>
      </table>
      <script>
        const ws = new WebSocket('ws://' + location.host);
        ws.onmessage = (e) => {
          const data = JSON.parse(e.data);
          const tr = document.createElement('tr');
          tr.innerHTML = \`
            <td>\${new Date().toLocaleString()}</td>
            <td>\${data.deviceModel}</td>
            <td>\${data.deviceImei}</td>
            <td>\${data.latitudeWithDirection}</td>
            <td>\${data.longitudeValue + ' ' + data.longitudeDirection}</td>
            <td>\${data.signalStrength}</td>
          \`
          document.getElementById('dataList').prepend(tr);
        }
      </script>
    </body>
    </html>
  `);
});

// 处理WebSocket升级请求
webServer.on('upgrade', (req, socket) => {
  // 完成WebSocket握手
  const key = req.headers['sec-websocket-key'];
  const acceptKey = crypto.createHash('sha1').update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11').digest('base64');
  socket.write('HTTP/1.1 101 Switching Protocols\r\n');
  socket.write('Upgrade: websocket\r\n');
  socket.write('Connection: Upgrade\r\n');
  socket.write('Sec-WebSocket-Accept: ' + acceptKey + '\r\n\r\n');
  
  // 把连接存入全局客户端列表
  webClients.add(socket);
  // 连接断开时从列表移除
  socket.on('close', () => {
    webClients.delete(socket);
  })
})

webServer.listen(8080, () => {
  console.log('Web服务启动,访问 http://localhost:8080 查看数据');
})

运行效果

  • TCP设备向9898端口发送符合格式的数据包时,服务端自动处理粘包问题,将原始包写入Log.txt文件
  • 浏览器打开8080端口页面,所有解析完成的数据包会自动追加到页面表格中,不需要手动刷新
  • 多客户端同时访问页面时,所有客户端都会同步收到最新的数据包推送

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.22 16:15:04