该文章记录了基于百度智能云MQTT服务器的测试使用过程,方便以后查阅。
MQTT协议中文版下载 https://legacy.gitbook.com/book/mcxiaoke/mqtt-cn/details
登陆入口地址:https://login.bce.baidu.com/?account=
找到如下入口并进入。
创建新项目:
设备型:百度云已提供JSON解析过滤功能,提供主题变动及发布等事件的处理。适用于一般控制类设备,不适合数据流设备。使用简单快速。
数据型:该类型设备只提供MQTT的服务器功能,不提供payload数据解析功能。使用灵活。
创建项目成功后会出现三个连接地址:
在用户列表下可进行测试,输入身份密钥执行连接,
测试主题发布与订阅时,必须保证该主题在策略列表中已添加过。只有策略列表里添加的主题才允许被使用
如上发布订阅测试通过。即可用于实际应用场合
参考百度智能云提供的教程 https://cloud.baidu.com/doc/IOT/Quickstart-new.html#.E6.93.8D.E4.BD.9C.E6.AD.A5.E9.AA.A4
原理:MQTT C语言库提供MQTT协议序列化和反序列功能。
接收过程是将Socket接收到的数据流传递给MQTT库解算。发送工程是将要登陆信息、发布的主题、订阅主题操作序列化成数据流传递给Socket发送数据流中。完成一个数据传输的功能。MQTT是一套协议,Socket是一个数据传输工具。
测试使用在VS2013平台,MQTT服务器连接在Socket连接阶段完成。
#include
#include
#include
#pragma comment (lib, "ws2_32.lib") //socket库文件
typedef struct sockaddr_in sockaddr_in;
SOCKET s_socket;
int SocketClass::Connect(char* hostname, int port)
{
sockaddr_in sockaddr;
char buffer[MAXBYTE] = { 0 };
WSADATA wsaData;
//加载库
WSAStartup(MAKEWORD(2, 2), &wsaData);
//创建套接字
s_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
//域名解析出IP
struct hostent *host = gethostbyname(hostname);
char *ip;
if (host == NULL) return -1;
else ip = inet_ntoa(*(struct in_addr*)host->h_addr_list[0]);
//连接服务器
sockaddr.sin_family = PF_INET;
sockaddr.sin_addr.S_un.S_addr = inet_addr(ip);
sockaddr.sin_port = htons(port);
return connect(s_socket, (SOCKADDR*)&sockaddr, sizeof(SOCKADDR));
}
int SocketClass::Close()
{
closesocket(s_socket);
WSACleanup();
return 0;
}
int SocketClass::Write(char* buff, int len)
{
return send(s_socket, buff, len, 0);
}
int SocketClass::Read(char* buff, int len)
{
//等待接收到数据才会退出
return recv(s_socket, buff, len, 0);
}
int mqtt_connect(void)
{
u8 connectBuf[CONNECTBUFMAX];
int len;
MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
data.clientID.cstring = (char*)coco_mqttConnectName; // 客户端标识符(自定义字符串)
data.keepAliveInterval = 50; // keep Alive 单位s
data.cleansession = 0; // 清理会话标志置位
data.username.cstring = "aut6m7h/device"; // 用户名
data.password.cstring = "yhnG3PJm6666666"; // 用户密码
len = MQTTSerialize_connect(connectBuf, sizeof(connectBuf), &data);
//发送登陆信息
_socket.Write((char*)connectBuf, len);
//等待登陆结果应答
len = _socket.Read(readBuff, 512);
if (len >= 4 && (memcmp("\x20\x02", readBuff, 2) == 0)) //参考MQTT文档CONNACK报文说明
{
printf_s("MQTT登陆成功\r\n");
return 0;
}
else
{
printf_s("MQTT登陆失败\r\n");
return -1;
}
}
bool MQTT_Subscribe(char *subscribe)
{
int len;
int req_qos = 0;
u8 buf[512];
MQTTString topicString = MQTTString_initializer;
topicString.cstring = subscribe;
len = MQTTSerialize_subscribe(buf, sizeof(buf), 0, MQTT_packid++, 1, &topicString, &req_qos);
_socket.Write((char*)buf, len);
len = _socket.Read((char*)buf, 512);
if (len >= 5 && (memcmp("\x90\x03", buf, 2) == 0))
{
if ((buf[4] & 0x80) != 0x80)
{
return true;
}
}
return false;
}
bool MQTT_Publish(char *topic, char *str)
{
u8 fifo[512];
int outLen = 0;
MQTTString topicString = MQTTString_initializer;
topicString.cstring = topic;
int len = MQTTSerialize_publish(fifo, 512, 0, 0, 0, MQTT_packid++, topicString, (u8*)str, strlen(str));
_socket.Write((char*)fifo, len);
return false;
}
int len = _socket.Read(rxfifo, 512);
if (len >= 2)
{
if ((rxfifo[0] & 0xF0) == 0x30)//收到发布的消息
{
u8 dup;
int qos;
u8 retanined;
u16 msgid;
MQTTString receiveTopic;
u8 *payload;
int payloadLen;
int ret = MQTTDeserialize_publish(&dup, &qos, &retanined, &msgid, &receiveTopic, &payload, &payloadLen, (u8*)rxfifo, len);
if (ret)
{
char topic[100];
char data[512];
memcpy(topic, receiveTopic.lenstring.data, receiveTopic.lenstring.len);
topic[receiveTopic.lenstring.len] = 0;
memcpy(data, payload, payloadLen);
data[payloadLen] = 0;
printf_s("发序列化成功 topic[%s] payload[%s]\r\n", topic, data);
}
}
}
注意:反序列化函数输出值 主题名(receiveTopic.lenstring.data)和主题的数据部分(payload)地址指向原始数据空间(rxfifo数组)中的某一个位置,结算后拷贝出来,不要对上述两个值执行写操作,以免数据破坏。