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

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; // 1G
	private 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("创建解析器新对象");
	}

	@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不会移动。

			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()); // 序号 4
			dataPool = 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值