【MinaFile】【十一】【2.0】实现客户端与服务器交互的自定义编码解码器

1.客户端编码类ByteProtocalEncoder

 首先进行的是客户端对消息的编码,然后再发送给客户端。

 * 客户端把消息文件实体封装发送给服务器
 * 文件名文件路径在handle中处理
 * handle从配置文件中获取到对应文件
 * 当把消息封装好之后,发送给服务器,服务器对文件解析。 

/**
 * 客户端把消息文件实体封装发送给服务器
 * 文件名文件路径在handle中处理
 * handle从配置文件中获取到对应文件
 * 当把消息封装好之后,发送给服务器,服务器对文件解析。 
 * @author king_fu
 * 
 */
public class ByteProtocalEncoder extends ProtocolEncoderAdapter {
	private static final Logger LOGGER = LoggerFactory
			.getLogger(ByteProtocalEncoder.class);

	@Override
	public void encode(IoSession session, Object message,
			ProtocolEncoderOutput out) throws Exception {
		LOGGER.info("客户端开始编码,准备发往服务器……");
		CharsetEncoder character = Charset.forName("UTF-8").newEncoder();
		ByteFileMessage bfm = (ByteFileMessage) message;
		// 输入流
		// 处理流部分
		File file = new File(bfm.getFilePath());
		bfm.setFileName(file.getName()); // 文件名
		bfm.setFileStreamLength(bfm.getFileName().getBytes().length);
		
		FileInputStream fis = new FileInputStream(file);
		FileChannel channel = fis.getChannel();
		ByteBuffer byteBuffer = ByteBuffer.allocate((int)channel.size());
		byteBuffer.clear();
		channel.read(byteBuffer);
		
		// Mina处理部分
		IoBuffer ioBuffer = IoBuffer.allocate(
				// 12 = 4+4+4
				(int) channel.size() + 12 + bfm.getFileName().getBytes().length
						+ bfm.getFilePath().getBytes().length).setAutoExpand(
				true);
		ioBuffer.putInt(bfm.getSeq()); // 序号
		ioBuffer.putInt(bfm.getFileName().getBytes().length);
		ioBuffer.putString(bfm.getFileName(),character); // 文件名
		ioBuffer.putInt((int)channel.size());
		ioBuffer.put(byteBuffer);
		ioBuffer.put(byteBuffer.array()); // bfm.getFileStream()
		ioBuffer.flip();
		
		out.write(ioBuffer);
	}

}

2. 服务器解码类ByteProtocalDecoder

 这个解码类继承Mina的CumulativeProtocolDecoder协议

 * 这个是服务器对客户端发来的消息进行解码
 * 继承CumulativeProtocolDecoder
 * 实现doDecode
 * 父类会将数据读取完之后,再调用实现的方法doDecode。
 * 如果成功读取完之后,服务器会去Handle中进行业务处理。
 * 对发来的文件进行业务处理,比如说保存之类的 动作。

/**
 * 这个是服务器对客户端发来的消息进行解码
 * 继承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
	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 (in.prefixedDataAvailable(4, MAX_FILE_SIZE)) {
			  ByteFileMessage bfm =  this.readFile(in);
			  out.write(bfm);
		  }else{
			  LOGGER.info("不符合读取条件");
			  return false;
		  }
		}catch (Exception e) {
			LOGGER.info("服务器解码过程中发生错误",e);
			return false;
		}
		return true;
	}
	
	private ByteFileMessage readFile(IoBuffer in) throws CharacterCodingException  {
		CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder();
		in.position(0);
		ByteFileMessage bfm = new ByteFileMessage();
		bfm.setSeq(in.getInt()); //序号
		bfm.setFileNameLength(in.getInt()); // 文件名长度(字节)
		bfm.setFileName(in.getString(bfm.getFileNameLength(),decoder)); // 文件名。UTF-8格式
		bfm.setFileStreamLength(in.getInt()); // 文件长度(字节)
		byte[] byteValue = new byte[bfm.getFileStreamLength()];
		LOGGER.info("读取的文件大小:" +  bfm.getFileStreamLength()/1024/1024 + "M"  );
		in.get(byteValue);
		bfm.setFileStream(byteValue);
		LOGGER.info("解析完成"  );
		return bfm;
    }
    

}

3. 服务器编码类ByteResultProtocalEncoder

服务器处理完业务之后,可能需要给客户端返回一条消息。告知客户端服务器收到了,

所以根据场景,需要一个服务器编码类。对返给客户端的信息进行编码

/**
 * 这个是服务器对返回客户端的消息进行编码。
 * @author king_fu
 *
 */
