mina框架CumulativeProtocolDecoder.doDecode方法浅析

来源:互联网 发布:淘宝four loko是真的嘛 编辑:程序博客网 时间:2024/05/19 22:58

测试说明
注释代码37行,打开38行。

服务器正常启动,确认设备连接正常。
设备为客户端,每3秒向服务器发送心跳

模拟网络短暂不通,多个包同时到达,粘包情况:
拔掉网线30秒,插入网线。

代码如下

package mina;import java.io.IOException;import java.net.InetSocketAddress;import java.util.Arrays;import org.apache.mina.core.buffer.IoBuffer;import org.apache.mina.core.service.IoHandlerAdapter;import org.apache.mina.core.session.AttributeKey;import org.apache.mina.core.session.IoSession;import org.apache.mina.filter.codec.CumulativeProtocolDecoder;import org.apache.mina.filter.codec.ProtocolCodecFilter;import org.apache.mina.filter.codec.ProtocolDecoderOutput;import org.apache.mina.filter.codec.ProtocolEncoderAdapter;import org.apache.mina.filter.codec.ProtocolEncoderOutput;import org.apache.mina.transport.socket.nio.NioSocketAcceptor;public class Test {    public static void main(String[] args) throws IOException {        final byte[] key = { (byte) 0xb9, (byte) 0xda, 0x5e, 0x15, 0x46, 0x57, (byte) 0xa7, (byte) 0x8d, (byte) 0x9d,                (byte) 0x84, (byte) 0x90, (byte) 0xd8, (byte) 0xab, 0x00, (byte) 0x8c, (byte) 0xbc, (byte) 0xd3, 0x0a,                (byte) 0xf7, (byte) 0xe4, 0x58, 0x05, (byte) 0xb8, (byte) 0xb3, 0x45, 0x06, (byte) 0xd0, 0x2c, 0x1e,                (byte) 0x8f, (byte) 0xca, 0x3f };        NioSocketAcceptor acceptor = new NioSocketAcceptor();        acceptor.getFilterChain().addFirst("codec", new ProtocolCodecFilter(new ProtocolEncoderAdapter() {            @Override            public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception {                System.out.println("编码:" + message);            }        }, new CumulativeProtocolDecoder() {            @Override            protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {                System.out.println();                System.out.println("长度:" + in.limit());                System.out.println();                IoBuffer ioBuffer = getContext(session).getIoBuffer();                //IoBuffer ioBuffer = IoBuffer.allocate(100).setAutoExpand(true);                while (in.hasRemaining()) {                    byte b = in.get();                    ioBuffer.put(b);                    System.out.print("【"+b+"】");                    if (b != 0x03 && b != 0x04) {                        continue;                    }                    byte[] array = ioBuffer.array();                    if (array[0] != 0x02) {                        System.out.println();                        System.out.println("=======================================");                        System.out.println("未知包" + ByteUtil.toHexStr(array));                        System.out.println("=======================================");                        throw new RuntimeException("================未知包================");                    }                    array = Arrays.copyOfRange(array, 1, ioBuffer.position() - 2);                    int temp1 = array.length / 32;                    int temp2 = array.length % 32;                    for (int i = 0; i < temp1; i++) {                        for (int j = 0; j < 32; j++) {                            array[i * 32 + j] ^= key[j];                        }                    }                    for (int j = 0; j < temp2; j++) {                        array[temp1 * 32 + j] ^= key[j];                    }                    System.out.println("");                    System.out.println("=======================================");                    System.out.println("解析到包:" + new String(array));                    System.out.println("=======================================");                    out.write(new String(array));                    ioBuffer.clear();                    System.out.println("返回true");                    return true;                }                System.out.println("返回false");                return false;            }            private final AttributeKey CONTEXT = new AttributeKey(getClass(), "context");            public Context getContext(IoSession session) {                Context ctx = (Context) session.getAttribute(CONTEXT);                if (ctx == null) {                    ctx = new Context();                    session.setAttribute(CONTEXT, ctx);                }                return ctx;            }            class Context {                private IoBuffer ioBuffer = IoBuffer.allocate(100).setAutoExpand(true);                public IoBuffer getIoBuffer() {                    return ioBuffer;                }            }        }));        acceptor.setHandler(new IoHandlerAdapter() {            @Override            public void messageReceived(IoSession session, Object message) throws Exception {                super.messageReceived(session, message);            }        });        acceptor.bind(new InetSocketAddress(3333));    }}

打印如下

长度:53【2】【-35】【-65】【40】【124】【37】【50】【-99】【-18】【-8】【-80】【-95】【-71】【-101】【53】【-24】【-100】【-70】【110】【-51】【-44】【120】【102】【-43】【-41】【127】【50】【-32】【20】【62】【-7】【-85】【83】【-52】【-65】【100】【119】【35】【54】【-45】【-13】【-29】【-92】【-4】【-67】【-59】【103】【-8】【-44】【-23】【61】【0】【3】=======================================解析到包:device:ce41a05d id:0 cmd:408 value:beat~~ length:7=======================================返回true长度:256【2】【-35】【-65】【40】【124】【37】【50】【-99】【-18】【-8】【-80】【-95】【-71】【-101】【53】【-24】【-100】【-70】【110】【-51】【-44】【120】【102】【-43】【-41】【127】【50】【-32】【20】【62】【-7】【-85】【83】【-52】【-65】【100】【119】【35】【54】【-45】【-13】【-29】【-92】【-4】【-67】【-59】【103】【-8】【-44】【-23】【61】【0】【3】=======================================解析到包:device:ce41a05d id:0 cmd:408 value:beat~~ length:7=======================================返回true长度:256【2】【-35】【-65】【40】【124】【37】【50】【-99】【-18】【-8】【-80】【-95】【-71】【-101】【53】【-24】【-100】【-70】【110】【-51】【-44】【120】【102】【-43】【-41】【127】【50】【-32】【20】【62】【-7】【-85】【83】【-52】【-65】【100】【119】【35】【54】【-45】【-13】【-29】【-92】【-4】【-67】【-59】【103】【-8】【-44】【-23】【61】【0】【3】=======================================解析到包:device:ce41a05d id:0 cmd:408 value:beat~~ length:7=======================================返回true长度:256【2】【-35】【-65】【40】【124】【37】【50】【-99】【-18】【-8】【-80】【-95】【-71】【-101】【53】【-24】【-100】【-70】【110】【-51】【-44】【120】【102】【-43】【-41】【127】【50】【-32】【20】【62】【-7】【-85】【83】【-52】【-65】【100】【119】【35】【54】【-45】【-13】【-29】【-92】【-4】【-67】【-59】【103】【-8】【-44】【-23】【61】【0】【3】=======================================解析到包:device:ce41a05d id:0 cmd:408 value:beat~~ length:7=======================================返回true长度:256【2】【-35】【-65】【40】【124】【37】【50】【-99】【-18】【-8】【-80】【-95】【-71】【-101】【53】【-24】【-100】【-70】【110】【-51】【-44】【120】【102】【-43】【-41】【127】【50】【-32】【20】【62】【-7】【-85】【83】【-52】【-65】【100】【119】【35】【54】【-45】【-13】【-29】【-92】【-4】【-67】【-59】【103】【-8】【-44】【-23】【61】【0】【3】=======================================解析到包:device:ce41a05d id:0 cmd:408 value:beat~~ length:7=======================================返回true长度:256【2】【-35】【-65】【40】【124】【37】【50】【-99】【-18】【-8】【-80】【-95】【-71】【-101】【53】【-24】【-100】【-70】【110】【-51】【-44】【120】【102】【-43】【-41】【127】【50】【-32】【20】【62】【-7】【-85】【83】【-52】【-65】【100】【119】【35】【54】【-45】【-13】【-29】【-92】【-4】返回false长度:433【-67】【-59】【103】【-8】【-44】【-23】【61】【0】【3】=======================================未知包BD,C5,67,F8,D4,E9,3D,00,03,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00=======================================长度:53【2】【-35】【-65】【40】【124】【37】【50】【-99】【-18】【-8】【-80】【-95】【-71】【-101】【53】【-24】【-100】【-70】【110】【-51】【-44】【120】【102】【-43】【-41】【127】【50】【-32】【20】【62】【-7】【-85】【83】【-52】【-65】【100】【119】【35】【54】【-45】【-13】【-29】【-92】【-4】【-67】【-59】【103】【-8】【-44】【-23】【61】【0】【3】=======================================解析到包:device:ce41a05d id:0 cmd:408 value:beat~~ length:7=======================================返回true

解释
插入网线后,不打印日志。(模拟的,有时是马上就打印)
大约20秒后,瞬间收到5、60行日志。

一个心跳包已0x02开头、已0x03结尾,长度为53。

日志第2行:长度为53,表示这个包正常

日志第10行:长度为256,说明IoBuffer in一次从缓存读取256个字节,是4个心跳包加半个心跳包的长度。
代码:
41行,每次取一个字节
42行,保存字节到ioBuffer
45行,如果不是取到一个完整的包,继续保存字节到ioBuffer
60行,解码数据
79行,返回true
上级代码

protected CumulativeProtocolDecoder() {    }    public void decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {        if (!session.getTransportMetadata().hasFragmentation()) {            while (in.hasRemaining()) {            /**        当前读取到in的53的位置,还有剩余字节(共256),并且doDecode返回的true,则继续执行doDecode。CumulativeProtocolDecoder.decode(..)的上层为同步,所以这个没循环完,就算缓存还有新数据,也需要等待decode方法执行完才可以。上层代码: synchronized (session) {                    decoder.decode(session, in, decoderOut);                }            */                if (!doDecode(session, in, out)) {                    break;                }            }            return;        }        boolean usingSessionBuffer = true;        IoBuffer buf = (IoBuffer) session.getAttribute(BUFFER);

日志第18行:
从in的256的53位置继续读取53个字节,继续返回true

日志第26行:
从in的256的106位置继续读取53个字节,继续返回true

日志第34行:
从in的256的159位置继续读取53个字节,继续返回true

日志第34行:
从in的256的212位置继续读取53个字节,继续返回true

日志第42行:
从in的256的212位置继续读取44个字节,返回false
CumulativeProtocolDecoder() {
}
public void decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
if (!session.getTransportMetadata().hasFragmentation()) {
while (in.hasRemaining()) {
/**
返回false,表示本方法执行完毕。如果有新包则在执行。
*/
if (!doDecode(session, in, out)) {
break;
}
}
return;
}

日志第42行:
本次长度433(上次心跳包的9个字节+新的8个心跳包的长度)
test.java.38行又重新创建的IoBuffer,通过0x03截取的时候,取到的是9个字节的长度。所以需要把IoBuffer放入session中,就可以连接上次取到的包加本次的9个字节,组合成一个完成的包。

原创粉丝点击