int mqtt_connect(mqtt_broker_handle_t* broker)
{
uint8_t flags = 0x00;
uint16_t clientidlen = strlen(broker->clientid);
uint16_t usernamelen = strlen(broker->username);
uint16_t passwordlen = strlen(broker->password);
uint16_t payload_len = clientidlen + 2;
// Preparing the flags
if(usernamelen) {
payload_len += usernamelen + 2;
flags |= MQTT_USERNAME_FLAG;
}
if(passwordlen) {
payload_len += passwordlen + 2;
flags |= MQTT_PASSWORD_FLAG;
}
if(broker->clean_session) {
flags |= MQTT_CLEAN_SESSION;
}
// Variable header
uint8_t var_header[] = {
0x00,0x06,0x4d,0x51,0x49,0x73,0x64,0x70, // Protocol name: MQIsdp
0x03, // Protocol version
flags, // Connect flags
broker->alive>>8, broker->alive&0xFF, // Keep alive
};
// Fixed header
uint8_t fixedHeaderSize = 2; // Default size = one byte Message Type + one byte Remaining Length
uint8_t remainLen = sizeof(var_header)+payload_len;
if (remainLen > 127) {
fixedHeaderSize++; // add an additional byte for Remaining Length
}
uint8_t fixed_header[fixedHeaderSize];
// Message Type
fixed_header[0] = MQTT_MSG_CONNECT;
// Remaining Length
if (remainLen <= 127) {
fixed_header[1] = remainLen;
} else {
// first byte is remainder (mod) of 128, then set the MSB to indicate more bytes
fixed_header[1] = remainLen % 128;
fixed_header[1] = fixed_header[1] | 0x80;
// second byte is number of 128s
fixed_header[2] = remainLen / 128;
}
uint16_t offset = 0;
uint8_t packet[sizeof(fixed_header)+sizeof(var_header)+payload_len];
memset(packet, 0, sizeof(packet));
memcpy(packet, fixed_header, sizeof(fixed_header));
offset += sizeof(fixed_header);
memcpy(packet+offset, var_header, sizeof(var_header));
offset += sizeof(var_header);
// Client ID - UTF encoded
packet[offset++] = clientidlen>>8;
packet[offset++] = clientidlen&0xFF;
memcpy(packet+offset, broker->clientid, clientidlen);
offset += clientidlen;
if(usernamelen) {
// Username - UTF encoded
packet[offset++] = usernamelen>>8;
packet[offset++] = usernamelen&0xFF;
memcpy(packet+offset, broker->username, usernamelen);
offset += usernamelen;
}
if(passwordlen) {
// Password - UTF encoded
packet[offset++] = passwordlen>>8;
packet[offset++] = passwordlen&0xFF;
memcpy(packet+offset, broker->password, passwordlen);
offset += passwordlen;
}
// Send the packet
if(broker->send(broker->socket_info, packet, sizeof(packet)) < sizeof(packet)) {
return -1;
}
return 1;
}
首先是fixed header,fixed header为两字节,按照协议来说第一个字节应该是0x10,第二字节是剩余数据长度,也就是remain length。然后是variable header,variable header定义如下:
uint8_t var_header[] = {
0x00,0x06,0x4d,0x51,0x49,0x73,0x64,0x70, // Protocol name: MQIsdp
0x03, // Protocol version
flags, // Connect flags
broker->alive>>8, broker->alive&0xFF, // Keep alive
};
首先是协议的名字,长度为6个字节,协议名字这里为"MQIsdp",同mqtt v3.1.1协议上有点区别,在v3.1.1上是"MQTT"。然后是协议版本和flags,这里协议版本是0x03。最后是keep alive,keep alive这里为30。
int read_packet(int timeout)
{
if(timeout > 0)
{
fd_set readfds;
struct timeval tmv;
// Initialize the file descriptor set
FD_ZERO (&readfds);
FD_SET (socket_id, &readfds);
// Initialize the timeout data structure
tmv.tv_sec = timeout;
tmv.tv_usec = 0;
// select returns 0 if timeout, 1 if input available, -1 if error
if(select(1, &readfds, NULL, NULL, &tmv))
return -2;
}
int total_bytes = 0, bytes_rcvd, packet_length;
memset(packet_buffer, 0, sizeof(packet_buffer));
if((bytes_rcvd = recv(socket_id, (packet_buffer+total_bytes), RCVBUFSIZE, 0)) <= 0) {
return -1;
}
total_bytes += bytes_rcvd; // Keep tally of total bytes
if (total_bytes < 2)
return -1;
// now we have the full fixed header in packet_buffer
// parse it for remaining length and number of bytes
uint16_t rem_len = mqtt_parse_rem_len(packet_buffer);
uint8_t rem_len_bytes = mqtt_num_rem_len_bytes(packet_buffer);
//packet_length = packet_buffer[1] + 2; // Remaining length + fixed header length
// total packet length = remaining length + byte 1 of fixed header + remaning length part of fixed header
packet_length = rem_len + rem_len_bytes + 1;
while(total_bytes < packet_length) // Reading the packet
{
if((bytes_rcvd = recv(socket_id, (packet_buffer+total_bytes), RCVBUFSIZE, 0)) <= 0)
return -1;
total_bytes += bytes_rcvd; // Keep tally of total bytes
}
return packet_length;
}
这个函数主要是从服务器端读取数据的,核心是socket的recv函数。首先是超时处理,这里使用的是select系统调用,然后调用recv接收数据,解析出remain length和remain length所占字节数,如果还有数据未接收完毕,继续调用recv函数,最终数据是读取到全局变量packet_buffer中的,并返回读取的数据长度。
int mqtt_subscribe(mqtt_broker_handle_t* broker, const char* topic, uint16_t* message_id) {
uint16_t topiclen = strlen(topic);
// Variable header
uint8_t var_header[2]; // Message ID
var_header[0] = broker->seq>>8;
var_header[1] = broker->seq&0xFF;
if(message_id) { // Returning message id
*message_id = broker->seq;
}
broker->seq++;
// utf topic
uint8_t utf_topic[topiclen+3]; // Topic size (2 bytes), utf-encoded topic, QoS byte
memset(utf_topic, 0, sizeof(utf_topic));
utf_topic[0] = topiclen>>8;
utf_topic[1] = topiclen&0xFF;
memcpy(utf_topic+2, topic, topiclen);
// Fixed header
uint8_t fixed_header[] = {
MQTT_MSG_SUBSCRIBE | MQTT_QOS1_FLAG, // Message Type, DUP flag, QoS level, Retain
sizeof(var_header)+sizeof(utf_topic)
};
uint8_t packet[sizeof(var_header)+sizeof(fixed_header)+sizeof(utf_topic)];
memset(packet, 0, sizeof(packet));
memcpy(packet, fixed_header, sizeof(fixed_header));
memcpy(packet+sizeof(fixed_header), var_header, sizeof(var_header));
memcpy(packet+sizeof(fixed_header)+sizeof(var_header), utf_topic, sizeof(utf_topic));
// Send the packet
if(broker->send(broker->socket_info, packet, sizeof(packet)) < sizeof(packet)) {
return -1;
}
return 1;
}
协议上说了fixed header第一字节为0x82,第二字节为remain length。
while(1)
{
// <<<<<
packet_length = read_packet(0);
if(packet_length == -1)
{
fprintf(stderr, "Error(%d) on read packet!\n", packet_length);
return -1;
}
else if(packet_length > 0)
{
printf("Packet Header: 0x%x...\n", packet_buffer[0]);
if(MQTTParseMessageType(packet_buffer) == MQTT_MSG_PUBLISH)
{
uint8_t topic[255], msg[1000];
uint16_t len;
len = mqtt_parse_pub_topic(packet_buffer, topic);
topic[len] = '\0'; // for printf
len = mqtt_parse_publish_msg(packet_buffer, msg);
msg[len] = '\0'; // for printf
printf("%s %s\n", topic, msg);
}
}
}
int mqtt_disconnect(mqtt_broker_handle_t* broker) {
uint8_t packet[] = {
MQTT_MSG_DISCONNECT, // Message Type, DUP flag, QoS level, Retain
0x00 // Remaining length
};
// Send the packet
if(broker->send(broker->socket_info, packet, sizeof(packet)) < sizeof(packet)) {
return -1;
}
return 1;
}
根据协议来看,这个消息相对比较简单,只有fixed header,第一字节为0xe0,第二字节为0x00。