public class ByteResultProtocalEncoder extends ProtocolEncoderAdapter{
	private static final Logger LOGGER = LoggerFactory.getLogger(ByteResultProtocalEncoder.class);
	@Override
	public void encode(IoSession session, Object message,
			ProtocolEncoderOutput out) throws Exception {
		LOGGER.info("服务器对返回客户端的消息进行编码");
		CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder();// 以UTF-8的方式进行解析字符串
		// 对message进行强转
		ByteReturnFileMessage returnMassage = (ByteReturnFileMessage)message;
		IoBuffer ioBuffer = IoBuffer.allocate( 4 + 4 + returnMassage.getReturnMassage().length()).setAutoExpand(true);
		ioBuffer.putInt(returnMassage.getSeq()); // 序号
		// 传给客户端消息的字节数。
		// 这里要用到字节数。不过只是获取字符串的长度,那么不对的。
		// 因为这是根据字节数获取数据的。所以如果字符串中有中文,那么就会获取出错。
		ioBuffer.putInt(returnMassage.getReturnMassageLength());
		
		LOGGER.info("服务器传给客户端字符长度(字节):" + returnMassage.getReturnMassageLength() );
		ioBuffer.putString(returnMassage.getReturnMassage(), encoder);
		LOGGER.info("服务器准备发送的信息【" + returnMassage.getReturnMassage()+ "】;字符串编码类型:" + encoder.toString());
		ioBuffer.flip();
		LOGGER.info("服务器编码完成");
		out.write(ioBuffer);
		
	}

}


4. 客户端解码类ByteResultProtocalDecoder

 * 这是客户端对服务器发来的消息进行解码 消息封装在ByteReturnFileMessage实体中。 
 * 在解析完之后,调用客户端定义的Handle
 * handle中的方法messageReceived 在方法中进行处理。

/**
 * 这是客户端对服务器发来的消息进行解码 消息封装在ByteReturnFileMessage实体中。 
 * 在解析完之后,调用客户端定义的Handle
 * handle中的方法messageReceived 在方法中进行处理。
 * 
 * @author king_fu
 * 
 */
public class ByteResultProtocalDecoder extends CumulativeProtocolDecoder {
	private static final Logger LOGGER = LoggerFactory
			.getLogger(ByteResultProtocalDecoder.class);
	public static final int MAX_FILE_SIZE = 1024 * 1024 * 2; // 2M
	public ByteResultProtocalDecoder() {
	}

	@Override
	protected boolean doDecode(IoSession session, IoBuffer in,
			ProtocolDecoderOutput out) throws Exception {
		LOGGER.info("客户端对服务器返回的消息进行解码。解码开始");
		LOGGER.info("当前pos:" + in.position());
		LOGGER.info("总共limit:" + in.limit());
		LOGGER.info("所有remaining:" + in.remaining());

		try {
			if (in.prefixedDataAvailable(4, MAX_FILE_SIZE)) {
				ByteReturnFileMessage brf =  this.readFile(in);
				out.write(brf);
			} else {
				LOGGER.info("【客户端解码】不符合读取条件");
				return false;
			}
		} catch (Exception e) {
			LOGGER.info("【客户端解码】解码过程中发生错误", e);
			return false;
		}
		return true;
	}
	
	private ByteReturnFileMessage readFile(IoBuffer in) throws CharacterCodingException  {
		CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder();
		ByteReturnFileMessage brf = new ByteReturnFileMessage();
		in.position(0); // position归零
		brf.setSeq(in.getInt()); // 序号
		brf.setReturnMassageLength(in.getInt()); // 字节数
		brf.setReturnMassage(in.getString(brf.getReturnMassageLength(),
				decoder)); // 服务器返回的消息。字符串
		LOGGER.info("【客户端解码】服务器返回内容:"  + brf.getReturnMassage());
		return brf;
    }

}

这个1234四个步骤代表了从客户端发送消息到服务器,服务器处理,消息再从服务器出来,返给客户端的一个流程。

忘了还有最后一个工厂。

这个工厂是对客户端和服务器实现的编码解码进行分配。就像是给客户端和服务器相应的功能。可以这样理解。


5. 编码解码类工厂ByteProtocalCodecFactory

/**
 * 编码解码类工厂。
 * 注意服务器和客户端需要的编码解码是不同的。
 * @author king_fu
 *
 */
public class ByteProtocalCodecFactory  implements ProtocolCodecFactory {
	
	public ByteProtocalCodecFactory(boolean isServer){
		if(isServer){
			encoder = new ByteResultProtocalEncoder(); // 返回给客户端的消息封装
			
			decoder = new ByteProtocalDecoder(); // 解析客户端发来的消息
		}else{
			// 客户端
			encoder = new ByteProtocalEncoder();	// 发给服务器的消息封装
			decoder = new ByteResultProtocalDecoder();	// 对服务器发来的回应信息进行解析
		}
		
	}
	
	private ProtocolDecoder decoder = new ByteProtocalDecoder();

	private ProtocolEncoder encoder = new ByteProtocalEncoder();
	@Override
	public ProtocolEncoder getEncoder(IoSession session) throws Exception {
		return encoder;
	}

	@Override
	public ProtocolDecoder getDecoder(IoSession session) throws Exception {
		return decoder;
	}

}

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

项目名:MinaFile





  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值