mina在做数据发送的时候通过通过过滤器来做数据的转换
ProtocolCodecFilter
用来在字节流和消息对象之间互相转换。当该过滤器接收到字节流的时候,需要首先判断消息的边界,然后把表示一条消息的字节提取出来,通过一定的逻辑转换成消息对象,再把消息对象往后传递,交给 I/O 处理器来执行业务逻辑。
在发送数据的时候,如果数据量大,是会分包发送的,同时mina的数据接收区有默认的大小限制,比如缓冲与为1024的话,那么当缓冲区满了就回进入decode来尝试解码,如果没有做协议长度的控制,就可能会丢失数据。这次项目就用到了mina,记录一些封包的经验。
一、协议如何定
1.固定长度的报文(长度固定为100),每次收满固定长度的报文就去解析,否则就等待数据
2.前N位放表明报文长度的数据,每次接收先读出报文长度,然后在读长度内容的数据
这里详细介绍下第2种,这里的报文长度也有一些区别,比如:前4字节表明一个byte和前4字节表明一个4位的字符串数字,是不同的概念
第一种:int用4字节表示,只要报文长度不大于MAXInt即可
报文发送方:
//报文的原始内容 String str = "content"; int length = 365; //转换报文长度为byte byte[] len = new byte[4]; for (int i = 0; i < 4; i++) { int offset = (len.length - 1 - i) * 8; len[i] = (byte) ((length >>> offset) & 0xFF); } //获取长度+报文的内容byte byte[] content = str.getBytes(); content = Utils.join(len, content);解析这种报文可以使用:
//判断数据是否接收完毕 if (in.prefixedDataAvailable(4)) { //处理报文 decodeHandle(in, out); } else { //报文没有接受完毕,需要继续等待数据 return false; }注意:
使用 in.prefixedDataAvailable(4) 的时候一定要注意,这个方法只支持1、2、4三个参数类型
1对应short、2对应UnsignedShort、4对应int。其实一个int对应的报文还是很长了,基本上够用了
这次的报文通信机构使用的是第二种方法
所以也介绍下第二种方法的封包方法
报文发送方:
//报文的原始内容 String str = "content"; String length = "0365"; //获取长度+报文的内容byte String content = length + str; byte[] byteContent = content.getBytes();解析这种报文可以使用:
//先标记当前buffer的位置 in.mark(); //判断当前buffer长度位是否发送过来 if (in.remaining() <= 4) { return false; } // 有数据时,根据定义好的字节个数判断消息长度 byte[] sizeBytes = new byte[4]; in.get(sizeBytes); // 报文长度 String lengthStr = new String(sizeBytes); int size = Integer.valueOf(lengthStr); // 判断报文 if (size <= in.remaining()) { //报文接收完毕 return true; } // 由于in.get会改变in游标的位置,所以reset到初始位置,也可以in.position(in.markValue()); in.reset(); return false;
它会一直递归读取数据,每次递归中会执行doEncode方法, 在子类的doEncode方法中,我们可以根据我们的协议来判断数据是否接收够了(所以每次循环的时候要保证游标不转移)。