【MinaFile】【十四】【2.0】多文件处理粘包之服务器接收端

来源:互联网 发布:iphone6s 4g网络用不了 编辑:程序博客网 时间:2024/04/29 23:00

1.处理方式

我的处理方式是设定一个池子,将每次接收的字节都放入一个池子中,如果达到一个文件所需要的字节,那么就进行保存。可能这个方式对于大文件来说,会很占内存,但是可以等到后面再进行优化。比如三个文件分别是5kb,3kb,14kb,如果接受的数据大于5Kb了,就先将5kb的文件保存起——把池子中的5kb的字节输出到文件中。如果小于5kb,那么继续接受,并放入池子中。实现代码如下。

还是类ByteProtocalDecoder

/** * 这个是服务器对客户端发来的消息进行解码 继承CumulativeProtocolDecoder 实现doDecode * 父类会将数据读取完之后,再调用实现的方法doDecode。 如果成功读取完之后,服务器会去Handle中进行业务处理。 * 对发来的文件进行业务处理,比如说保存之类的 动作。 1. 2016.02.14 实现多文件粘包 *  * @author king_fu *  */public class ByteProtocalDecoder extends CumulativeProtocolDecoder {private static final Logger LOGGER = LoggerFactory.getLogger(ByteProtocalDecoder.class);public static final int MAX_FILE_SIZE = 1024 * 1024 * 1024; // 1Gprivate boolean isFinish = false; // 是否已经处理所有数据(当前)private boolean isFirst = true; // 是否是第一次进来private ByteFileMessage bfm = new ByteFileMessage(); // 保存对象private int dataPool = 0;private static IoBuffer newIoBuffer = IoBuffer.allocate(0).setAutoExpand(true);private boolean readHead = false; // 是否读取头部信息private int needData = 0; // 一个文件中需要的字节数private int headData = 0; // 一个文件中需要的字节数public ByteProtocalDecoder() {LOGGER.info("创建解析器新对象");}@Overrideprotected boolean doDecode(IoSession session, IoBuffer in,ProtocolDecoderOutput out) throws Exception {LOGGER.info("服务器对客户端发来的消息进行解码。解码开始");LOGGER.info("limit:" + in.limit());LOGGER.info("remaining:" + in.remaining());try {// 这个方法的调用是判断IoBuffer里的数据是否满足一条消息了// dataLength = getInt(position()); 用绝对值的方式读取,position不会移动。this.readFile(in);if (isFinish) {// 解析完成out.write(bfm);dataInit();} else {return true;}} catch (Exception e) {LOGGER.info("服务器解码过程中发生错误", e);return false;}return true;}private void readFile(IoBuffer in) throws CharacterCodingException {CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder();LOGGER.info("【服务器解析】继续接受in:" + in.remaining());IoBuffer bufTmp = null;byte[] byteValue = null;if (isFirst) {bufTmp = IoBuffer.allocate(newIoBuffer.remaining() + in.remaining()).setAutoExpand(true);bufTmp.order(newIoBuffer.order());if (!readHead) {// 未读取了头部信息bfm.setFileNameLength(in.getInt(in.position())); // 文件名长度(字节)bfm.setFileStreamLength(in.getInt(in.position() + 4)); // 文件长度(字节)needData = bfm.getFileStreamLength() + bfm.getFileNameLength()+ 12;headData = bfm.getFileNameLength() + 12; // 头部信息byteValue = new byte[needData];readHead = true;}// 判断是否可以读取所有的数据if (needData > newIoBuffer.remaining() + in.remaining()) {bufTmp.put(newIoBuffer);bufTmp.put(in);} else {bufTmp.put(newIoBuffer);in.get(byteValue);bufTmp.put(byteValue);}bufTmp.flip();newIoBuffer = bufTmp;} else {bufTmp = IoBuffer.allocate(newIoBuffer.remaining() + in.remaining()).setAutoExpand(true);bufTmp.order(newIoBuffer.order());if (bfm.getFileStreamLength() > newIoBuffer.remaining()+ in.remaining()) {bufTmp.put(newIoBuffer);bufTmp.put(in);} else {byteValue = new byte[in.remaining()- (newIoBuffer.remaining() + in.remaining() - bfm.getFileStreamLength())];bufTmp.put(newIoBuffer);in.get(byteValue);bufTmp.put(byteValue);}bufTmp.flip();newIoBuffer = bufTmp;}// 判断是否可以读取头部信息if (headData <= newIoBuffer.remaining() + in.remaining()) {bfm.setFileNameLength(newIoBuffer.getInt()); // 文件名长度(字节)bfm.setFileStreamLength(newIoBuffer.getInt()); // 文件长度(字节)bfm.setFileName(newIoBuffer.getString(bfm.getFileNameLength(),decoder)); // 文件名。UTF-8格式bfm.setSeq(newIoBuffer.getInt()); // 序号 4dataPool = bfm.getFileStreamLength();headData = Integer.MAX_VALUE;LOGGER.info("文件序号:" + bfm.getSeq() + "   文件名:" + bfm.getFileName());}// 如果读的文件的长度(字节:比如4096个字节),远远大于这个缓冲区的容量(最大容量:2048字节),那么需要进行把一个个包黏起来if (bfm.getFileStreamLength() > (newIoBuffer.remaining())) { // 2.14把limit改成remaining/* * bfm.setFileStreamLength(bfm.getFileStreamLength() - * (newIoBuffer.remaining())); // 2.14把limit改成remaining */if (isFirst) { // 第一次isFirst = false;LOGGER.info("【服务器解析】未解析数据:" + newIoBuffer.remaining() + "字节");}isFinish = false; // 数据未读取完。} else {int remainingData = newIoBuffer.remaining(); // 总共多少LOGGER.info("【服务器解析】当前数据池中总共多少:" + remainingData + " 字节");if (remainingData >= dataPool) {byteValue = new byte[dataPool];} else {// byteValue = new byte[remainingData];LOGGER.info("【服务器解析】数据长度未满足,【退出】");return; // 退出}// byteValue = new byte[remainingData];newIoBuffer.get(byteValue);LOGGER.info("当前读取的文件大小:" + Math.ceil(dataPool / 1024) + "KB");bfm.setFileStream(byteValue);isFinish = true;LOGGER.info("【服务器解析】解析完成");}}/** * 处理完成,将所有数据初始化 */private void dataInit() {isFinish = false; // 是否已经处理所有数据isFirst = true; // 是否是第一次进来bfm = new ByteFileMessage(); // 保存对象readHead = false;}}



最新代码已经更新在github中,欢迎fork。

项目名:MinaFile


1 0
原创粉丝点击