2015-06-26 2 views
1

Я использую сетевой сервер узла js и использую функцию socket.on ('data') для приема данных. Для анализа сообщений TCP я использую метод буфера анализа. Это использует первые 4 байта как длину TCP-сообщения, чтобы я мог читать из потока TCP и формировать отдельные команды. В целом, что происходит при высокой нагрузке, это то, что есть некоторые данные мусора, возвращаемые как часть потока TCP, что вызывает проблемы.Узел js TCP-сервер, socket.on ('data') - буфер данных содержит данные мусора при высокой нагрузке

function onConnect(client) { 
var accumulatingBuffer = new Buffer(0); 
var totalPacketLen = -1; 
var accumulatingLen = 0; 
var recvedThisTimeLen = 0; 

client.on('data', function (data) { 
    parseBuffer(client, data, accumulatingBuffer, totalPacketLen, accumulatingLen, recvedThisTimeLen); 
}); 
} 

и здесь метод parsebuffer.

function parseBuffer(client, data, accumulatingBuffer, totalPacketLen, accumulatingLen, recvedThisTimeLen) { 
recvedThisTimeLen = Buffer.byteLength(data); 
var tmpBuffer = new Buffer(accumulatingLen + recvedThisTimeLen); 
accumulatingBuffer.copy(tmpBuffer); 
data.copy(tmpBuffer, accumulatingLen); // offset for accumulating 
accumulatingBuffer = tmpBuffer; 
tmpBuffer = null; 
accumulatingLen = accumulatingLen + recvedThisTimeLen; 

if (accumulatingLen < PACKETHEADERLEN) { 
    return; 
} else if (accumulatingLen === PACKETHEADERLEN) { 
    packetHeaderLen 
    return; 
} else { 
    //a packet info is available.. 
    if (totalPacketLen < 0) { 
     totalPacketLen = accumulatingBuffer.readUInt32BE(0); 
    } 
} 
while (accumulatingLen >= totalPacketLen + PACKETHEADERLEN) { 
    var aPacketBufExceptHeader = new Buffer(totalPacketLen); // a whole packet is available... 
    accumulatingBuffer.copy(aPacketBufExceptHeader, 0, PACKETHEADERLEN, PACKETHEADERLEN + totalPacketLen); 

    //////////////////////////////////////////////////////////////////// 
    //process packet data 
    var stringData = aPacketBufExceptHeader.toString(); 
    try { 
     var JSONObject = JSON.parse(stringData); 
     handler(client, JSONObject); 


     var newBufRebuild = new Buffer(accumulatingBuffer.length - (totalPacketLen + PACKETHEADERLEN)); // we can reduce size of allocatin 
     accumulatingBuffer.copy(newBufRebuild, 0, totalPacketLen + PACKETHEADERLEN, accumulatingBuffer.length); 

     //init  
     accumulatingLen = accumulatingLen - (totalPacketLen + PACKETHEADERLEN); //totalPacketLen+4 
     accumulatingBuffer = newBufRebuild; 
     newBufRebuild = null; 
     totalPacketLen = -1; 

     //For a case in which multiple packets are transmitted at once. 
     if (accumulatingLen <= PACKETHEADERLEN) { 
      //need to get more data -> wait.. 
      return; 
     } else { 
      totalPacketLen = accumulatingBuffer.readUInt32BE(0); 
     } 
    } catch (ex) { 
     console.log(ex + ' unable to process data'); 
     return; 
    } 
} 
} 

Все хорошо, пока не будет высокой симулированной нагрузки, используя кучу клиентов, быстро отправляющих сообщения. В этот момент времени внутри метода ParseBuffer первая строка «data.length» возвращает больше, чем длина данных TCP. Это приводит к сбою чтения кода как UInt32BE, которые вызывают очень высокое значение в totalpacketlength (что говорит о следующей длине пакетов). Это приводит к потере сообщений. Я что-то упускаю. Пожалуйста помоги.

ответ

2

Когда вы делаете это в parseBuffer() функции:

accumulatingBuffer = tmpBuffer; 

это просто назначение tmpBuffer в качестве аргумента функции по имени accumulatingBuffer. Он НЕ меняет переменную accumulatingBuffer в вашем методе onConnect(). Таким образом, когда вы получаете частичный буфер, вы теряете накопленную часть. Эта же проблема относится и к другим аргументам, которые вы передаете parseBuffer(). Присвоение им внутри parseBuffer() не меняет переменные с тем же именем в onConnect().

Есть, вероятно, более простые способы написать это, но самый простой способ сохранить свою структуру - не передавать отдельные переменные, а передавать один объект, который имеет эти переменные в качестве свойств объекта. Затем, когда вы назначаете свойства, вы можете получить эти новые значения в пределах onConnect().

Общая структура будет выглядеть следующим образом:

function onConnect(client) { 
    var args = {}; 
    args.accumulatingBuffer = new Buffer(0); 
    args.totalPacketLen = -1; 
    args.accumulatingLen = 0; 
    args.recvedThisTimeLen = 0; 

    client.on('data', function (data) { 
     parseBuffer(client, data, args); 
    }); 
} 

И затем внести соответствующие изменения в parseBuffer() для доступа аргументов как свойства args объекта. Поскольку объекты передаются указателем, когда вы назначаете свойства объекта args из parseBuffer, они будут видны в объекте args в методе onConnect.


FYI, я не следовал всей логике в другом месте функции, поэтому могут быть и другие ошибки. Этот код кажется довольно сложным с большим количеством буферных копий, для чего довольно распространенные задачи, которые он пытается сделать. Это также код, который, вероятно, был написан много раз раньше и, вероятно, даже существует в некоторых предварительно построенных библиотеках.

+0

Спасибо за помощь. Это был плохой надзор. –

+0

@VivekAnandY - мне просто пришло в голову, что вы можете сделать логику 'parseBuffer() логичной, а затем вы можете просто ссылаться на локальные переменные в' onConnect() 'непосредственно (поскольку они находятся в родительской области) , – jfriend00

+0

Это то, что я сделал сейчас, и он отлично работает. Большое спасибо за помощь. –

Смежные вопросы