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