说到NIO框架,目前最流行的有Mina、Jetty、HP-Socket等。它们的实现原理基本相同,只是在细节上有些区别。说到网络编程,就离不开编解码,NIO的核心也就是让我们实现编解码,更多的关注于业务,而不再关注架构的设计。如果你会了编解码,那么网络编程就不再是问题。
今天小编就为大家详细的实现一个普通的编解码功能。先来看一下我们的协议:

解码代码如下:
public class XttblogDecoder extends CumulativeProtocolDecoder {
private static final Logger log = Logger.getLogger(XttblogDecoder.class);
private Charset charset =null;
private static final String HUPU_TCP_BUFFER = "xttblog_tcp_msg_buffer";
public XttblogDecoder(Charset charset) {
this.charset=charset;
}
@Override
protected boolean doDecode(IoSession session, IoBuffer ioBuffer, ProtocolDecoderOutput out) {
return mergePocket(session, ioBuffer, out);
}
private boolean mergePocket(IoSession session, IoBuffer ioBuffer, ProtocolDecoderOutput out){
// 上一次未处理完的byte数组 ,当前需要读的byte数组,两次合起来的总byte数组
byte[] last_byte = {} , read_byte = null , total_byte = null;
Object msg = session.getAttribute(HUPU_TCP_BUFFER);
try {
last_byte = (null == msg) ? last_byte : (byte[])msg;
// 没有剩下的则解析完成
if(ioBuffer.hasRemaining()){
ioBuffer.order(ByteOrder.LITTLE_ENDIAN);
// 获取消息剩下的长度
int bufLen = ioBuffer.remaining();
read_byte = new byte[bufLen];
ioBuffer.get(read_byte);
total_byte = new byte[last_byte.length + read_byte.length];
System.arraycopy(last_byte, 0, total_byte, 0, last_byte.length);
System.arraycopy(read_byte, 0, total_byte, last_byte.length, read_byte.length);
return decodePocket(session,out,total_byte);
}
if (last_byte.length <= 0) {
// 当前为空数据包,继续下一次读取
return Boolean.TRUE;
}
return decodePocket(session,out,last_byte);
} catch (Exception e) {
disposeTcpConnect(session);
log.error("TCP协议解码异常"+e.getMessage(),e);
} finally{
//等待回收
last_byte = null;
read_byte = null;
total_byte = null;
msg = null;
}
return Boolean.FALSE;
}
private boolean decodePocket(IoSession session,ProtocolDecoderOutput out,byte [] bufByte){
int bufByteLen = bufByte.length;
// 如果存在头部部分
if(bufByteLen>8){
MessageInfo msgInfo = new MessageInfo();
msgInfo.setMsgIdentify((short) (((bufByte[1] & 0xff) << 8) | (bufByte[0] & 0xff)));
msgInfo.setMsgBodySize(((bufByte[7] & 0xff) << 24) | ((bufByte[6] & 0xff) << 16) | ((bufByte[5] & 0xff) << 8) | (bufByte[4] & 0xff));
int msgLen = msgInfo.getMsgBodySize() + 8;
if(bufByteLen >= msgLen){
byte [] body_byte = new byte[msgInfo.getMsgBodySize()];
System.arraycopy(bufByte, 8, body_byte, 0, body_byte.length);
msgInfo.setMsgBody(new String(body_byte,charset));
out.write(msgInfo);
if(bufByteLen == msgLen){
session.removeAttribute(HUPU_TCP_BUFFER);
return Boolean.TRUE;
}
byte [] last_byte = new byte[bufByteLen - msgLen];
System.arraycopy(bufByte, msgLen , last_byte, 0, last_byte.length);
//当前数据包未读取完整,存储到会话中,等待下次读取
session.setAttribute(HUPU_TCP_BUFFER, last_byte);
if(last_byte.length > 8){
return decodePocket(session, out, last_byte);
}
body_byte = null;
last_byte = null;
}else{
session.setAttribute(HUPU_TCP_BUFFER, bufByte);
}
}
return Boolean.FALSE;
}
private void disposeTcpConnect(IoSession session){
try {
// 关闭会话
if (null != session) {
session.close(true);
//很多人因为没有销毁IoService出现打开文件过多问题
//具体文章,查看:http://www.xttblog.com/?p=446
session=null;
}
} catch (Exception e) {
log.error("关闭TCP连接异常" + e.getMessage(), e);
}
}
}
编码代码如下:
public class XttblogEncoder extends ProtocolEncoderAdapter {
private static final Logger log = Logger.getLogger(XttblogEncoder.class);
private Charset charset = null;
public XttblogEncoder(Charset charset) {
this.charset = charset;
}
public void encode(IoSession session, Object message,ProtocolEncoderOutput out) {
try {
MessageInfo hmi = (MessageInfo) message;
if (null != hmi) {
IoBuffer buf = IoBuffer.allocate(100).setAutoExpand(true);
buf.order(ByteOrder.LITTLE_ENDIAN);
byte[] bytearr = hmi.getMsgBody().getBytes(charset);
hmi.setMsgBodySize(bytearr.length);
buf.putShort(hmi.getMsgIdentify());
buf.putShort((short)0);//保留字段
buf.putInt(hmi.getMsgBodySize());
buf.put(bytearr);
buf.flip();
out.write(buf);
out.flush();
}
} catch (Exception e) {
log.error("TCP协议编码异常!"+e.getMessage(),e);
}
}
}
版权声明:本文为博主原创文章,未经博主允许不得转载。

: » 详解NIO框架Mina的编解码的应用实例
原创文章,作者:bd101bd101,如若转载,请注明出处:https://blog.ytso.com/tech/java/251444.html