MQTT-java 代码解析-接收信息

60 阅读3分钟

目标

学习接收MQTT数据并解码

核心方法

  • 方法中的in初始化方法,参见“输入流初始化过程”
  • 读取Remaining Length之后的字节,参见“读取剩余字节长度”
  • 字节流转换为MqttWireMessage对象,参见“字节流转换为MqttWireMessage对象”
    /**
    * Reads an <code>MqttWireMessage</code> from the stream.
    * If the message cannot be fully read within the socket read timeout,
    * a null message is returned and the method can be called again until
    * the message is fully read.
    * @return The {@link MqttWireMessage}
    * @throws IOException if an exception is thrown when reading from the stream
    * @throws MqttException if the message is invalid 
    */
   public MqttWireMessage readMqttWireMessage() throws IOException, MqttException {
      final String methodName ="readMqttWireMessage";
      
      MqttWireMessage message = null;
      try {
         // read header
         if (remLen < 0) {
            // Assume we can read the whole header at once.
            // The header is very small so it's likely we
            // are able to read it fully or not at all.
            // This keeps the parser lean since we don't
            // need to cope with a partial header.
            // Should we lose synch with the stream,
            // the keepalive mechanism would kick in
            // closing the connection.
            bais.reset();
            
            //读取一个字节,in的初始化参见 “输入流初始化过程”
            byte first = in.readByte();
            clientState.notifyReceivedBytes(1);

            //规范“2.1.2 MQTT Control Packet type”
            //类型判定,如果包类型大于15 或者 小于1,则判定数据包非法,抛出异常
            byte type = (byte) ((first >>> 4) & 0x0F);
            if ((type < MqttWireMessage.MESSAGE_TYPE_CONNECT) ||
                  (type > MqttWireMessage.MESSAGE_TYPE_AUTH)) {
               // Invalid MQTT message type...
               throw ExceptionHelper.createMqttException(MqttClientException.REASON_CODE_INVALID_MESSAGE);
            }

            //规范“2.1.3 Flags”
            //剩余位合法性判定,PUBLISH协议除外
            byte reserved = (byte) (first & 0x0F);
            MqttWireMessage.validateReservedBits(type, reserved);
            
            //规范“2.1.4 Remaining Length”
            //解码可变字节,获取剩余字节长度
            remLen = MqttDataTypes.readVariableByteInteger(in).getValue();
            
            //拼装数据:写入MQTT数据包第一个字节
            bais.write(first);
           //拼装数据: 写入MQTT数据包剩余长度字节 bais.write(MqttWireMessage.encodeVariableByteInteger((int)remLen));
           
           //创建数据包整体字节组,长度计算=MQTT固定头长度 + 剩余字节长度
            packet = new byte[(int)(bais.size()+remLen)];
            
            //接收包大小判定,如果超过可允许接收包的大小,抛异常
            if(this.clientState.getIncomingMaximumPacketSize() != null && 
                  bais.size()+remLen > this.clientState.getIncomingMaximumPacketSize() ) {
               // Incoming packet is too large
               throw ExceptionHelper.createMqttException(MqttClientException.REASON_CODE_INCOMING_PACKET_TOO_LARGE);
            }
            packetLen = 0;
         }
         
         // read remaining packet
         if (remLen >= 0) {
            // the remaining packet can be read with timeouts
            // 读取剩余字节内容,参见“读取剩余字节长度”
            readFully();

            // reset packet parsing state 
            remLen = -1;
            
            //转换固定头为字节数组
            byte[] header = bais.toByteArray();
            //将固定头数组复制到packet数组中,拼装完整的数据包
            System.arraycopy(header,0,packet,0, header.length);
            
            //将packet数据包构建为 MqttWireMessage对象,参见“字节流转换为MqttWireMessage对象”
            message = MqttWireMessage.createWireMessage(packet);
        
            // @TRACE 530= Received {0} 
            log.fine(CLASS_NAME, methodName, "530",new Object[] {message});
         }
      } catch (SocketTimeoutException e) {
         // ignore socket read timeout
         //TODO HARVEY
//       System.out.println(e.getMessage());
      }
      
      return message;
   }

输入流初始化过程

创建socket


SocketAddress sockaddr = new InetSocketAddress(host, port);
socket = factory.createSocket();
socket.connect(sockaddr, conTimeout*1000);
socket.setSoTimeout(1000);

创建输入流

public InputStream getInputStream() throws IOException {
   return socket.getInputStream();
}

