let _dataHeadLen = 4; timer && clearInterval(timer); timer = setInterval(()=>{ // 缓冲区数据不够解析出包头 if (_dataLen < _dataHeadLen) { console.log('数据长度小于包头规定长度,等待数据......') clearInterval(timer); } // 解析包头长度 // 尾部最后剩余可读字节长度 let restDataLen = _bufferLength - _readPointer; let dataLen = 0; let headBuffer = Buffer.alloc(_dataHeadLen); // 数据包为分段存储,不能直接解析出包头,先拼接 if (restDataLen < _dataHeadLen) { // 取出第一部分头部字节 _buffer.copy(headBuffer, 0, _readPointer, _bufferLength) // 取出第二部分头部字节 let unReadHeadLen = _dataHeadLen - restDataLen; _buffer.copy(headBuffer, restDataLen, 0, unReadHeadLen) dataLen = headBuffer.readUInt32BE(0); } else { _buffer.copy(headBuffer, 0, _readPointer, _readPointer + _dataHeadLen); dataLen = headBuffer.readUInt32BE(0);; } // 数据长度不够读取,直接返回 if (_dataLen - _dataHeadLen < dataLen) { log.info("缓冲区已有body数据长度小于包头定义body的长度,等待数据......") clearInterval(timer); } else { // 数据够读,读取数据包 let package = Buffer.alloc(dataLen); // 数据是分段存储,需要分两次读取 if (_bufferLength - _readPointer < dataLen) { let firstPartLen = _bufferLength - _readPointer; // 读取第一部分,直接到字符尾部的数据 _buffer.copy(package, 0, _readPointer, firstPartLen + _readPointer); // 读取第二部分,存储在开头的数据 let secondPartLen = dataLen - firstPartLen; _buffer.copy(package, firstPartLen, 0, secondPartLen); _readPointer = secondPartLen; //更新可读起点 } else { // 直接读取数据 _buffer.copy(package, 0, _readPointer, _readPointer + dataLen); _readPointer += dataLen; //更新可读起点 } _dataLen -= readData.length; //更新数据长度 // 已经读取完所有数据 if (_readPointer === _writePointer) { clearInterval(timer) } //开始解包 callback(package); } }, 50);
4)拆包得到数据
let headBytes = 4; let head = new Buffer(headBytes); buffer.copy(head, 0, 0, headBytes); let dataLen = head.readUInt32BE(); const body = new Buffer(dataLen); buffer.copy(body, 0, headBytes, headBytes + dataLen) let content = null; try { const str = body.toString('utf-8'); if(str === ''){ content = null; }else{ content = JSON.parse(body); } } catch (e) { log.error('head指定body长度有问题') } //传递给业务层 callback(content);
5、总结
从上面我们已经了解到了封包解包的一个过程。TCP是可靠传输的,同一时间在网络上只会有一个数据包,并且丢包会重传,因此不用担心丢包或者数据包乱序问题。UDP有消息保护边界,不需要进行拆包解包,然后其是非可靠传输,也需要解决其他一些问题,譬如丢包和数据包排序问题。
上面进行数据包结构设计时只是简单地加了一个包体长度,事实上在业务场景可以自由增加需要的字段,譬如协议版本,协议类型等等。