├── ExBuffer.js ├── README.md ├── package.json ├── test.js └── testByteBuffer.js /ExBuffer.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * ExBuffer 3 | * yoyo 2012 https://github.com/play175/ExBuffer 4 | * new BSD Licensed 5 | */ 6 | 7 | /* 8 | * 构造方法 9 | * @param bufferLength 缓存区长度,默认512 byte 10 | */ 11 | var ExBuffer = function (bufferLength) { 12 | var self = this; 13 | var _headLen = 2; 14 | var _endian = 'B'; 15 | var _buffer = new Buffer(bufferLength || 512);//Buffer大于8kb 会使用slowBuffer,效率低 16 | var _readOffset = 0; 17 | var _putOffset = 0; 18 | var _dlen = 0; 19 | var slice = Array.prototype.slice; 20 | var _readMethod = 'readUInt16BE'; 21 | 22 | /* 23 | * 指定包长是uint32型(默认是ushort型) 24 | */ 25 | this.uint32Head = function(){ 26 | _headLen = 4; 27 | _readMethod = 'readUInt' + (8*_headLen) + ''+ _endian +'E'; 28 | return this; 29 | }; 30 | 31 | /* 32 | * 指定包长是ushort型(默认是ushort型) 33 | */ 34 | this.ushortHead = function(){ 35 | _headLen = 2; 36 | _readMethod = 'readUInt' + (8*_headLen) + ''+ _endian +'E'; 37 | return this; 38 | }; 39 | 40 | /* 41 | * 指定字节序 为Little Endian (默认:Big Endian) 42 | */ 43 | this.littleEndian = function(){ 44 | _endian = 'L'; 45 | _readMethod = 'readUInt' + (8*_headLen) + ''+ _endian +'E'; 46 | return this; 47 | }; 48 | 49 | /* 50 | * 指定字节序 为Big Endian (默认:Big Endian) 51 | */ 52 | this.bigEndian = function(){ 53 | _endian = 'B'; 54 | _readMethod = 'readUInt' + (8*_headLen) + ''+ _endian +'E'; 55 | return this; 56 | }; 57 | 58 | this.once = function(e,cb){ 59 | if(!this.listeners_once)this.listeners_once = {}; 60 | this.listeners_once[e] = this.listeners_once[e] || []; 61 | if(this.listeners_once[e].indexOf(cb) == -1)this.listeners_once[e].push(cb); 62 | }; 63 | 64 | this.on = function(e,cb){ 65 | if(!this.listeners)this.listeners = {}; 66 | this.listeners[e] = this.listeners[e] || []; 67 | if(this.listeners[e].indexOf(cb) == -1)this.listeners[e].push(cb); 68 | }; 69 | 70 | this.off = function(e,cb){ 71 | var index = -1; 72 | if(this.listeners && this.listeners[e] && (index = this.listeners[e].indexOf(cb)) != -1) 73 | this.listeners[e].splice(index); 74 | }; 75 | 76 | this.emit = function(e){ 77 | var other_parameters = slice.call(arguments, 1); 78 | if(this.listeners) { 79 | var list = this.listeners[e]; 80 | if(list) { 81 | for(var i=0;i _buffer.length - 1){ 115 | var ex = Math.ceil((len + getLen())/(1024));//每次扩展1kb 116 | var tmp = new Buffer(ex * 1024); 117 | var exlen = tmp.length - _buffer.length; 118 | _buffer.copy(tmp); 119 | //fix bug : superzheng 120 | if (_putOffset < _readOffset) { 121 | if (_putOffset <= exlen) { 122 | tmp.copy(tmp, _buffer.length, 0, _putOffset); 123 | _putOffset += _buffer.length; 124 | } else { 125 | //fix bug : superzheng 126 | tmp.copy(tmp, _buffer.length, 0, exlen); 127 | tmp.copy(tmp, 0, exlen, _putOffset); 128 | _putOffset -= exlen; 129 | } 130 | } 131 | _buffer = tmp; 132 | } 133 | if(getLen() == 0){ 134 | _putOffset = _readOffset = 0; 135 | } 136 | //判断是否会冲破_buffer尾部 137 | if((_putOffset + len) > _buffer.length){ 138 | //分两次存 一部分存在数据后面 一部分存在数据前面 139 | var len1 = _buffer.length - _putOffset; 140 | if (len1 > 0) { 141 | buffer.copy(_buffer,_putOffset,offset,offset + len1); 142 | offset += len1; 143 | } 144 | 145 | var len2 = len - len1; 146 | buffer.copy(_buffer,0,offset,offset + len2); 147 | _putOffset = len2; 148 | }else{ 149 | buffer.copy(_buffer,_putOffset,offset,offset + len); 150 | _putOffset += len; 151 | } 152 | 153 | var count = 0; 154 | while(true){ 155 | //console.log('_readOffset:'+_readOffset); 156 | //console.log('_putOffset:'+_putOffset); 157 | //console.log(_buffer); 158 | count++; 159 | if(count>1000)break;//1000次还没读完?? 160 | if(_dlen == 0){ 161 | if(getLen() < _headLen){ 162 | break;//连包头都读不了 163 | } 164 | if(_buffer.length - _readOffset >= _headLen){ 165 | _dlen = _buffer[_readMethod](_readOffset); 166 | _readOffset += _headLen; 167 | }else {// 168 | var hbuf = new Buffer(_headLen); 169 | var rlen = 0; 170 | for(var i = 0;i<(_buffer.length - _readOffset);i++){ 171 | hbuf[i] = _buffer[_readOffset++]; 172 | rlen++; 173 | } 174 | _readOffset = 0; 175 | for(var i = 0;i<(_headLen - rlen);i++){ 176 | hbuf[rlen+i] = _buffer[_readOffset++]; 177 | } 178 | _dlen = hbuf[_readMethod](0); 179 | } 180 | } 181 | 182 | //console.log('_dlen:'+_dlen + ',unreadLen:'+getLen()); 183 | 184 | if(getLen() >= _dlen){ 185 | var dbuff = new Buffer(_dlen); 186 | if(_readOffset + _dlen > _buffer.length){ 187 | var len1 = _buffer.length - _readOffset; 188 | if (len1 > 0) { 189 | _buffer.copy(dbuff,0,_readOffset,_readOffset + len1); 190 | } 191 | 192 | _readOffset = 0; 193 | var len2 = _dlen - len1; 194 | _buffer.copy(dbuff,len1,_readOffset,_readOffset += len2); 195 | }else { 196 | _buffer.copy(dbuff,0,_readOffset,_readOffset += _dlen); 197 | } 198 | try { 199 | _dlen = 0; 200 | self.emit("data", dbuff); 201 | if (_readOffset === _putOffset) { 202 | break; 203 | } 204 | } catch(e) { 205 | self.emit("error", e); 206 | } 207 | }else { 208 | break; 209 | } 210 | } 211 | 212 | }; 213 | 214 | 215 | //获取现在的数据长度 216 | function getLen() { 217 | if(_putOffset>= _readOffset){ // ------******------- 218 | return _putOffset - _readOffset; 219 | } 220 | return _buffer.length - _readOffset + _putOffset; //***-------********* 221 | } 222 | }; 223 | 224 | module.exports = exports = ExBuffer; 225 | 226 | 227 | /**************************************************************** 228 | //构造一个ExBuffer,采用4个字节(uint32无符号整型)表示包长,而且是little endian 字节序 229 | var exBuffer = new ExBuffer().uint32Head().littleEndian(); 230 | //或者构造一个ExBuffer,采用2个字节(ushort型)表示包长,而且是big endian 字节序 (默认) 231 | var exBuffer = new ExBuffer().ushortHead().bigEndian(); 232 | 233 | //只要收到满足的包就会触发事件 234 | exBuffer.on('data',function(buffer){ 235 | console.log('>> receive data,length:'+buffer.length); 236 | console.log(buffer); 237 | }); 238 | 239 | //传入一个9字节长的数据,分多次put (对应于TCP中的分包的情况) 240 | exBuffer.put(new Buffer([0,9])); 241 | exBuffer.put(new Buffer([1,2,3,4,5,6,7])); 242 | exBuffer.put(new Buffer([8,9])); 243 | 244 | //传入一个3个字节的数据和一个6个字节的数据,一次put(对应于TCP中的粘包的情况) 245 | exBuffer.put(new Buffer([0,3,1,2,3,0,6,1,2,3,4,5,6])); 246 | 247 | ****************************************************************/ 248 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ExBuffer,NodeJs的TCP中的粘包、分包问题的解决方案! 2 | 3 | 推荐结合ByteBuffer来做通信协议!https://github.com/play175/ByteBuffer 4 | 5 | C版本的ExBuffer:https://github.com/play175/exbuffer.c 6 | 7 | ```javascript 8 | var ExBuffer = require('./ExBuffer'); 9 | 10 | /*************************基本操作****************************/ 11 | 12 | //构造一个ExBuffer,采用4个字节(uint32无符号整型)表示包长,而且是little endian 字节序 13 | var exBuffer = new ExBuffer().uint32Head().littleEndian(); 14 | //或者构造一个ExBuffer,采用2个字节(ushort型)表示包长,而且是big endian 字节序 (默认) 15 | var exBuffer = new ExBuffer().ushortHead().bigEndian(); 16 | 17 | //只要收到满足的包就会触发事件 18 | exBuffer.on('data',function(buffer){ 19 | console.log('>> receive data,length:'+buffer.length); 20 | //console.log(buffer); 21 | }); 22 | 23 | 24 | //传入一个9字节长的数据,分多次put (对应于TCP中的分包的情况) 25 | exBuffer.put(new Buffer([0,9])); 26 | exBuffer.put(new Buffer([1,2,3,4,5,6,7])); 27 | exBuffer.put(new Buffer([8,9])); 28 | 29 | //传入一个3个字节的数据和一个6个字节的数据,一次put(对应于TCP中的粘包的情况) 30 | exBuffer.put(new Buffer([0,3,1,2,3,0,6,1,2,3,4,5,6])); 31 | 32 | 33 | //大数据处理测试 (20MB) 34 | var exBuffer = new ExBuffer().uint32Head().bigEndian(); 35 | exBuffer.on('data',function(buffer){ 36 | console.log('>> receive data,length:'+buffer.length); 37 | console.log(buffer); 38 | }); 39 | var sbuf = new Buffer(4); 40 | sbuf.writeUInt32BE(1024*1024*20,0);//写入包长 41 | exBuffer.put(sbuf); 42 | exBuffer.put(new Buffer(1024*1024*20)); 43 | 44 | 45 | /*************************在socket中的应用****************************/ 46 | 47 | console.log('-----------------------use in socket------------------------'); 48 | 49 | var net = require('net'); 50 | 51 | //测试服务端 52 | var server = net.createServer(function(socket) { 53 | console.log('client connected'); 54 | new Connection(socket);//有客户端连入时 55 | }); 56 | server.listen(8124); 57 | 58 | //服务端中映射客户端的类 59 | function Connection(socket) { 60 | var exBuffer = new ExBuffer(); 61 | exBuffer.on('data',onReceivePackData); 62 | 63 | socket.on('data', function(data) { 64 | exBuffer.put(data);//只要收到数据就往ExBuffer里面put 65 | }); 66 | 67 | //当服务端收到完整的包时 68 | function onReceivePackData(buffer){ 69 | console.log('>> server receive data,length:'+buffer.length); 70 | console.log(buffer.toString()); 71 | 72 | var data = 'wellcom, I am server'; 73 | var len = Buffer.byteLength(data); 74 | 75 | //写入2个字节表示本次包长 76 | var headBuf = new Buffer(2); 77 | headBuf.writeUInt16BE(len, 0) 78 | socket.write(headBuf); 79 | 80 | var bodyBuf = new Buffer(len); 81 | bodyBuf.write(data); 82 | socket.write(bodyBuf); 83 | } 84 | } 85 | 86 | //测试客户端 87 | var exBuffer = new ExBuffer(); 88 | var client = net.connect(8124, function() { 89 | 90 | var data = 'hello I am client'; 91 | var len = Buffer.byteLength(data); 92 | 93 | //写入2个字节表示本次包长 94 | var headBuf = new Buffer(2); 95 | headBuf.writeUInt16BE(len, 0) 96 | client.write(headBuf); 97 | 98 | var bodyBuf = new Buffer(len); 99 | bodyBuf.write(data); 100 | client.write(bodyBuf); 101 | 102 | }); 103 | 104 | client.on('data', function(data) { 105 | exBuffer.put(data);//只要收到数据就往ExBuffer里面put 106 | }); 107 | 108 | //当客户端收到完整的数据包时 109 | exBuffer.on('data', function(buffer) { 110 | console.log('>> client receive data,length:'+buffer.length); 111 | console.log(buffer.toString()); 112 | }); 113 | 114 | ``` 115 | 116 | 安装 117 | 118 | ```javascript 119 | npm install ExBuffer 120 | ``` 121 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "ExBuffer", 3 | "description" : "usefull for socket tcp receive data", 4 | "homepage" : "http://play175.github.com/ExBuffer/", 5 | "keywords" : ["Buffer","byte array","tcp","socket"], 6 | "author" : "yoyo ", 7 | "repository" : {"type": "git", "url": "https://github.com/play175/ExBuffer.git"}, 8 | "main" : "ExBuffer.js", 9 | "version" : "0.4.0" 10 | } 11 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var ExBuffer = require('./ExBuffer'); 2 | 3 | /*************************基本操作****************************/ 4 | 5 | //构造一个ExBuffer,采用4个字节(uint32无符号整型)表示包长,而且是little endian 字节序 6 | var exBuffer = new ExBuffer().uint32Head().littleEndian(); 7 | //或者构造一个ExBuffer,采用2个字节(ushort型)表示包长,而且是big endian 字节序 (默认) 8 | var exBuffer = new ExBuffer().ushortHead().bigEndian(); 9 | 10 | //只要收到满足的包就会触发事件 11 | exBuffer.on('data',function(buffer){ 12 | console.log('>> receive data,length:'+buffer.length); 13 | //console.log(buffer); 14 | }); 15 | 16 | 17 | //传入一个9字节长的数据,分多次put (对应于TCP中的分包的情况) 18 | exBuffer.put(new Buffer([0,9])); 19 | exBuffer.put(new Buffer([1,2,3,4,5,6,7])); 20 | exBuffer.put(new Buffer([8,9])); 21 | 22 | //传入一个3个字节的数据和一个6个字节的数据,一次put(对应于TCP中的粘包的情况) 23 | exBuffer.put(new Buffer([0,3,1,2,3,0,6,1,2,3,4,5,6])); 24 | 25 | 26 | //大数据处理测试 (20MB) 27 | var exBuffer = new ExBuffer().uint32Head().bigEndian(); 28 | exBuffer.on('data',function(buffer){ 29 | console.log('>> receive data,length:'+buffer.length); 30 | console.log(buffer); 31 | }); 32 | var sbuf = new Buffer(4); 33 | sbuf.writeUInt32BE(1024*1024*20,0);//写入包长 34 | exBuffer.put(sbuf); 35 | exBuffer.put(new Buffer(1024*1024*20)); 36 | 37 | 38 | /*************************在socket中的应用****************************/ 39 | 40 | console.log('-----------------------use in socket------------------------'); 41 | 42 | var net = require('net'); 43 | 44 | //测试服务端 45 | var server = net.createServer(function(socket) { 46 | console.log('client connected'); 47 | new Connection(socket);//有客户端连入时 48 | }); 49 | server.listen(8124); 50 | 51 | //服务端中映射客户端的类 52 | function Connection(socket) { 53 | var exBuffer = new ExBuffer(); 54 | exBuffer.on('data',onReceivePackData); 55 | 56 | socket.on('data', function(data) { 57 | exBuffer.put(data);//只要收到数据就往ExBuffer里面put 58 | }); 59 | 60 | //当服务端收到完整的包时 61 | function onReceivePackData(buffer){ 62 | console.log('>> server receive data,length:'+buffer.length); 63 | console.log(buffer.toString()); 64 | 65 | var data = 'wellcom, I am server'; 66 | var len = Buffer.byteLength(data); 67 | 68 | //写入2个字节表示本次包长 69 | var headBuf = new Buffer(2); 70 | headBuf.writeUInt16BE(len, 0) 71 | socket.write(headBuf); 72 | 73 | var bodyBuf = new Buffer(len); 74 | bodyBuf.write(data); 75 | socket.write(bodyBuf); 76 | } 77 | } 78 | 79 | //测试客户端 80 | var exBuffer = new ExBuffer(); 81 | var client = net.connect(8124, function() { 82 | 83 | var data = 'hello I am client'; 84 | var len = Buffer.byteLength(data); 85 | 86 | //写入2个字节表示本次包长 87 | var headBuf = new Buffer(2); 88 | headBuf.writeUInt16BE(len, 0) 89 | client.write(headBuf); 90 | 91 | var bodyBuf = new Buffer(len); 92 | bodyBuf.write(data); 93 | client.write(bodyBuf); 94 | 95 | }); 96 | 97 | client.on('data', function(data) { 98 | exBuffer.put(data);//只要收到数据就往ExBuffer里面put 99 | }); 100 | 101 | //当客户端收到完整的数据包时 102 | exBuffer.on('data', function(buffer) { 103 | console.log('>> client receive data,length:'+buffer.length); 104 | console.log(buffer.toString()); 105 | }); 106 | 107 | -------------------------------------------------------------------------------- /testByteBuffer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ExBuffer ByteBuffer结合的测试 3 | **/ 4 | var ExBuffer = require('ExBuffer'); 5 | var ByteBuffer = require('ByteBuffer'); 6 | var net = require('net'); 7 | 8 | //server 9 | var server = net.createServer(function(socket) { 10 | console.log('>> server:client connected'); 11 | new Connection(socket);//有客户端连入时 12 | }); 13 | 14 | server.listen(8124); 15 | console.log('>> server start listening:'); 16 | 17 | //connection class 18 | function Connection(socket) { 19 | var exBuffer = new ExBuffer(); 20 | exBuffer.on('data',onReceivePackData); 21 | 22 | socket.on('data', function(data) { 23 | console.log('>> server receive scoket data,length:'+data.length); 24 | exBuffer.put(data);//只要收到数据就往ExBuffer里面put 25 | }); 26 | 27 | //当服务端收到完整的包时 28 | function onReceivePackData(buffer){ 29 | console.log('>> server receive packet,length:'+buffer.length); 30 | //unpack the packet 31 | var bytebuf = new ByteBuffer(buffer); 32 | var resArr = bytebuf.uint32().string().unpack(); 33 | console.log('>> server unpack packet:['+resArr[0]+','+resArr[1]+']'); 34 | 35 | //send a packet 36 | var sbuf = new ByteBuffer(); 37 | var buf = sbuf.uint32(123).string('welcome,client:' + resArr[0]).pack(true); 38 | socket.write(buf); 39 | } 40 | } 41 | 42 | //client 43 | var exBuffer = new ExBuffer(); 44 | var client = net.connect(8124, function() { 45 | console.log('>> client:connect server success!'); 46 | //send a packet 47 | var sbuf = new ByteBuffer(); 48 | var buf = sbuf.uint32(123).string('hello,I am client').pack(true); 49 | client.write(buf); 50 | console.log('>> client sent packet,length:'+buf.length); 51 | }); 52 | 53 | client.on('data', function(data) { 54 | console.log('>> client receive socket data,length:'+data.length); 55 | exBuffer.put(data);//只要收到数据就往ExBuffer里面put 56 | }); 57 | 58 | //当客户端收到完整的数据包时 59 | exBuffer.on('data', function(buffer) { 60 | console.log('>> client receive packet,length:'+buffer.length); 61 | //unpack the packet 62 | var bytebuf = new ByteBuffer(buffer); 63 | var resArr = bytebuf.uint32().string().unpack(); 64 | console.log('>> client receive packet:['+resArr[0]+','+resArr[1]+']'); 65 | //delay to exit 66 | console.log('exit...'); 67 | setTimeout(function(){process.exit(0);},2000); 68 | }); 69 | 70 | --------------------------------------------------------------------------------