Node.js TCP socket服务端存储GPS设备数据到数组复用及数据混淆排查
问题核心原因
- 全局/外层作用域变量污染:你定义的
car_id、car_no、car_username、index都是外层作用域的共享变量,TCP服务是并发处理多连接的,不同设备的上报请求会同时覆盖这些变量的值,直接导致不同设备的数据串用。 - 异步函数时序错误:
getCarDetails是异步IO操作,你在if (find_string === -1)分支里将index = length - 1写在回调函数外部,此时数据库查询还没返回,socketarray.push根本没有执行,拿到的length是旧数组的长度,index值完全错误,后续用这个索引修改数组自然会操作到其他设备的条目。 - 数据存储结构不合理:用数组存储+
findIndex遍历查找的方式效率低,且并发push时容易出现索引偏移,同时没有绑定socket实例与对应设备信息,连接断开后也没有清理旧数据,进一步加剧数据混乱。
修复方案
- 移除所有共享的全局变量,设备信息要么绑定到对应socket实例上,要么使用
Map结构以car_id为key存储,避免索引错乱。 - 所有需要使用数据库查询结果的逻辑,全部放到
getCarDetails的回调内部执行,保证异步逻辑执行完成后再操作对应数据。 - socket断开连接时,及时清理存储的对应设备数据,避免旧数据干扰。
修复后参考代码
const Net = require('net'); // 用Map存设备信息,key为car_id,查询效率更高 const carMap = new Map(); const server = new Net.Server(); const HOST = "IP"; const PORT = 8888; server.listen(PORT, HOST); server.on('connection', function(socket) { console.log('A new connection has been established.'); // 每个socket单独绑定自己的car信息,不共享 socket.carInfo = null; socket.on('data', async function(data) { const server_data = data.toString(); const car_details = server_data.split(',').filter(str => str != ""); const car_id = car_details[0]; let currentCarInfo = null; // 先查Map里有没有该设备信息 if (carMap.has(car_id)) { currentCarInfo = carMap.get(car_id); socket.carInfo = currentCarInfo; } else { // 新设备查数据库,所有后续逻辑放回调里 getCarDetails(car_id, function(dbRes) { if (!dbRes) return; currentCarInfo = { car_id: "" + car_id, car_no: dbRes.car_no, car_username: dbRes.username, socket: socket // 可选,绑定socket实例 }; carMap.set(car_id, currentCarInfo); socket.carInfo = currentCarInfo; // 这里写新设备查询完成后的后续业务逻辑 handleBusinessLogic(socket.carInfo, data); }); // 异步未返回前直接返回,不要执行后续逻辑 return; } // 已有设备信息的情况下执行业务逻辑 handleBusinessLogic(currentCarInfo, data); }); socket.on('end', function() { console.log('Closing connection with the client'); // 断开连接时清理数据 if (socket.carInfo?.car_id) { carMap.delete(socket.carInfo.car_id); } }); socket.on('error', function(err) { console.log(`Error: ${err}`); }); }); // 业务逻辑单独抽离,直接传当前设备信息,不用依赖全局变量 function handleBusinessLogic(carInfo, data) { // 这里直接用传入的carInfo操作,比如修改用户名: // carInfo.car_username = "bello"; // 写数据库也直接用carInfo里的id,不会串数据 } function getCarDetails(car_id,callback) { console.log("db called"); db.getConnection((err, connection) => { if(err){ console.log("error is" + err); return callback(false); } console.log('connected as id ' + connection.threadId); let selectQuery = 'SELECT * FROM ?? WHERE ?? = ?'; let query = mysql.format(selectQuery, ["car", "id", car_id]); connection.query(query, (err, data) => { connection.release(); if(err) { console.error(err); return callback(false); } if(data.length > 0){ return callback(data[0]); } else{ return callback(false); } }); }); }
内容的提问来源于stack exchange,提问作者mynameisbutt
相关产品推荐
相关产品推荐

