Mqtt底層使用webSocket實現,經過發送http或https請求與服務端開始進行handshake,握手完成後協議將從http(https)升級成webSocket並創建長連,以後經過長連進行通訊。sdk中關於websocket部分結構及handshake信息以下:git
分析心跳包首先看用於發送心跳信息的MqttPingReq.class和用於接收心跳答覆的MqttPingResp.classweb
而後,咱們須要從其發送的內容當中逆向推出其心跳包的內容。我 們先看其發送的的模塊:找到public class CommsSender implements Runnable 類,看到其有一個private MqttOutputStream out;私有字段,一看這個方法,咱們就能判斷,這個字段就是輸出流,打開public class MqttOutputStream extends OutputStream這個類,你會看到這樣一個方法:websocket
/** * Writes an <code>MqttWireMessage</code> to the stream. */ public void write(MqttWireMessage message) throws IOException, MqttException { final String methodName = "write"; byte[] bytes = message.getHeader(); byte[] pl = message.getPayload(); // out.write(message.getHeader()); // out.write(message.getPayload()); out.write(bytes,0,bytes.length); clientState.notifySentBytes(bytes.length); int offset = 0; int chunckSize = 1024; while (offset < pl.length) { int length = Math.min(chunckSize, pl.length - offset); out.write(pl, offset, length); offset += chunckSize; clientState.notifySentBytes(length); } // @TRACE 500= sent {0} log.fine(CLASS_NAME, methodName, "500", new Object[]{message}); }
原來,其發送的是header和payload,而後咱們再看看心跳包的header和payload。socket
MqttPingReq和MqttPingResp中都有這麼個方法:ide
protected byte[] getVariableHeader() throws MqttException { return new byte[0]; }
往上查看其共同父類MqttWireMessage.class中的getHeader():this
public byte[] getHeader() throws MqttException { try { int first = ((getType() & 0x0f) << 4) ^ (getMessageInfo() & 0x0f); byte[] varHeader = getVariableHeader(); int remLen = varHeader.length + getPayload().length;//長度爲0 ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); dos.writeByte(first);//寫入一個字節 dos.write(encodeMBI(remLen));//查看encodeMBI()方法實現可知此處也是隻有一個字節 dos.write(varHeader);//new byte[0] dos.flush(); return baos.toByteArray(); } catch(IOException ioe) { throw new MqttException(ioe); } }
protected static byte[] encodeMBI( long number) { int numBytes = 0; long no = number; ByteArrayOutputStream bos = new ByteArrayOutputStream(); // Encode the remaining length fields in the four bytes do { byte digit = (byte)(no % 128); no = no / 128; if (no > 0) { digit |= 0x80; } bos.write(digit); numBytes++; } while ( (no > 0) && (numBytes<4) ); return bos.toByteArray(); }
而MqttWireMessage中還有一個getPayload方法,MqttPingReq和MqttPingResp都沒有重寫這個方法:spa
/** * Sub-classes should override this method to supply the payload bytes. */ public byte[] getPayload() throws MqttException { return new byte[0]; }
綜合以上分析可知MQTT的心跳包實際只有2個字節,且第一個字節中前4位表示消息類型後4位表示消息內容。code