目标
学习接收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);
}
}