Skip to content

Instantly share code, notes, and snippets.

@guileen
Created January 7, 2014 07:43
Show Gist options
  • Save guileen/8295905 to your computer and use it in GitHub Desktop.
Save guileen/8295905 to your computer and use it in GitHub Desktop.
node.js packet parser
/**
* @param {Socket} socket auto handle 'data' event, and emit 'packet' event.
* @param {Object} options options params
* options.headSize the size of header
* options.getBodySize function(headBuffer) return bodySize
*/
exports.autoParsePacket = function (socket, options) {
var headSize = options.headSize;
var getBodySize = options.getBodySize;
if(!headSize || headSize <= 0) {
throw new Error('headSize should be a positive integer');
}
if(!getBodySize) {
throw new Error('getBodySize function is required');
}
var STATE_HEAD = 0;
var STATE_BODY = 1;
var state = STATE_HEAD;
var headOffset = 0;
// init headBuffer, we can reuse it cause it is fixed size.
var headBuffer = new Buffer(headSize);
var packetSize = 0;
var packetOffset = 0;
var packetBuffer;
socket.on('data', function (data) {
// data reading offset
var offset = 0, end = data.length;
// not read to the end
while(offset < end) {
if(state == STATE_HEAD) {
offset = parseHead(data, offset);
} else if (state == STATE_BODY) {
offset = parseBody(data, offset);
}
}
});
// read headBuffer from data start from offset.
// if ready init packetBuffer by headBuffer.
function parseHead(data, offset) {
var headEnd = Math.min(data.length, offset + headSize - headOffset);
data.copy(headBuffer, headOffset, offset, headEnd);
headOffset += (headEnd - offset);
if(headOffset === headSize) {
packetSize = headSize + getBodySize(headBuffer);
packetBuffer = new Buffer(packetSize);
headBuffer.copy(packetBuffer);
packetOffset = headSize;
state = STATE_BODY;
}
return headEnd;
}
// read packetBuffer from data start from offset
// if ready read next packet head.
function parseBody(data, offset) {
var bodyEnd = Math.min(data.length, offset + packetSize - packetOffset);
data.copy(packetBuffer, packetOffset, offset, bodyEnd);
packetOffset += (bodyEnd - offset);
if(packetOffset == packetSize) {
socket.emit('packet', packetBuffer);
headOffset = 0;
packetOffset = 0;
packetSize = 0;
packetBuffer = null;
state = STATE_HEAD;
}
return bodyEnd;
}
}
describe('packetParser', function() {
var mockSocket = new EventEmitter();
packetUtils.autoParsePacket(mockSocket, {
headSize: 3,
getBodySize: function (headBuffer) {
return headBuffer[2];
}
});
var packets = [];
mockSocket.on('packet', function(packet) {
packets.push(packet);
});
function clearMessages() {
packets = [];
}
function emitBuffer(buff) {
mockSocket.emit('data', buff);
}
it('should parse single packet', function(){
emitBuffer(new Buffer([0xaa, 0xbb, 0x02, 0x11, 0x22]));
packets.length.should.eql(1);
clearMessages();
});
it('should parse multi packet', function(){
emitBuffer(new Buffer([0xaa, 0xbb, 0x02, 0x11, 0x22,
0xaa, 0xbb, 0x03, 0x11, 0x22, 0x33]));
packets.length.should.eql(2);
clearMessages();
})
it('should parse multi packet in seperate events', function(){
emitBuffer(new Buffer([0xaa])); // broken head
emitBuffer(new Buffer([0xbb, 0x02, 0x11])); // broken body
emitBuffer(new Buffer([0x22, 0xaa, 0xbb, 0x03])); // full head
emitBuffer(new Buffer([0x11, 0x22, 0x33, 0xaa, 0xbb, 0x02, 0x11, 0x22])); // full packet
packets.length.should.eql(3);
clearMessages();
})
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment