MQTT 是一个客户端-服务器发布/订阅消息传输协议. 它轻量，开放，简单, 并易于实现.这些特性，使它在许多情况下适合使用, 包括受限的环境中,如机器对机器(M2M)之间通信和物联网(IoT)环境中,需要体积小的代码和/或网络带宽是昂贵的。
此协议运行于TCP/IP以上, 或在其他网络协议，提供有序、 无损、 双向网络连接. 它的功能包括:
· 有三种消息传递服务质量 ：
· “At most once”, 至多一次”,消息传送根据操作环境的最好状态。会发生消息丢失。 这一级别可用于如下情况,如环境传感器数据,这种情况下,丢失一次读记录无所谓,因为第二个 数据的发布紧跟其后。
· “At least once” “至少一次”,确保消息到达,但可能发生消息重复。
· “Exactly once”, “只有一次”,确保消息只到达一次。这一级别可用于如下情况,如计费系统中,消息重复或丢失会导致不正确的收费问题。
· 提供一种机制,使得发生异常中断时,能够通知有关各方 。
MQTT协议通过网络为应用程序携带的数据。 当应用程序消息通过 MQTT 传输他们有关联的服务质量和主题名称。
使用 MQTT 的程序或设备。客户端总是建立到服务器的网络连接。它能
MQTT Control Packet:（MQTT 控制包）
在网络连接上发送的信息包。MQTT 规范定义了十四个不同类型的控制包，其中之一 （PUBLISH packet发布包） 用来发送应用程序消息。
RESERVED = 0x00 保留
CONNECT = 0x01 连接请求:客户端请求连接到服务器
CONACK = 0x02 连接确认
PUBLISH = 0x03 发布消息
PUBACK = 0x04 发布确认
PUBREC = 0x05 发布信息收到(确保分发的第1部分)
PUBREL = 0x06 发布信息分发(确保分发的第2部分);
PUBCOMP = 0x07 发布完成(确保分发的第3部分);
SUBSCRIBE = 0x08 客户端订阅请求
SUBACK = 0x09 订阅确认
UNSUBSCRIBE = 0x0a 客户端取消订阅请求
UNSUBACK = 0x0b 取消订阅确认
PINGREQ = 0x0c ping请求
PINGRESP = 0x0d ping响应
DISCONNECT = 0x0e 客户端正在断开连接
RESERVED = 0x0f 保留
每个客户端都要订阅一个 遗嘱消息 ,如客户端 1,订阅last will 主题为qq/1 ,如客户端 2,订阅last will 主题为qq/2;接收客户端
订阅last will 主题为qq/#,就能接收到所有客户端的非正常断开. 要和retain消息一起使用,以便存储客户端状态.
- 客户端在Keep Alive时间内通信失败。
Clean Session 持久连接 针对连接 (qos1 &qos2 设为0) （）
通常，客户端应该一直使用CleanSession 0或一直使用CleanSession 1来建立连接，而不要切换来切换去。选择哪个由应用程序决定。使用CleanSession 1的客户端不会收到旧的应用消息，而且每次连接都要重新订阅感兴趣的话题。客户端使用CleanSession 0会收到上次短线之后的QoS 1和QoS 2的消息。因此，为了确保消息不会再断线时丢失，使用QoS 1或QoS 2并把CleanSession设为0
Position: bit 1 of the Connect Flags byte.
This bit specifies the handling of the Session state.
The Client and Server can store Session state to enable reliable messaging to continue across a sequence of Network Connections. This bit is used to control the lifetime of the Session state.
If CleanSession is set to 0, the Server MUST resume communications with the Client based on state from the current Session (as identified by the Client identifier). If there is no Session associated with the Client identifier the Server MUST create a new Session. The Client and Server MUST store the Session after the Client and Server are disconnected [MQTT-3.1.2-4]. After the disconnection of a Session that had CleanSession set to 0, the Server MUST store further QoS 1 and QoS 2 messages that match any subscriptions that the client had at the time of disconnection as part of the Session state [MQTT-3.1.2-5]. It MAY also store QoS 0 messages that meet the same criteria.
If CleanSession is set to 1, the Client and Server MUST discard any previous Session and start a new one. This Session lasts as long as the Network Connection. State data associated with this Session MUST NOT be reused in any subsequent Session [MQTT-3.1.2-6].
The Session state in the Client consists of:
· QoS 1 and QoS 2 messages which have been sent to the Server, but have not been completely acknowledged.
· QoS 2 messages which have been received from the Server, but have not been completely acknowledged.
The Session state in the Server consists of:
· The existence of a Session, even if the rest of the Session state is empty.
· The Client’s subscriptions.
· QoS 1 and QoS 2 messages which have been sent to the Client, but have not been completely acknowledged.
· QoS 1 and QoS 2 messages pending transmission to the Client.
· QoS 2 messages which have been received from the Client, but have not been completely acknowledged.
· Optionally, QoS 0 messages pending transmission to the Client.
Retained messages do not form part of the Session state in the Server, they MUST NOT be deleted when the Session ends [MQTT-184.108.40.206].
See Section 4.1 for details and limitations of stored state.
When CleanSession is set to 1 the Client and Server need not process the deletion of state atomically.
Non normative comment
To ensure consistent state in the event of a failure, the Client should repeat its attempts to connect with CleanSession set to 1, until it connects successfully.
Non normative comment
Typically, a Client will always connect using CleanSession set to 0 or CleanSession set to 1 and not swap between the two values. The choice will depend on the application. A Client using CleanSession set to 1 will not receive old Application Messages and has to subscribe afresh to any topics that it is interested in each time it connects. A Client using CleanSession set to 0 will receive all QoS 1 or QoS 2 messages that were published while it was disconnected. Hence, to ensure that you do not lose messages while disconnected, use QoS 1 or QoS 2 with CleanSession set to 0.
Non normative comment
When a Client connects with CleanSession set to 0, it is requesting that the Server maintain its MQTT session state after it disconnects. Clients should only connect with CleanSession set to 0, if they intend to reconnect to the Server at some later point in time. When a Client has determined that it has no further use for the session it should do a final connect with CleanSession set to 1 and then disconnect.
备注：新来乍到的订阅者，只会取出最新的一个RETAIN flag = 1的消息推送。
假如服务器收到一个空消息体(zero-length payload)、RETAIN = 1、已存在Topic name的PUBLISH消息，服务器可以删除掉对应的已被持久化的PUBLISH消息。*/
Position: byte 1, bit 0.
If the RETAIN flag is set to 1, in a PUBLISH Packet sent by a Client to a Server, the Server MUST store the Application Message and its QoS, so that it can be delivered to future subscribers whose subscriptions match its topic name[MQTT-3.3.1-5]. When a new subscription is established, the last retained message, if any, on each matching topic name MUST be sent to the subscriber [MQTT-3.3.1-6]. If the Server receives a QoS 0 message with the RETAIN flag set to 1 it MUST discard any message previously retained for that topic. It SHOULD store the new QoS 0 message as the new retained message for that topic, but MAY choose to discard it at any time – if this happens there will be no retained message for that topic [MQTT-3.3.1-7]. See Section 4.1 for more information on storing state.
When sending a PUBLISH Packet to a Client the Server MUST set the RETAIN flag to 1 if a message is sent as a result of a new subscription being made by a Client [MQTT-3.3.1-8]. It MUST set the RETAIN flag to 0 when a PUBLISH Packet is sent to a Client because it matches an established subscription regardless of how the flag was set in the message it received [MQTT-3.3.1-9].
A PUBLISH Packet with a RETAIN flag set to 1 and a payload containing zero bytes will be processed as normal by the Server and sent to Clients with a subscription matching the topic name. Additionally any existing retained message with the same topic name MUST be removed and any future subscribers for the topic will not receive a retained message [MQTT-3.3.1-10]. “As normal” means that the RETAIN flag is not set in the message received by existing Clients. A zero byte retained message MUST NOT be stored as a retained message on the Server [MQTT-3.3.1-11].
If the RETAIN flag is 0, in a PUBLISH Packet sent by a Client to a Server, the Server MUST NOT store the message and MUST NOT remove or replace any existing retained message [MQTT-3.3.1-12].
Non normative comment
Retained messages are useful where publishers send state messages on an irregular basis. A new subscriber will receive the most recent state.
MQTT 定义了3个级别的QoS(服务质量)。消息可能被用任何QoS级别发送，客户端也可能尝试用任何QoS级别去订阅。这意味着发布者和订阅者当使用不同的QoS时，客户端总是选择将收到的最大的QoS。例如：一个消息被发布为QoS 2 ，订阅时为QoS 0, 那么此消息将被作为QoS 0发送。如果第二个客户端 订阅了相同的主题，但是用了QoS 2，那么它会收到相同的消息但是用的是QoS 2.第二个例子，如果一个客户端 用QoS 2订阅，但是一个消息是用QoS 0发布，客户端会收到的它用QoS 0.
qos0: 最多一次 服务器与 客户端 交互1次 。
qos1 :至少一次 服务器与 客户端 交互2次 。
qos2:洽好一次 服务器与 客户端 交互4次 。
重复发送一个 PUBLISH Control Packet
Position: byte 1, bit 3.
If the DUP flag is set to 0, it indicates that this is the first occasion that the Client or Server has attempted to send this MQTT PUBLISH Packet. If the DUP flag is set to 1, it indicates that this might be re-delivery of an earlier attempt to send the Packet.
The DUP flag MUST be set to 1 by the Client or Server when it attempts to re-deliver a PUBLISH Packet [MQTT-3.3.1.-1]. The DUP flag MUST be set to 0 for all QoS 0 messages [MQTT-3.3.1-2].
The value of the DUP flag from an incoming PUBLISH packet is not propagated when the PUBLISH Packet is sent to subscribers by the Server. The DUP flag in the outgoing PUBLISH packet is set independently to the incoming PUBLISH packet, its value MUST be determined solely by whether the outgoing PUBLISH packet is a retransmission [MQTT-3.3.1-3].
Non normative comment
The recipient of a Control Packet that contains the DUP flag set to 1 cannot assume that it has seen an earlier copy of this packet.
Non normative comment
It is important to note that the DUP flag refers to the Control Packet itself and not to the Application Message that it contains. When using QoS 1, it is possible for a Client to receive a PUBLISH Packet with DUP flag set to 0 that contains a repetition of an Application Message that it received earlier, but with a different Packet Identifier. Section 2.3.1 provides more information about Packet Identifiers.
Figure 3.5 Keep Alive bytes
|byte 9||Keep Alive MSB|
|byte 10||Keep Alive LSB|
The Keep Alive is a time interval measured in seconds. Expressed as a 16-bit word, it is the maximum time interval that is permitted to elapse between the point at which the Client finishes transmitting one Control Packet and the point it starts sending the next. It is the responsibility of the Client to ensure that the interval between Control Packets being sent does not exceed the Keep Alive value. In the absence of sending any other Control Packets, the Client MUST send a PINGREQ Packet [MQTT-3.1.2-23].
The Client can send PINGREQ at any time, irrespective of the Keep Alive value, and use the PINGRESP to determine that the network and the Server are working.
If the Keep Alive value is non-zero and the Server does not receive a Control Packet from the Client within one and a half times the Keep Alive time period, it MUST disconnect the Network Connection to the Client as if the network had failed [MQTT-3.1.2-24].
If a Client does not receive a PINGRESP Packet within a reasonable amount of time after it has sent a PINGREQ, it SHOULD close the Network Connection to the Server.
A Keep Alive value of zero (0) has the effect of turning off the keep alive mechanism. This means that, in this case, the Server is not required to disconnect the Client on the grounds of inactivity.
Note that a Server is permitted to disconnect a Client that it determines to be inactive or non-responsive at any time, regardless of the Keep Alive value provided by that Client.
Non normative comment
The actual value of the Keep Alive is application specific; typically this is a few minutes. The maximum value is 18 hours 12 minutes and 15 seconds.
相同clients id的客户端 不能同时连接
The Client Identifier (ClientId) identifies the Client to the Server. Each Client connecting to the Server has a unique ClientId. The ClientId MUST be used by Clients and by Servers to identify state that they hold relating to this MQTT Session between the Client and the Server [MQTT-3.1.3-2].
The Client Identifier (ClientId) MUST be present and MUST be the first field in the CONNECT packet payload [MQTT-3.1.3-3].
The ClientId MUST be a UTF-8 encoded string as defined in Section 1.5.3 [MQTT-3.1.3-4].
The Server MUST allow ClientIds which are between 1 and 23 UTF-8 encoded bytes in length, and that contain only the characters
The Server MAY allow ClientId’s that contain more than 23 encoded bytes. The Server MAY allow ClientId’s that contain characters not included in the list given above.
A Server MAY allow a Client to supply a ClientId that has a length of zero bytes, however if it does so the Server MUST treat this as a special case and assign a unique ClientId to that Client. It MUST then process the CONNECT packet as if the Client had provided that unique ClientId [MQTT-3.1.3-6].
If the Client supplies a zero-byte ClientId, the Client MUST also set CleanSession to 1 [MQTT-3.1.3-7].
If the Client supplies a zero-byte ClientId with CleanSession set to 0, the Server MUST respond to the CONNECT Packet with a CONNACK return code 0x02 (Identifier rejected) and then close the Network Connection [MQTT-3.1.3-8].
If the Server rejects the ClientId it MUST respond to the CONNECT Packet with a CONNACK return code 0x02 (Identifier rejected) and then close the Network Connection [MQTT-3.1.3-9].
Non normative comment
A Client implementation could provide a convenience method to generate a random ClientId. Use of such a method should be actively discouraged when the CleanSession is set to 0.
Packet Identifier （包标识符） ( 以前叫Message Identifier)
the max value is 65535
Figure 2.3 – Packet Identifier bytes
|byte 1||Packet Identifier MSB|
|byte 2||Packet Identifier LSB|
The variable header component of many of the Control Packet types includes a 2 byte Packet Identifier field. These Control Packets are PUBLISH (where QoS > 0), PUBACK, PUBREC, PUBREL, PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK.
SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier [MQTT-2.3.1-1]. Each time a Client sends a new packet of one of these types it MUST assign it a currently unused Packet Identifier [MQTT-2.3.1-2]. If a Client re-sends a particular Control Packet, then it MUST use the same Packet Identifier in subsequent re-sends of that packet. The Packet Identifier becomes available for reuse after the Client has processed the corresponding acknowledgement packet. In the case of a QoS 1 PUBLISH this is the corresponding PUBACK; in the case of QoS 2 it is PUBCOMP. For SUBSCRIBE or UNSUBSCRIBE it is the corresponding SUBACK or UNSUBACK [MQTT-2.3.1-3]. The same conditions apply to a Server when it sends a PUBLISH with QoS > 0 [MQTT-2.3.1-4].
订阅和取消订阅，发布 （在情况下，QoS > 0) 控制数据包必须包含一个非零 16 位包标识符 [MQTT-2.3.1-1]。每次客户端发送新的数据包，这些类型之一的它必须分配一个当前未使用的包标识符 [MQTT-2.3.1-2]。如果客户端重新发送一个特定的控制数据包，那么它必须使用相同的包标识符在随后重新发送该数据包。以后客户端已处理相应的确认数据包，数据包标识符成为可供重用。在 QoS 1 发布的情况下，这是相应的 PUBACK ；在 QoS 2 的情况下，它是 PUBCOMP。订阅或取消订阅它是相应的 SUBACK 或 UNSUBACK [MQTT-2.3.1-3]。同样的条件适用于服务器时它会发送 qos 发布 > 0 [MQTT-2.3.1-4]。
A PUBLISH Packet MUST NOT contain a Packet Identifier if its QoS value is set to 0 [MQTT-2.3.1-5].发布数据包必须不包含数据包标识符如果其 QoS 值设置为 0
A PUBACK, PUBREC or PUBREL Packet MUST contain the same Packet Identifier as the PUBLISH Packet that was originally sent [MQTT-2.3.1-6]. Similarly SUBACK and UNSUBACK MUST contain the Packet Identifier that was used in the corresponding SUBSCRIBE and UNSUBSCRIBE Packet respectively [MQTT-2.3.1-7].PUBACK、 PUBREC 或 PUBREL 数据包必须包含相同的包标识符作为原先发送[MQTT-2.3.1-6] 发布包。同样 SUBACK 和 UNSUBACK 必须包含包标识符使用的相应订阅和取消订阅包分别 [MQTT-2.3.1-7]。
Control Packets that require a Packet Identifier are listed in Table 2.5 – Control Packets that contain a Packet Identifier.
Table 2.5 – Control Packets that contain a Packet Identifier(控制数据包包含的包标识符)
|Control Packet||Packet Identifier field|
|PUBLISH||YES (If QoS > 0)|
The Client and Server assign Packet Identifiers independently of each other. As a result, Client Server pairs can participate in concurrent message exchanges using the same Packet Identifiers.
Non normative comment
It is possible for a Client to send a PUBLISH Packet with Packet Identifier 0x1234 and then receive a different PUBLISH with Packet Identifier 0x1234 from its Server before it receives a PUBACK for the PUBLISH that it sent.
PUBLISH Packet Identifier=0x1234—à
ß–PUBLISH Packet Identifier=0x1234
PUBACK Packet Identifier=0x1234—à
ß–PUBACK Packet Identifier=0x1234
Probably the first point to make clear is that message IDs are handled on a per client and per direction basis. That is to say that the broker will create a message ID for each outgoing message with QoS>0 for each client that is connected and these message IDs will be completely independent of any other message IDs used for the same message published to other clients. Likewise, each client generates its own message IDs for messages that it sends.
The message ID doesn’t have to be unique, so your client sending 15 messages per hour with QoS level 2 would simply overflow at some point. The real limitation is that there can only be a maximum of 65535 messages per direction “in flight” at once (i.e. part way through the message handshake). Once a message with a given ID has been fully processed then that message ID can be reused.
Another way of looking at it is to consider how it would work if your client only ever had one message in flight at once, whether because of the rate the messages are being transmitted or by design in the way you handle the messages. In this case, you could keep message ID set to 1 for every single message because there is never a chance that there will be a duplicate.
If you wish to support having multiple messages in flight at once it would be relatively straightforward to check there are no message ID duplicates before you assign a new one.
Because the message ID is per client, if you send a single message to >65535 clients there will be no chance of message ID collisions. If you send >65535 messages to each client at once and the message flows aren’t complete then there will be problems.
Answering the comment “I have noticed that every MQTT broker tends to deliver only the last QoS1/2 message”:
The broker will only send messages to clients it knows about. If you connect for the first time there is no way to get messages from the past, with one exception: retained messages. If a message is set to retained then it is a “last known good” value. When a new client subscribes it will be sent the retained message immediately which makes it useful for things that are updated infrequently. I suspect this is what you are referring to. If you want a client to have messages queued when it is not connected then you must connect with the “clean session” option disabled to make the client persistent. You must also use QoS>0 subscriptions and QoS>0 publications. When your client reconnects (with clean session still set to disabled), the queued messages will be delivered. You can normally configure the number of messages to queue in this way in the broker, where any further messages will be discarded. An important point is that queueing messages for a client that has not previously connected is not supported by design.
MQTT PUBLISH messages with QoS 1 or 2 require a message id as part of the packet. The message id is used to identify which message a PUBACK (or PUBREC/PUBREL/PUBCOMP for QoS 2) is referring to. This is an important feature because you may have multiple messages “in flight” at once.
An important point that you may be missing is that clients are completely separate from one another. This means that message ids are unique to a client (and direction of message flow, broker to client or client to broker). The broker generates message ids for messages originating from the broker and the client generates message ids for messages originating from the client; the message ids are independent for each direction so that there is no need for the broker and the client to keep track of what the other is doing.
If you want to keep track of which incoming messages have been sent to all subscribing clients, you will need to keep track of what outgoing messages relate to the incoming message and only trigger your DB once all of the PUBACKs have been received for those outgoing messages. That will tell you which messages have successfully been sent to all clients that were subscribed at the time of the message being received.
If you just want a log of all messages that have been sent to the broker and can assume that the sending works ok, then life is a lot easier. Simply create a client on the broker host that listens to the “#” topic, or whatever you are interested in, then use the client on_message() callback (or however your library manages it) to process the message and store it in the DB.
see also http://mosquitto.org/man/mqtt-7.html , http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html
Persistent session on the client side
Similar to the broker, each MQTT client must store a persistent session too. So when a client requests the server to hold session data, it also has the responsibility to hold some information by itself:
All messages in a QoS 1 or 2 flow, which are not confirmed by the broker
All received QoS 2 messages, which are not yet confirmed to the broker
The client should resume all QoS 1 and 2 publish messages after a reconnect.
A client is not subscribing, but only publishing messages to topics. It doesn’t need any session information to be stored on the broker and publishing messages with QoS 1 and 2 should not be retried.
A client should explicitly not get messages for the time it is offline.
lastwill(一般订阅时用）Last-Will Topic,Last-Will QoS,Last-Will Retain,Last-Will Messsage
Clean Session针对连接 (qos1 &qos2 设为0
broker 的 msgid 自增？
客户端 订阅 主题‘#’ ，可以监控 所有客户端 的 接收情况？
客户端每断开一次链接，broker 返回的msgid要重置（mosquitto） ，emqtt貌似不会
测试程序是否符合 mqtt 3.1.1规范 http://www.eclipse.org/paho/clients/testing/
转载请注明：来自Lenix的博客 http://blog.p2hp.com/ http://blog.p2hp.com/archives/1528