使用java的mina框架接收数据总是会把数据拼接在一起然后再发送给上层的,所以在自定义网络传输协议的时候一定就在数据开始的固定位置处存放主体内容的数据长度,要不就是固定每个数据包的固定长度,其实两种方案的实现方式,只不过在拆包的时候一个变长,另一个是固定长度。下面就来开始代码的具体实现:
第一步,定义一个全局的变量接收保存接收的数据:
public class ClientHandler extends IoHandlerAdapter {
//...
IoBuffer recData = null;
//然后在初始化的函数里分配空间:
recData = IoBuffer.allocate(8 *1024);
recData.setAutoExpand(true);
}
第二步,在实现接收数据的接口函数里拆包,然后把不完整的数据重新存到全局变量里去:
public void messageReceived(IoSession session, Object message)
throws Exception {
//固定包头的大小
int headerSize = 16;
IoBuffer ioBuffer = (IoBuffer) message;
recData.put(ioBuffer);
recData.flip();
while(recData.remaining() >= headerSize){
recData.mark();
byte[] headBuf = new byte[headerSize];
recData.get(headBuf, 0, headerSize);
//从headBuf中解析数据得到主体内容的数据长度,存到contentLength中
int contentLength = XXX;
//如果接收到的剩余数据不够主体内容的解析大小,就重置数据,退出解析
if (contentLength > recData.remaining()){
recData.reset();
break;
}
byte[] contentBuf = new byte[contentLength];
receData.get(contentBuf, 0, contentLength);
//主体内容都存放到contentBuf,完整内容接收完后做数据的具体业务处理
//.......
}
//如果解析完还有数据多余的,就存到全局的数据块的开头。
if (recData.hasRemaining()) {
IoBuffer temp = IoBuffer.allocate(4 * 1024).setAutoExpand(true);
temp.put(recData);
temp.flip();
recData.clear();
recData.put(temp);
} else {
recData.clear();
}
}
具体的实现过程就到这里了。同时用java接收网络数据的时候一般都会遇到大小端的问题,如果数据是字符串流,这个问题就基本可以忽略了,所以在最初设计方案协议的时候可以考虑用字符串的格式,但数据格式已经是固定C结构那种,就必然会遇到大小端的问题,其实不用区分哪个是大端哪个是小端,只要两边的数据大小不一致,在读写受影响的数据的时候反转一下就可以了,主要受到影响的有16位的数据类型、32位的数据类型、64位的数据类型,字符串的数据流是不受影响的,这里给出这几种数据类型的大小端反转具体实现:
//16位
public static short NetShort(short value) {
short tmp;
tmp = (short) (((value << 8) & 0xFF00) | ((value >> 8) & 0xFF));
return tmp;
}
//32位
public static int NetInt(int value) {
int tmp;
tmp = (value << 24) | ((value << 8) & 0xFF0000)
| ((value >> 8) & 0xFF00) | ((value >> 24) & 0xFF);
return tmp;
}
//64位
public static long NetLong(long value) {
long tmp;
tmp = (((long) NetTransInt((int) (value & 0x0FFFFFFFFL)) << 32) & 0xFFFFFFFF00000000L)
| ((long)NetTransInt((int) ((value >> 32) & 0x0FFFFFFFFL)) );
return tmp;
}