MQTT之CONNECT报文和CONNACK报文
1、CONNECT报文的固定报头
由上表可知:“CONNECT报文的固定报头”第1个字节的“高4位”表示“CONNECT报文的类型”,“低4位”为保留位,固定为0;“CONNECT报文的固定报头”从第2个字节开始就是“剩余长度”,可见这个剩余长度的字节数是可变的。注意:剩余长度的字节数最大为4,最少为1。
CONNECT报文的固定包头
typedef union
{
unsigned char byte; /**< the whole byte */
struct
{
unsigned int retain : 1;
/*占bit0,在CONNECT报文的固定包头中是保留位 */
unsigned int qos : 2;
/*占bit2:1, 在CONNECT报文的固定包头中是保留位 */
unsigned int dup : 1;
/*占bit3, 在CONNECT报文的固定包头中是保留位 */
unsigned int type : 4;
/*占bit7:4,MQTT头字节高4位,表示MQTT控制报文的类型*/
} bits;
} MQTTHeader;
2、CONNECT报文的可变报头
CONNECT报文的可变报头按下列次序,包含四个字段:协议名(Protocol Name),协议级别(Protocol Level),连接标志(Connect Flags)和保持连接(Keep Alive)。
2.1、协议名字段
由上表可知:协议名字段占用“CONNECT报文的可变报头”中的字节1和字节6,协议名为“MQTT”,占4个字节,协议名的长度为0x0004,占用2个字节。
2.2、协议级别
由上表可知:“协议级别”占用“CONNECT报文的可变报头”中的字节7,上表是3.1.1版协议,协议级别字段的值是0x04。
2.3、连接标志
由上表可知:“连接标志”占用“CONNECT报文的可变报头”中的字节8。
bit7=1表示“用户名标志”置1,则“有效载荷”中必须包含“用户名字段”;
bit6=1表示“密码标志”置1,则 “有效载荷”中必须包含“密码字段”;
bit5=1表示“遗嘱保留(Will Retain)标志”置1,则服务端必须将“遗嘱消息”当作“保留消息”发布;
bit4:3=00表示“遗嘱QoS0”; bit4:3=01表示“遗嘱QoS1”; bit4:3=10表示“遗嘱QoS2”;
bit2=0表示如果“遗嘱标志”被设置为0,则“遗嘱保留(Will Retain)标志”也必须设置为0;
bit2=1表示如果遗嘱标志被设置为1,连接标志中的“遗嘱QoS”和“遗嘱保留(Will Retain)”字段会被服务端用到,同时有效载荷中必须包含“Will Topic”和“Will Message”字段。
bit1=1表示“清理会话(CleanSession)标志”置1,客户端和服务端必须丢弃之前的任何会话,并开始一个新的会话。
bit0=0,为保留位。
连接标志字节定义如下:
typedef union
{
unsigned char all; /**< all connect flags */
struct
{
unsigned int : 1; /*为保留位*/
unsigned int cleansession : 1;
/*bit1=1表示“清理会话(CleanSession)标志”置1,客户端和服务端必须丢
弃之前的任何会话,并开始一个新的会话。cleansession flag */
unsigned int will : 1;
/*bit2=0表示如果“遗嘱标志”被设置为0,则“遗嘱保留(Will Retain)标志”也必须设置为0;
bit2=1表示如果遗嘱标志被设置为1,连接标志中的“遗嘱QoS”和“遗嘱保留(Will Retain)”字段会被服务端用到,同时有效载荷中必须包含“Will Topic”和“Will Message”字段。
will flag */
unsigned int willQoS : 2;
/*bit4:3=00表示“遗嘱QoS0”;
bit4:3=01表示“遗嘱QoS1”;
bit4:3=10表示“遗嘱QoS2”;
will QoS value */
unsigned int willRetain : 1;
/*bit5=1表示“遗嘱保留(Will Retain)标志”置1,则服务端必须将
“遗嘱消息”当作“保留消息”发布;will retain setting */
unsigned int password : 1;
/*bit6=1表示“密码标志”置1,则 “有效载荷”中必须包含“密码
字段”;3.1 password */
unsigned int username : 1;
/*bit7=1表示“用户名标志”置1,则“有效载荷”中必须包含“用户名字段”;
3.1 user name */
} bits;
} MQTTConnectFlags; /**< connect flags byte */
2.4、保持连接时间
保持连接(Keep Alive)是一个以秒为单位的时间间隔,表示为一个16位的字,它是指在客户端传输完成一个控制报文的时刻到发送下一个报文的时刻,两者之间允许空闲的最大时间间隔。客户端负责保证控制报文发送的时间间隔不超过保持连接的值。如果没有任何其它的控制报文可以发送,客户端必须发送一个PINGREQ报文。
不管“保持连接的值”是多少,客户端在任何时候都可以发送PINGREQ报文,并且使用PINGRESP报文判断网络和服务端的活动状态。
如果保持连接时间非零,并且服务端在“1.5倍的保持连接时间”内没有收到客户端的控制报文,它必须断开客户端的网络连接,认为网络连接已断开。
3、有效载荷
“CONNECT报文”的“有效载荷(payload)”包含多个“以长度为前缀”的字段,这个字段的出现顺序:客户端标识符(必须出现),遗嘱主题,遗嘱消息,用户名,密码。
有些字段的出现是由“可变报头”中的“连接标志(Connect Flags)”决定是否包含这些字段。
MQTTConnectFlags ConnectFlags;//声明“连接标志”结构变量ConnectFlags
ConnectFlags. bits.will=1,“遗嘱主题”和“遗嘱消息”会出现在“有效载荷”中。
ConnectFlags. bits.username=1,“用户名”会出现在“有效载荷”中。
ConnectFlags. bits.password=1,“密码”会出现在“有效载荷”中。
3.1、客户端标识符
“客户端标识符 (ClientId) ”是CONNECT报文有效载荷的第一个字段。服务端必须允许1到23个字节长的UTF-8编码的客户端标识符,客户端标识符只能包含这些字符:“0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ”(大写字母,小写字母和数字)。
3.2、遗嘱主题
“客户端标识符”的后面的字段是“遗嘱主题”。“可变报头”中的“连接标志(Connect Flags)”中,如果ConnectFlags. bits.will=1,“遗嘱主题”必须出现在“有效载荷”中。
3.3、遗嘱消息
“遗嘱主题”的后面的字段是“遗嘱消息”。“可变报头”中的“连接标志(Connect Flags)”中,如果ConnectFlags. bits.will=1,“遗嘱消息”必须出现在“有效载荷”中。
3.4、用户名
“遗嘱消息”的后面的字段是“用户名”。“可变报头”中的“连接标志(Connect Flags)”中,如果ConnectFlags. bits. username =1,“用户名”必须出现在“有效载荷”中。
3.5、密码
“用户名”的后面的字段是“密码”。“可变报头”中的“连接标志(Connect Flags)”中,如果ConnectFlags. bits. password =1,“密码”必须出现在“有效载荷”中。
4、“可变报头”结构体
4.1、MQTTString结构体
typedef struct
{
int len; //非"字符串"的有效字节总数
char* data; //指向非"字符串"的首地址
} MQTTLenString;
typedef struct
{
char* cstring;//用来指向"字符串"
MQTTLenString lenstring;//用来指向非"字符串"数据
} MQTTString
4.2、遗嘱结构体
typedef struct
{
char struct_id[4];//结构ID必须为MQTW
int struct_version;//此结构的版本号,必须为0
MQTTString topicName; //遗嘱主题的结构指针
MQTTString message;//遗嘱消息内容的结构指针
unsigned char retained;//设置是否保留这个遗嘱消息
char qos;//遗嘱消息的qos,表示“消息的服务质量”
} MQTTPacket_willOptions;
4.3、CONNECT报文中的“可变报头”结构体
typedef struct
{
char struct_id[4];//保存的是"MQTC"
int struct_version;//此结构的版本号必须为0
unsigned char MQTTVersion;//MQTT的版本: 3 = 3.1; 4 = 3.1.1
MQTTString clientID;//定义“客户端标识符”clientID
unsigned short keepAliveInterval;//“保持连接的时间”是以秒为单位的时间
unsigned char cleansession;
//1表示新建一个临时会话,在客户端断开时,会话自动销毁
unsigned char willFlag;
/*遗嘱标志(Will Flag)被设置为1,表示如果连接请求被接受了,遗嘱“Will消息”必须被存储在服务端并且与这个网络连接关联*/
MQTTPacket_willOptions will;//“Will消息”结构变量
MQTTString username;//指向用户名
MQTTString password;//指向用户密码
} MQTTPacket_connectData;
MQTTPacket_connectData MyData;//声明MQTTPacket_connectData型结构变量
4.4、服务器网络参数结构体
typedef struct MQTT_Connection_Parameter_Type
{
char mqttHostUrl[35];
/*服务器URL地址,如:"mqtt.1234.com";由服务器设计人员告知*/
uint8_t server_ip[4]; /*服务器的IP地址,通过DNS解析得到IP地址;*/
int port; /*服务器端口号,如:61613;由服务器设计人员告知*/
char clientid[17]; /*客户端标识符如:"04661219C1676702"*/
char username[17]; /*登录服务器的用户名,如:"username";*/
char passwd[10]; /*登录服务器的用户密码,如:"passwd"*/
uint8_t MQTT_Connect_Flag; //为1,表示MQTT已经连接到服务器
uint8_t MQTT_Init_Steps; //MQTT初始化步骤计数器
}MQTT_Connection_Parameter_Def;
MQTT_Connection_Parameter_Def MQTT_Parameter;
4.6、MQTT包中的常用函数
/*
从(*pptr)所指向的存储单元读取一个字节,同时将存储单元的指针加1,并返回读到的数值
*/
char readChar(unsigned char** pptr)
{
char c;
c = **pptr;//通过指针从缓冲区读取一个字节
(*pptr)++;//修改指针值,为下次操作作准备
return c;
}
/*
从(*pptr)所指向的存储单元读取2个字节,同时将存储单元的指针加2,并返回读到的双字节数据
*/
int readInt(unsigned char** pptr)
{
unsigned char* ptr;
int len;
ptr = *pptr;
len = 256*(*ptr) + (*(ptr+1));
*pptr += 2;
return len;
}
/**
将c的值写入(*pptr)所指向的存储单元,同时将存储单元的指针加1
pptr指向输出缓冲区的指针
c是要写入的字符
*/
void writeChar(unsigned char** pptr, char c)
{
**pptr = c;
(*pptr)++;
}
/*
将双字节anInt的值写入(*pptr)所指向的存储单元,同时将存储单元的指针加2
*/
void writeInt(unsigned char** pptr, int anInt)
{
**pptr = (unsigned char)(anInt / 256);//保存高8位
(*pptr)++;
**pptr = (unsigned char)(anInt % 256);//保存低8位
(*pptr)++;
}
/*
添加字符串长度和字符串到(* pptr)所指向的存储区,并修改指针(* pptr)
*/
void writeCString(unsigned char** pptr, const char* string)
{
int len;
len = strlen(string);//求字符串长度
writeInt(pptr, len);//添加字符串的长度,并修改指针pptr
memcpy(*pptr, string, len);
//将string中的前len个字符拷贝到(*pptr)为首地址的存储区
*pptr += len;//并修改指针pptr
}
//函数功能:
//先保存长度,再拷贝字符串,长度占2个字节
//如果mqttstring.lenstring.len=0,将mqttstring.cstring所指向的字符串拷贝到*pptr所指向的存储单元
//如果mqttstring.lenstring.len>0,将mqttstring.lenstring.data所指向的字符串拷贝到*pptr所指向的存储单元
void writeMQTTString(unsigned char** pptr, MQTTString mqttstring)
{
if (mqttstring.lenstring.len > 0)//判断字符串长度是否大于0
{
writeInt(pptr, mqttstring.lenstring.len);
//添加mqttstring.lenstring.data的长度
memcpy(*pptr, mqttstring.lenstring.data, mqttstring.lenstring.len);
//添加mqttstring.lenstring.data所指向的字符串
*pptr += mqttstring.lenstring.len;//修改指针
}
else if (mqttstring.cstring)//判断字符串是否为空
writeCString(pptr, mqttstring.cstring);
else
writeInt(pptr, 0);
}
4.7、生成“CONNECT报文”
//MQTT_Parameter结构变量初始化
void MQTT_Parameter_Init(void)
{
strcpy(MQTT_Parameter.mqttHostUrl,” mqtt.1234.com”);
//服务器URL地址: MQTT_Parameter.mqttHostUrl[]="mqtt.1234.com "
memset(MQTT_Parameter.server_ip,0,sizeof(MQTT_Parameter.server_ip));
//连接服务器的IP: MQTT_Parameter.server_ip[]={0,0,0,0};
MQTT_Parameter.port=61613;//服务器端口号为61613
strcpy(MQTT_Parameter.clientid, "04661219C1676702");
//客户ID: MQTT_Parameter.clientid[]="04661219C1676702"
strcpy(MQTT_Parameter.username, "username");
//登录的用户名: MQTT_Parameter.username[]="username"
strcpy(MQTT_Parameter.passwd, "passwd");
//用户登录密码: MQTT_Parameter.passwd[]="passwd"
MQTT_Parameter.MQTT_Connect_Flag = 0; //0表示MQTT没有连接到服务器
}
MQTTPacket_connectData 型结构变量MyData初始化如下:
void MyData_Init(void)
{
MyData.struct_id[0]='M';MyData.struct_id[1]='Q';
MyData.struct_id[2]='T';MyData.struct_id[3]='C';//"MQTC"
MyData.struct_version=0;
MyData.MQTTVersion=4;//MQTT的版本: 3 = 3.1; 4 = 3.1.1
MyData.clientID.cstring=NULL;
MyData.clientID.lenstring.len=0;
MyData.clientID.lenstring.data=NULL;
MyData.keepAliveInterval=60;//60秒
MyData.cleansession=1;//1表示新建一个临时会话,在客户端断开时,会话自动销毁
//////“Will消息”配置开始//////
MyData.willFlag=0;
//将“遗嘱标志(Will Flag)”被设置为0,意思是告诉服务器不使用“Will消息”
//如果设置为0,连接标志中的will.qos和will.retained字段必须设置为0
//并且有效载荷中不能包含will.topicName和will.message字段
MyData.will.struct_id[0]='M';MyData.will.struct_id[1]='Q';
MyData.will.struct_id[2]='T';MyData.will.struct_id[3]='W';//MQTW
MyData.will.struct_version=3;//willdata结构的版本号
MyData.will.topicName.cstring=NULL; //因为MyData.willFlag=0,所以设置为NULL
MyData.will.topicName.lenstring.len=0;
MyData.will.topicName.lenstring.data=NULL;
MyData.will.message.cstring=NULL; //因为MyData.willFlag=0,所以设置为NULL
MyData.will.message.lenstring.len=0;
MyData.will.message.lenstring.data=NULL;
MyData.will.retained=0;//因为MyData.willFlag=0,所以设置为0
MyData.will.qos=0; //因为MyData.willFlag=0,所以设置为0
//////“Will消息”配置结束//////
MyData.clientID.cstring = MQTT_Parameter.clientid;
//给指向“客户标识符”的指针赋值
MyData.username.cstring = MQTT_Parameter.username;
//给指向“登录的用户名”的指针赋值,MQTT_Parameter.username[]="username"
MyData.password.cstring = MQTT_Parameter.passwd;
//给指向“登录密码”的指针指针赋值,MQTT_Parameter.passwd[]="passwd"
MyData.cleansession = 1;
//1表示新建一个临时会话,在客户端断开时,会话自动销毁
}
/*
根据MQTTPacket_connectData型结构指针options,生成“CONNECT报文”,保存到buf[]中
返回值大于0,表示“CONNECT报文”的字节数;
返回值等于0,表示出现错误
buf[]={0x10,0x2e,0x00,0x04,MQTT,0x04,0xC2,0x00,0x3C,0x00,0x10,04661219C1676702,0x00,0x08,username,0x00,0x06,passwd}
buf[]用来保存生成的“CONNECT报文”
buflen表示buf[]的最大字节数
options为MQTTPacket_connectData型结构指针
*/
int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options)
{
unsigned char *ptr = buf;
MQTTHeader header = {0};
/*申请一个MQTTHeader类型的联合体变量并初始化,该联合体变量存放的是固定头部内容*/
MQTTConnectFlags flags = {0};
/*申请一个MQTTConnectFlags类型的联合体变量并初始化,该联合体变量存放的是连接标志内容*/
int len = 0;
int rc = -1;
FUNC_ENTRY;
len = MQTTSerialize_connectLength(options);
//根据MQTTPacket_connectData型结构变量options计算MQTT连接包的长度
if ( MQTTPacket_len(len) > buflen )
//判断:len被编码后的字节数是否超出了buflen
{
rc = MQTTPACKET_BUFFER_TOO_SHORT;
goto exit;
}
/////CONNECT报文的固定报头字节1/////
header.byte = 0;//将头部字节设置为0,为修改头部字节做准备
header.bits.type = CONNECT;
/*CONNECT是msgTypes中的枚举成员,CONNECT=1,写入头部字节的高4位*/
writeChar(&ptr, header.byte);
//令buf[0]=0x10,同时修改指针ptr,为下次修改做准备,write header
/////CONNECT报文的固定报头字节2:“剩余长度值”/////
ptr += MQTTPacket_encode(ptr, len);
/*
对“消息长度len”进行编码,然后保存到ptr[]中,返回值为“剩余长度”编码后保存到ptr[]中的字节数为1;
*/
/////CONNECT报文的可变报头:协议名长度+协议名+协议级别/////
if (options->MQTTVersion == 4)//程序初始化时,定义MQTT版本为3.1
{
writeCString(&ptr, "MQTT");
//写字符串,其格式为先写字符串的长度,然后在写协议字符串的具体内容
//添加字符串长度4和字符串"MQTT"到buf[]中,并修改ptr指针
//buf[2]=0,buf[3]=4,buf[4]='M',buf[5]='Q',buf[6]='T',buf[7]='T'
//"MQTT"的长度是16位,使用大端序(big-endian,高位字节在低位字节前面)
writeChar(&ptr, (char) 4);//协议级别:buf[8]=4,并修改ptr指针
}
else
{
writeCString(&ptr, "MQIsdp");
writeChar(&ptr, (char) 3);
}
//CONNECT报文的可变报头:连接标志Connect Flags字段,占1个字节/////
flags.all = 0;//将“连接标志字节”设置为0,为后面修改做准备
flags.bits.cleansession = options->cleansession;
/*程序初始化时options->cleansession=1,注意:1表示新建一个临时会话,在客户端断开时,会话自动销毁;flags.bits.cleansession占用flags.all的bit1=1*/
flags.bits.will = (options->willFlag) ? 1 : 0;
/*程序初始化时options->willFlag=0,意思是告诉服务器不使用“Will消息”,占用flags.all的bit2*/
if (flags.bits.will)//如果使用“Will消息”
{
flags.bits.willQoS = options->will.qos;//占用flags.all的bit4:3=00
flags.bits.willRetain = options->will.retained;//占用flags.all的bit5=0
}
if (options->username.cstring || options->username.lenstring.data)
flags.bits.username = 1;//占用flags.all的bit6=1,有效载荷有用户名字段
if (options->password.cstring || options->password.lenstring.data)
flags.bits.password = 1;//占用flags.all的bit7=1,有效载荷有用密码字段
writeChar(&ptr, flags.all);
//buf[9]=flags.all,并修改ptr=ptr+1指针,flags.all=0xC2
/////////连接标志Connect Flags字段初始化结束/////
//////CONNECT报文的可变报头:保持连接(Keep Alive),占1个字节////
writeInt(&ptr, options->keepAliveInterval);
//程序初始化时options->keepAliveInterval=60=0x3C
//令buf[10]=0x00,buf[11]=0x3C,并修改ptr指针, ptr=ptr+2;
//设置 Keep Alive timer
//////CONNECT报文的有效载荷(payload):客户端标识符
writeMQTTString(&ptr, options->clientID);
//添加客户ID,即设置填写客户端标识符字段
//程序初始化时options->clientID指向字符串"04661219C1676702"
//先保存长度,再拷贝字符串,长度占2个字节
//令buf[12]=0x00,buf[13]=0x10,并修改ptr指针
//////CONNECT报文的有效载荷(payload):遗嘱主题
if (options->willFlag)//程序初始化时options->willFlag=0
{
writeMQTTString(&ptr, options->will.topicName);//遗嘱主题
writeMQTTString(&ptr, options->will.message);//遗嘱消息
}
//////CONNECT报文的有效载荷(payload):登录的用户名
if (flags.bits.username)//有效载荷有“用户名”
writeMQTTString(&ptr, options->username);
//程序初始化时options->username指向字符串"username"
//先保存长度,再拷贝字符串,长度占2个字节
//////CONNECT报文的有效载荷(payload):登录的用户密码
if (flags.bits.password)//有效载荷有“用户密码”
writeMQTTString(&ptr, options->password);
//程序初始化时options->password指向字符串"password"
//先保存长度,再拷贝字符串,长度占2个字节
rc = ptr - buf;
exit: FUNC_EXIT_RC(rc);
return rc;
/*返回值大于0,表示“CONNECT报文”的字节数;返回值等于0,表示出现错误*/
}
5、CONNACK报文
服务端发送CONNACK报文响应从客户端收到的CONNECT报文。
5.1、CONNACK报文的固定报头
由上表可知,CONNACK报文的固定报头字节1的高4位是报文类型,其值为2,其低4位为保留位。CONNACK报文的固定报头字节2是剩余长度,占一个字节,其值为2。
CONNACK报文的固定包头
typedef union
{
unsigned char byte; /**< the whole byte */
struct
{
unsigned int retain : 1;
/*占bit0,在CONNACK报文的固定包头中是保留位 */
unsigned int qos : 2;
/*占bit2:1, 在CONNACK报文的固定包头中是保留位 */
unsigned int dup : 1;
/*占bit3, 在CONNACK报文的固定包头中是保留位 */
unsigned int type : 4;
/*占bit7:4,MQTT头字节高4位,表示MQTT控制报文的类型*/
} bits;
} MQTTHeader;
5.2、CONNACK报文的可变报头
如果服务端收到“清理会话(CleanSession)标志”为1的连接,会将CONNACK报文中的“当前会话设置(Session Present)”标志为0,同时会将CONNACK报文中的“连接返回码”设置为0。
typedef union
{
unsigned char all; /*MQTT连接标志字节,*< all connack flags */
struct
{
unsigned int : 7; /*占用bit6:0,< unused */
unsigned int sessionpresent : 1; /*占用bit7 session present flag */
} bits;
} MQTTConnackFlags; /**< connack flags byte */
5.3、CONNACK报文的有效载荷
CONNACK报文没有有效载荷。
5.4、为什么CONNACK报文的固定报头的剩余长度,占一个字节,其值为2?
因为CONNACK报文的可变报头只有2个字节,并且CONNACK报文没有有效载荷。
所以,CONNACK报文的剩余长度值为2。
5.5、通过代码了解CONNACK报文
/*
解析接收到的“CONNACK报文”,连接成功:“连接确认标志”sessionPresent=0, 连接返回码的值connack_rc=0
*/
int MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen)
{
MQTTHeader header = {0}; //CONNACK报文的固定包头
unsigned char* curdata = buf;
unsigned char* enddata = NULL;
int rc = 0;
int mylen;
MQTTConnackFlags flags = {0};// CONNACK报文的可变报头
////CONNACK报文固定报头:字节1是头部字节
header.byte = readChar(&curdata);//读取固定头部字节的值
if (header.bits.type != CONNACK) goto exit;
////CONNACK报文固定报头:字节2和字节3是剩余长度的字节数
rc = MQTTPacket_decodeBuf(curdata, &mylen);
//解码后的"长度"保存到mylen中,返回值为解码前长度所占的字节数
curdata =curdata + rc;//指向“CONNACK报文的可变报头的起始位置”
enddata = curdata + mylen;//指向“CONNACK报文的可变报头的结束位置”
if (enddata - curdata < 2) goto exit;
//CONNACK报文可变报头
//CONNACK报文可变报头的字节1的bit7表示“连接确认标志”
flags.all = readChar(&curdata);
/*读CONNACK报文的可变报头的字节1“连接确认标志”
同时修改指针curdata*/
*sessionPresent = flags.bits.sessionpresent;
//如果读flags.all的bit7=0, “连接确认标志”为0,则表示连接成功
//CONNACK报文可变报头的字节2表示“连接返回码”
*connack_rc = readChar(&curdata);
//读CONNACK报文的可变报头的字节2,同时修改指针curdata
//“连接返回码”=0x00,连接已被服务端接受;
//“连接返回码”=0x01,服务端不支持客户端请求的MQTT协议级别;
//“连接返回码”=0x02,客户端标识符是正确的UTF-8编码,但服务端不允许使用;
//“连接返回码”=0x03,网络连接已建立,但MQTT服务不可用;
//“连接返回码”=0x04,用户名或密码的数据格式无效;
rc = 1;
exit:
return rc;
}