zoukankan      html  css  js  c++  java
  • [编织消息框架][设计协议]解决粘包半包(中)

    上节介绍问题出现跟处理方式,写数据部份已经实现

    这节介绍如何读处理

    处理流程分三部分

    1.校验包是否合法

    2.读取包内容

    3.切割包

    由于切割包用的是netty处理,所以只需集中精力解决前两个问题即可

    ByteToMessageHandler.class

     1  @Override
     2  public QPacket decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
     3     ByteBuf byteBuf = decoder.decode(ctx, in);//解释包 netty框架处理
     4     if (byteBuf == null || !byteBuf.isReadable()) { //netty解释失败或未成功会返回null 
     5         return null;
     6     }
     7     if (byteBuf.readableBytes() < QPacket.PACK_FIXED_LENG) { //缓冲内容bytes长度少于包最小固定长度
     8         return null;
     9     }
    10     final short headFlag = byteBuf.readShort(); //读取头部开始信息 2byte
    11     final int packetLen = byteBuf.readInt(); //读取内容长度
    12     final int endLen = 1; //尾部结束标志 1byte
    13   //比较头部信息是否合法
    14     if (headFlag != QMConfig.getInstance().getPacketHeadFlag(packetLen)) {
    15         throw new QSocketException(QCode.SOCKET_ERROR_PCKET_FLAG, "解码包头标识不对");
    16     }
    17 
    18     // 缓冲可读长度是否少于包长度 netty 处理过了
    19     // if (byteBuf.readableBytes() - endLen < packetLen) {
    20     // return null;
    21     // }
    22   
    23     final int mark = byteBuf.readerIndex(); //标记已读坐标,目的先读最后结束信息做比较
    24     byteBuf.skipBytes(packetLen); //跳到最后结束标记
    25     final byte endFlag = byteBuf.readByte(); //读取结束标记1byte
    26     if (endFlag != QMConfig.getInstance().getPacketEndFlag(packetLen)) {
    27         throw new QSocketException(QCode.SOCKET_ERROR_PCKET_FLAG, "解码包尾标识不对");
    28     }
    29     byteBuf.readerIndex(mark);//还原坐标,开始处理QPacket
    30     QPacket ret = QPacket.of(byteBuf, packetLen); //构造QPacket
    31     byteBuf.skipBytes(endLen); //跳结束标记
    32     return ret;
    33  }

    QPacket

    1   public static QPacket of(ByteBuf byteBuf, int packetLen) { //按写入顺序读取
    2     long sn = byteBuf.readLong(); //读包序列
    3     short c = byteBuf.readShort();//读opcode
    4     byte[] b = new byte[packetLen - QPacket.PACK_FIXED_LENG]; //算出内容实际大小
    5     byteBuf.readBytes(b); //读取内容
    6     long sid = byteBuf.readLong(); //读取sessionId
    7     return of(c, sn, sid, null, b);
    8   }
    1   public static QPacket of(byte[] bytes) {
    2     int offset = 0;
    3     long sn = PacketUtil.readLong(offset, bytes);
    4     short c = PacketUtil.readShort(offset += Long.BYTES, bytes);
    5     byte[] b = PacketUtil.readBytes(offset += Short.BYTES, bytes.length - QPacket.PACK_FIXED_LENG, bytes);
    6     long sid = PacketUtil.readLong(offset += b.length, bytes);
    7     return of(c, sn, sid, null, b);
    8   }

    为什么没有直接用第二种方式去构造QPacket?

    如果多一个临时对象array byte 会有损耗,无必要占创建时间,多一份内存开销

    之前写入消息也采取相同处理方式,直接把数据传给netty ByteBuf 底层

     1   public void writeToByteBuf(ByteBuf byteBuf) {
     2     final int packetLen = toSize();
     3     byteBuf.writeShort(QMConfig.getInstance().getPacketHeadFlag(packetLen));
     4     byteBuf.writeInt(packetLen);
     5     writeBytes(byteBuf);
     6     byteBuf.writeByte(QMConfig.getInstance().getPacketEndFlag(packetLen));
     7   }    
     8 
     9   @Override
    10   public byte[] toBytes() {
    11     final int len = toSize();
    12     byte[] ret = new byte[len];
    13     int offset = 0;
    14     PacketUtil.writeLong(offset, sn, ret);
    15     PacketUtil.writeShort(offset += Long.BYTES, c, ret);
    16     PacketUtil.writeBytes(offset += Short.BYTES, b, ret);
    17     PacketUtil.writeLong(offset += b.length, sid, ret);
    18     return ret;
    19   }
  • 相关阅读:
    树莓派.安装Firefox浏览器
    树莓派.Raspberry Pi 3碰到"Unable to determine hardware version. I see: Hardware : BCM2835"错误的解决过程
    Linux.Centos6编译安装nginx
    树莓派.桌面分辨率设置
    [转]树莓派.设置自动重连WiFi
    树莓派.系统.官方下载中NOOBS和Raspbian的区别
    树莓派.设置无线网卡为AP工作模式(pi2和pi3)
    Nodejs.调用Linux命令
    树莓派.屏幕休眠控制
    GO语言.树莓派.环境安装和测试
  • 原文地址:https://www.cnblogs.com/solq111/p/6541445.html
Copyright © 2011-2022 走看看