【MinaFile】【十三】【2.0】关于粘包的处理

我们在Mina服务器对文件进行自定义解码的时候,继承了类CumulativeProtocolDecoder。

这个类默认的字节数处理是2048,所以当文件超过了2048个字节之后,服务器解码器就会报错java.nio.BufferUnderflowException 这个异常。

以为需要解析的字节为4096KB,但是Mina能够一次性处理的只有2048个字节,所以就会报错。很简单吧?

解决办法,重写一下第【十一】章里面的ByteProtocalDecoder类


/**
 * 这个是服务器对客户端发来的消息进行解码
 * 继承CumulativeProtocolDecoder
 * 实现doDecode
 * 父类会将数据读取完之后,再调用实现的方法doDecode。
 * 如果成功读取完之后,服务器会去Handle中进行业务处理。
 * 对发来的文件进行业务处理,比如说保存之类的 动作。
 * @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; // 1G
	private boolean isFinish = false; // 是否已经处理所有数据
	private static boolean isFirst = true; // 是否是第一次进来
	private ByteFileMessage bfm = new ByteFileMessage(); // 保存对象
	private static IoBuffer newIoBuffer = IoBuffer.allocate(0).setAutoExpand(true) ;
	public ByteProtocalDecoder() {
	}

	@Override
	protected 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不会移动。
		
		  if ((!isFirst) || in.prefixedDataAvailable(4, MAX_FILE_SIZE)) {
			  this.readFile(in);
			  if(isFinish){
				  // 解析完成
				  out.write(bfm);
			  }else{
				  return true;
			  }
			  
		  }else{
			  in.position(0);
			  LOGGER.info("不符合读取条件");
			  return false;
		  }
		}catch (Exception e) {
			LOGGER.info("服务器解码过程中发生错误",e);
			return false;
		}
		return true;
	}
	
	private void readFile(IoBuffer in) throws CharacterCodingException  {
		CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder();
		in.position(0);
		// ByteFileMessage bfm = new ByteFileMessage();
		if(isFirst){
			bfm.setSeq(in.getInt()); //序号
			bfm.setFileNameLength(in.getInt()); // 文件名长度(字节)
			bfm.setFileName(in.getString(bfm.getFileNameLength(),decoder)); // 文件名。UTF-8格式
			bfm.setFileStreamLength(in.getInt()); // 文件长度(字节)
		}
		//如果读的文件的长度(字节:比如4096个字节),远远大于这个缓冲区的容量(最大容量:2048字节),那么需要进行把一个个包黏起来
		byte[] byteValue = null;
		if(bfm.getFileStreamLength() > in.limit()){
			/*int dealDataLen = in.limit(); // 本次要处理的数据长度
			
			byteValue = new byte[dealDataLen];*/
			bfm.setFileStreamLength(bfm.getFileStreamLength() - in.limit());
			IoBuffer bufTmp = null;
			if(isFirst){ // 第一次
				LOGGER.info("【服务器解析】继续接受in:" + in.remaining());
				bufTmp = IoBuffer.allocate(newIoBuffer.remaining() +in.remaining() ).setAutoExpand(true);
				isFirst = false;
			}else{
				LOGGER.info("【服务器解析】继续接受in:" + in.remaining());
				bufTmp = IoBuffer.allocate(newIoBuffer.remaining() + in.remaining() ).setAutoExpand(true);
			}
			bufTmp.order(newIoBuffer.order());
			bufTmp.put(newIoBuffer);
			bufTmp.put(in);
			bufTmp.flip();
			newIoBuffer = bufTmp;
			isFinish = false; // 数据未读取完。
			LOGGER.info("【服务器解析】未解析数据:" + newIoBuffer.remaining() + "字节");
		}else{
			IoBuffer bufTmp = IoBuffer.allocate( newIoBuffer.remaining() + in.remaining() ).setAutoExpand(true);
			bufTmp.order(newIoBuffer.order());
			bufTmp.put(newIoBuffer);
			bufTmp.put(in);
			bufTmp.flip();
			newIoBuffer = bufTmp;
			int remainingData = newIoBuffer.remaining(); // 总共多少
			LOGGER.info("【服务器解析】文件需要解析多少字节:" + remainingData);
			byteValue = new byte[remainingData];
			newIoBuffer.get(byteValue);
			LOGGER.info("当前读取的文件大小:" +  remainingData/1024 + "KB"  );
			bfm.setFileStream(byteValue);
			isFinish = true;
			LOGGER.info("【服务器解析】解析完成");
		}
		//LOGGER.info(new String(). bfm.getFileStream());
		//return isFinish;
    }
}

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

项目名:MinaFile


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值