说明:
该文章记录了基于百度智能云MQTT服务器的测试使用过程,方便以后查阅。
MQTT协议中文版下载 https://legacy.gitbook.com/book/mcxiaoke/mqtt-cn/details
1、创建百度智能云 登陆入口地址:https://login.bce.baidu.com/?account=
找到如下入口并进入。
文章图片
文章图片
创建新项目:
文章图片
设备型:百度云已提供JSON解析过滤功能,提供主题变动及发布等事件的处理。适用于一般控制类设备,不适合数据流设备。使用简单快速。
数据型:该类型设备只提供MQTT的服务器功能,不提供payload数据解析功能。使用灵活。
创建项目成功后会出现三个连接地址:
- tcp://au****.mqtt.iot.gz.baidubce.com:1883用于TCP通讯使用
- ssl://au*****.mqtt.iot.gz.baidubce.com:1884客户端MQTT.fx 使用该端口1884支持 SSL/TLS安全认证
- wss://au*****.mqtt.iot.gz.baidubce.com:443
测试主题发布与订阅时,必须保证该主题在策略列表中已添加过。只有策略列表里添加的主题才允许被使用
文章图片
如上发布订阅测试通过。即可用于实际应用场合
2、通过 MQTT.fx 客户端测试连接 参考百度智能云提供的教程 https://cloud.baidu.com/doc/IOT/Quickstart-new.html#.E6.93.8D.E4.BD.9C.E6.AD.A5.E9.AA.A4
文章图片
文章图片
3、Socket 通讯测试 原理:MQTT C语言库提供MQTT协议序列化和反序列功能。
接收过程是将Socket接收到的数据流传递给MQTT库解算。发送工程是将要登陆信息、发布的主题、订阅主题操作序列化成数据流传递给Socket发送数据流中。完成一个数据传输的功能。MQTT是一套协议,Socket是一个数据传输工具。
3.1、C语言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);
}
3.2、MQTT登陆
int mqtt_connect(void)
{
u8 connectBuf[CONNECTBUFMAX];
int len;
MQTTPacket_connectData data = https://www.it610.com/article/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;
}
}
3.3、MQTT主题订阅
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;
}
3.4、MQTT主题发布
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;
}
3.5、MQTT接收订阅消息
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);
}
}
}
【MQTT|MQTT初体验-百度智能云】注意:反序列化函数输出值主题名(receiveTopic.lenstring.data)和主题的数据部分(payload)地址指向原始数据空间(rxfifo数组)中的某一个位置,结算后拷贝出来,不要对上述两个值执行写操作,以免数据破坏。
推荐阅读
- C++(类的成员函数定义方式)
- emqx|车联网 TSP 平台场景中的 MQTT 主题设计
- c#|面试题及答案_Java
- C/C++|C/C++ 大型工程工具链搭建
- C/C++系列|C++中将字符(串)转化成数字,将数字转化成字符(串)
- CS139 C 项目
- C语言|学习C语言编程,推荐你看这6本书
- c|关于指针,我对初学C语言的同窗们真的有话要说!4
- c|《C陷阱与缺陷》第二章阅读笔记