Created
January 7, 2014 07:43
-
-
Save guileen/8295905 to your computer and use it in GitHub Desktop.
node.js packet parser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @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; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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