最近用JAVA做服务端,使用Mina框架对接终端设备,通过TCP通讯,在数据量较大的情况下,遇到Mina接收数据不完整的情况,经过研究,找到解决方法,记录如下:
Mina版本:2.1.2
1.实现ProtocolCodecFactory类,用来拦截数据包。
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFactory;
import org.apache.mina.filter.codec.ProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolEncoder;
import org.apache.mina.filter.codec.ProtocolEncoderAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class MessageCodecFactory implements ProtocolCodecFactory {
@Autowired
private MinaMessageEncoder encoder;
@Autowired
private MinaMessageDecoder decoder;
@Override
public ProtocolDecoder getDecoder(IoSession session) throws Exception {
return decoder;
}
@Override
public ProtocolEncoder getEncoder(IoSession session) throws Exception {
return encoder;
}
}
2.再在在chain里面注册编解码器。
@Autowired
private MessageCodecFactory factory;
public static IoAcceptor acceptor;
/**处理数据的服务*/
@Autowired
private MinaServer minaServer;
@Bean
public IoAcceptor ioAcceptor() throws Exception {
acceptor = new NioSocketAcceptor();
acceptor.getFilterChain().addLast("logger", new LoggingFilter());
acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(factory));
acceptor.getSessionConfig().setMaxReadBufferSize(1024);
acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 60);
acceptor.setHandler(minaServer);
acceptor.bind(new InetSocketAddress(port));
return acceptor;
}
3.实现decode和encoder
继承CumulativeProtocolDecoder类实现Decode:
import lombok.extern.slf4j.Slf4j;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.springframework.stereotype.Component;
/**
* mina自动分包粘包的处理
*/
@Slf4j
@Component
public class MinaMessageDecoder extends CumulativeProtocolDecoder {
@Override
protected boolean doDecode(IoSession ioSession, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
/**读取当前 IoBuffer 中已接收到的数据,判断是否得到了完整的数据包*/
byte[] data = new byte[in.limit()];
in.get(data);
/**解析接收到的数据,根据指令约定进行判断数据是否接收完整*/
String typeStr = ByteUtil.bytesToHexString(data).toLowerCase();
if (typeStr.startsWith("cc")) {
byte[] msg = FingerParamHandler.getFingerMsg(data);
/**msg为空则为数据接收不完整,这里根据自身情况更改判断逻辑,接收不完整,调用in.rewind(),并返回false,*/
if (msg == null) {
in.rewind();
return false;/**通知父类的 decode 方法,当前数据不完整,放弃本次读取,下个数据包到来时再做处理*/
}
}
/**创建一个包含一个完整数据包的IoBuffer对象*/
IoBuffer buffer = IoBuffer.wrap(data);
/**将IoBuffer对象写出,在IoHandlerAdapter类的messageReceived方法中进行处理*/
out.write(buffer);
return true;/**通知父类的 decode 方法,当前接收并读取的数据已处理*/
}
}
继承ProtocolEncoderAdapter实现Encode:
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolEncoderAdapter;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
import org.springframework.stereotype.Component;
@Component
public class MinaMessageEncoder extends ProtocolEncoderAdapter {
/**这里不做处理*/
@Override
public void encode(IoSession ioSession, Object message, ProtocolEncoderOutput out) throws Exception {
}
}
到此,完成Mina自动分包粘包的问题。