输入流转化

public MqttInputStream(MqttState clientState, InputStream in, String clientId) {
   this.clientState = clientState;
   //
   this.in = new DataInputStream(in);    
   this.bais = new ByteArrayOutputStream();
   this.remLen = -1;
   log.setResourceName(clientId);
}

读取剩余字节长度

private void readFully() throws IOException {
   //预设偏移位置,packetLen记录超时时,已读取到的字节位置
   int off = bais.size() + (int) packetLen;
   //最大读取的字节数
   int len = (int) (remLen - packetLen);
   if (len < 0)
      throw new IndexOutOfBoundsException();
   int n = 0;
   while (n < len) {
      int count = -1;
      try {
         count = in.read(packet, off + n, len - n);
      } catch (SocketTimeoutException e) {
         // remember the packet read so far 
         packetLen += n;
         throw e;
      }
      

      if (count < 0) {
         throw new EOFException();
      }
      clientState.notifyReceivedBytes(count);
      n += count;
   }
}

字节流转换为MqttWireMessage对象

转换目的:方便程序使用相关参数数据

private static MqttWireMessage createWireMessage(InputStream inputStream) throws MqttException {
   try {
      CountingInputStream counter = new CountingInputStream(inputStream);
      DataInputStream in = new DataInputStream(counter);
      //读取第一个字节
      int first = in.readUnsignedByte();
      //计算类型,“2.1.2 MQTT Control Packet type”
      byte type = (byte) (first >> 4);
      byte info = (byte) (first &= 0x0f);
      //计算剩余长度,“2.1.4 Remaining Length” ,“1.5.5 Variable Byte Integer”
      long remLen = MqttDataTypes.readVariableByteInteger(in).getValue();
      
      //由于读取了第一个字节和剩余长度字节,所在计算总数据长度时,计算公式如下
      //但是这段代码看起来有点匪夷所思,已经拿到了remLen, 为什么还要在下一行代码重新计算一次呢?并且计算的公式,仅仅是把这行代码稍微做了一下调整
      long totalToRead = counter.getCounter() + remLen;

      MqttWireMessage result;
      long remainder = totalToRead - counter.getCounter();
      byte[] data = new byte[0];

      // The remaining bytes must be the payload
      if (remainder > 0) {
         data = new byte[(int) remainder];
         in.readFully(data, 0, data.length);
      }

     //根据包类型,创建不同的数据对象
      switch (type) {
      case MqttWireMessage.MESSAGE_TYPE_CONNECT:
         result = new MqttConnect(info, data);
         break;
      case MqttWireMessage.MESSAGE_TYPE_CONNACK:
         result = new MqttConnAck(data);
         break;
      case MqttWireMessage.MESSAGE_TYPE_PUBLISH:
         result = new MqttPublish(info, data);
         break;
      case MqttWireMessage.MESSAGE_TYPE_PUBACK:
         result = new MqttPubAck(data);
         break;
      case MqttWireMessage.MESSAGE_TYPE_PUBREC:
         result = new MqttPubRec(data);
         break;
      case MqttWireMessage.MESSAGE_TYPE_PUBREL:
         result = new MqttPubRel(data);
         break;
      case MqttWireMessage.MESSAGE_TYPE_PUBCOMP:
         result = new MqttPubComp(data);
         break;
      case MqttWireMessage.MESSAGE_TYPE_SUBSCRIBE:
         result = new MqttSubscribe(data);
         break;
      case MqttWireMessage.MESSAGE_TYPE_SUBACK:
         result = new MqttSubAck(data);
         break;
      case MqttWireMessage.MESSAGE_TYPE_UNSUBSCRIBE:
         result = new MqttUnsubscribe(data);
         break;
      case MqttWireMessage.MESSAGE_TYPE_UNSUBACK:
         result = new MqttUnsubAck(data);
         break;
      case MqttWireMessage.MESSAGE_TYPE_PINGREQ:
         result = new MqttPingReq();
         break;
      case MqttWireMessage.MESSAGE_TYPE_PINGRESP:
         result = new MqttPingResp();
         break;
      case MqttWireMessage.MESSAGE_TYPE_DISCONNECT:
         result = new MqttDisconnect(data);
         break;
      case MqttWireMessage.MESSAGE_TYPE_AUTH:
         result = new MqttAuth(data);
         break;
      default:
         throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_MALFORMED_PACKET);
      }
      return result;

   } catch (IOException ioe) {
      throw new MqttException(ioe);
   }
}