Node.js通过TCP传输解析大JSON文件失败问题求助
TCP传输大JSON解析失败:解决流数据分段问题
看起来你遇到的核心问题是TCP是面向流的协议,而非面向消息——简单来说,当你发送大JSON时,数据会被拆分成多个数据包分段传输。你的代码现在在data事件触发时就直接尝试解析,但此时拿到的可能只是完整JSON的一部分,自然会抛出语法错误。本地环境因为传输距离极短,数据大概率一次性到达,所以不会触发这个问题,但线上服务器的网络传输必然会出现分段情况。
你的代码问题分析
看错误日志里的"Cha明显是"Channel_Name"的开头片段,这说明这次data事件只收到了JSON的一部分。你当前的代码每次触发data事件时,直接用pack = JSON.parse(d)解析当前片段,完全忽略了TCP流的特性,这是导致报错的直接原因。
解决方案:先拼接完整数据再解析
你需要先把所有收到的数据拼接起来,直到确认完整的JSON已经接收完毕,再进行解析。这里有两种实用的实现方式:
方案1:用换行符作为消息结束标记
在发送JSON时,在末尾追加一个换行符\n作为消息结束的约定标记。服务器端每次收到数据后先拼接,再检查是否包含结束标记,确认完整后再解析:
var express = require('express'); var net = require("net"); var app = express(); var server = net.createServer(); // 允许跨域 app.use(function(req, res, next){ res.header("Access-Control-Allow-Origin", "*"); next(); }); // 用参数化路由替代动态添加路由,避免路由冲突 app.get('/data/:formatNumber', function(req, res){ const storedData = req.app.get(`data_${req.params.formatNumber}`); if (storedData) { res.setHeader('Content-Type', 'application/json'); res.send(storedData); } else { res.status(404).send('Data not found'); } }); server.on("connection", function(socket){ let receivedData = ""; // 每个连接单独维护接收的数据 socket.setEncoding('utf8'); socket.on("data", function(d){ receivedData += d; // 追加数据,而非覆盖 // 检查是否收到结束标记(发送方需在JSON末尾加\n) if (receivedData.includes('\n')) { // 分割出所有完整消息,过滤空行 const completeMessages = receivedData.split('\n').filter(msg => msg.trim()); for (const msg of completeMessages) { try { const parsedData = JSON.parse(msg); console.log(`Received data for FormatNumber: ${parsedData.FormatNumber}`); // 用唯一键存储数据,避免冲突 app.set(`data_${parsedData.FormatNumber}`, msg); } catch (err) { console.error('JSON解析错误:', err); } } receivedData = ""; // 重置,准备接收下一条消息 } }); socket.once("close", function(){ console.log("connection closed"); }); }); server.on("error", function(err){ console.log("connection error:", err); }); server.listen(9000, function(){ console.log("TCP Server Listening to Port 9000"); }); app.listen(8081, function(){ console.log("Express Server Listening to Port 8081"); });
方案2:先发送数据长度,再发送JSON(更可靠的大文件方式)
这种方式适合超大文件传输,步骤如下:
- 发送方先计算JSON的字节长度,把长度转换成固定格式(比如8位数字字符串)发送;
- 服务器先接收长度,再根据长度接收对应字节数的JSON数据,直到全部接收完成再解析。
服务器端核心修改示例:
server.on("connection", function(socket){ let state = 'waiting-for-length'; // 当前状态:等待接收长度 let expectedLength = 0; let receivedData = ""; socket.on("data", function(d){ if (state === 'waiting-for-length') { // 假设长度是8位固定长度字符串,比如"00012345" receivedData += d; if (receivedData.length >= 8) { const lengthStr = receivedData.slice(0,8); expectedLength = parseInt(lengthStr, 10); receivedData = receivedData.slice(8); state = 'waiting-for-data'; } } if (state === 'waiting-for-data') { receivedData += d; // 检查是否已接收足够长度的数据 if (Buffer.byteLength(receivedData, 'utf8') >= expectedLength) { const completeJson = receivedData.slice(0, expectedLength); try { const parsedData = JSON.parse(completeJson); console.log(`Received data for FormatNumber: ${parsedData.FormatNumber}`); app.set(`data_${parsedData.FormatNumber}`, completeJson); } catch (err) { console.error('JSON解析错误:', err); } // 处理剩余的多余数据(如果存在多条消息) const remaining = receivedData.slice(expectedLength); receivedData = remaining; state = 'waiting-for-length'; } } }); });
额外注意事项
- 你原代码里尝试访问
pack.Timestamp.LocalTimestamp,但示例JSON中并没有Timestamp字段,这会导致额外报错,请检查数据结构; - 动态添加路由的方式会导致路由冲突,建议用参数化路由(如
/data/:formatNumber)替代; - 响应的
Content-Type应该设置为application/json,方便客户端直接解析。
内容的提问来源于stack exchange,提问作者Jason Chen
相关产品推荐
相关产品推荐

