MQTT 桌面客户端实现

资料参考

mqtt协议参考

MQTT Version 3.1.1  链接:http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.pdf

概要:MQTT是一个基于C/S的发布/订阅 消息传输协议,它轻量化,开源,简单 ,易于实现。这些特性让它 适用于下面这些情况:Machine to Machine (M2M),Internet of Things (IoT)的通讯,并且在受限的环境中,比如代码占用少,网络带宽有限。mqtt是理想的选择。 它直接运行在TCP/IP协议层上,由网络协议栈提供服务。详细参考 MQTT Version 3.1.1

winsock参考

Windows Sockets 2 链接:Windows Sockets 2 - Win32 apps | Microsoft Learn

概要:标准的套接字编程都类似于(BSD) api 接口,易于操作,详情 参考微软文档。
 

win32窗口及消息参考

Windows and messages 链接:Windows and Messages - Win32 apps | Microsoft Learn

概要:主要讲述 窗口的 各种层次关系,主要子父关系。窗口对象类的注册,它是一种面向对象的思想,但是为C语言实现。窗口处理程式(window procedures)或者叫handler, 发生了各种事件消息时,就调用它来处理,一种回调机制,广泛用于图形界面编程。还有各种事件的载体 message,用于传递各种事件给窗体。详情 参考微软文档。

win32 控件参考

Windows controls 链接:Windows Controls - Win32 apps | Microsoft Learn

概要:控件也是窗体,作为主窗体的子窗体。一起使用来与用户做交互。窗体与窗体的通讯以message的形式实现。微软写好了一部分控件,形成库供客户用,当然你也可以自定义控件,灵活性高。详情 参考微软文档。

win32 进程和线程参考

processes and threads链接:Processes and Threads - Win32 apps | Microsoft Learn

Synchronization           链接:Synchronization - Win32 apps | Microsoft Learn

概要:主要讲述CPU调度相关的点子,和一些win32内核对象,信号量,互斥量,队列,用于线程间同步与通讯等等,详情 参考微软文档。

mqtt 封包函数参考

mqtt 封包函数 链接:eclipse/paho.mqtt.embedded-c: Paho MQTT C client library for embedded systems. Paho is an Eclipse IoT project (https://iot.eclipse.org/) (github.com)

概要:主要提供的功能是,根据mqtt 的协议,将待发送数据,封装成协议包,然后通过tcp连接,发送到broker server。和从broker server接受到协议包,解封协议包,后呈现给上层应用。在API中广泛用 串行化 表示 封装协议包,反串行化 表示 解封协议包。

Broker Server

broker server 用的是 百度智能云 提供的 物联网核心套件 IoT Core 服务,目前此服务,前很多万条消息都是免费的,个人测试完全足够了。但是需要个人身份验证。下单此服务即可提供限额免费服务。个人觉得非常爽的。

下单后,管理中心 如图

点击进入,运行着一个物联网核心,新注册的需要自己创建,如图

 

点击核心后,进入设备列表,创建两设备 如图

新建设备,可能需要先 新建模板 在其中添加主题(topic) 如图

 

然后点击,某个设备名称,进入设备连接详情 如图

 

重要消息:

其中 broker address:接入点

其中 user name:IoTCoreId/DeviceKey

其中 password: DeviceSecret

其中 两个设备,可以自由的订阅 模板中的 主题(topic),和发布到 主题(topic)。

阿里 和中国移动 也有类似服务,有兴趣可以去玩玩。

软件结构

图形元素结构

图形窗口的最顶层是,主窗口,由自己注册窗体类,然后创建窗体实例。

主窗体 的客户区域,我创建了一个 客户窗(clientwnd),由自己注册窗体类,然后创建窗体实例。

在客户窗(clientwnd) 下,用来button group窗体,来控件分组。

在分组下,放各种控件。如图

 

线程结构

主要有两根线程构成。

一根线程处理窗体的各种事件与消息,也就是message_loop,抓消息,转化,派发给指定窗体处理。比如,按钮 控件点击,编辑框控件内容改变等等。

//
// window app entry point function
//
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{

	user_debug_init();

	if (user_register_class(hInstance) == 0)
	{
		XDEBUG("user_register_class failed\n");
		return 400;
	}
	else {
		XDEBUG("user_register_class ok\n");
	}

	if (user_init_instance(hInstance, nShowCmd) == 0)
	{
		XDEBUG("user_init_instance failed\n");
		return 400;
	}
	else {
		XDEBUG("user_init_instance ok\n");
	}

	//main message loop.
	return main_message_loop();
}

int main_message_loop(void)
{
	MSG msg;

	while (GetMessageA(&msg, nullptr, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessageA(&msg);
	}

	return (int)msg.wParam;
}

另一根线程处理MQTT协议,比如收到publish包,要回publish ack包等等,而且会被动态 创建 与 删除。具体 见源码库。

一些重要Tips

mqtt 封包函数 原本设计给嵌入式MCU用的,为了适配win32,做了一些信号量,计时,线程方面的适配。有一些修改,但不大。添加了 UNSUBACK 到cycle函数中,原本没有。

注意 PostMessagA 和 SendMessageA的区别。

注意 两根线程的 同步问题。

注意 终结一个线程问题,相关微软参考

The TerminateThread and TerminateProcess functions should be used only in extreme 
circumstances, since they do not allow threads to clean up, do not notify attached DLLs,
 and do not free the initial stack. In addition, handles to objects owned by 
the thread are not closed until the process terminates. The following steps 
provide a better solution:

1.Create an event object using the CreateEvent function.
2.Create the threads.
3.Each thread monitors the event state by calling the WaitForSingleObject function. 
Use a wait time-out interval of zero.
4.Each thread terminates its own execution when the event is set to 
the signaled state (WaitForSingleObject returns WAIT_OBJECT_0).

GitHub仓库

工具:vs2022, v143tools.

相关源码:G-ATLAS/mqtt_win32: MQTT Client sample demo , base on win32 api , UI client. (github.com)

主体功能测试概览

编译源码,启动运行,填入连接参数,如图

 点击 connect ,连接成功后,参数修改会被禁用,连接失败则不会禁用,提示retry。如图

 用另一个客户端软件来辅助测试,填入参数,后连接,

 并订阅一个话题

 然后用自己写的 客户端 向这个话题发送数据。

 然后另一个已经订阅此话题客户端,就收到了“hello mqtt”,如图

 然后反过来,另一个客户端发,自己写的客户端收,如下图

再发送

 

 

 

 

 再用自己写的客户端,自己发送,自己接收

 

 

最后关闭连接,如图

 

总结:主体功能没什么问题,当然也有一些不足,

局限于字符串 发送接收,HEX未实现。

还有许多细节参数,未提供设置,使用的默认值,出于简化的目的。

设备的遗愿(will flag)功能,也未实现。 

由于时间精力实在有限,这只是个简单的demo,如果你感兴趣,可以完善完善。如果你得到了些许思路,也可以按照自己的方式重写。

结束

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值