计算机系统应用教程网站

网站首页 > 技术文章 正文

物联网开发|编译MQTT库mosquitto遇到版本问题(提供补丁)

btikc 2024-09-08 12:03:19 技术文章 55 ℃ 0 评论

1. 下达任务

领导:“你对物联网领域mqtt熟悉吗?其他项目组需要你帮忙。”

面试不都会被问到某技术栈的熟练程度吗,怎么说我以前还看过MQTT的官方文档,也用过mosquitto跑过例程,相对于“API调用者”略知一二。

我厚着脸皮说:“多少了解点。”

项目组白蔡解释:“公司和招标称为某新能源汽车厂商,咱们开发子系统接入汽车网络,业务需要用到MQTT协议,公司初定的第三方库有mosquitto和QT的MQTT库,不过两个库的环境都没搭建好”。

我问白蔡:“我只用过libmosquitto.so,跑过几个demo,它符合你们的业务要求吗?”

领导:“哈哈~早应该把任务派发到你这来,搭建环境就交给你了。”

白蔡捂着脸:“能用就行,挑剔个什么,曾今尝试编译mosquitto,但没成功。”

编译开源程序那还不是 有双手就行 的吗,我说:“不就是依赖第三方库比较多吗,源码都下载下来,累是累了点,应该问题不大吧。”

2. 还真不一定有手就行

期初我没打算手动组个下载第三方库,毫无技术含量还累人,打算用buildroot自动构建mosquitto依赖关系,最终生成的文件系统也能正常运行MQTT业务。只不过buildroot选用的是ucLibc,我以前的业务都在glibc上跑,以至于以前的程序都需要重新编译,甚至部分程序存在兼容问题。

虽说buildroot可以选择外部交叉编译器,可我的原始编译器版本arm-linux-gcc-4.4.4太老了,buildroot明确提示不建议使用,所以我不得不用原始编译器手动编译mosquitto。

作罢,想偷懒也不给机会。

非常奇怪,同样的配置文件下采用buildroot构建的交叉编译器、或本地x86编译器去编译mosquitto-2.0.14没有任何报错,用本地arm-linux-gcc-4.4.4出现若干问题。

文稿的内容讲述排查编译问题的过程,我已经将排查后的补丁放到推送链接里,补丁文件
mosquitto-2.0.14-gcc-4.4.x.patch

获取补丁途径关注程序员写个解:发送20220413

  • arm-linux-gcc-4.4.4
  • glibc-2.11.1(libc、librtpthread)
  • openssl-1.1.1(libssl、libcrypto)
  • mosquitto-2.0.14

3. 编译选项

编译mosquitto之前保证openssl、cjson等已经正确编译并安装到本地交叉编译环境,我这里安装到目录/home/${USER}/cross/imx287,如果你需要mosquitto支持更多的特性,还得预先编译更多的第三方库。

至于mosquitto支持哪些第三方库,工程目录README.md “Build Dependencies” 章节有讲解,配置方法可以直接 “make WITH_SRV=yes WITH_TLS=no” 指定,也可以编辑config.mk配置工程。 为节省时间,我采用默认配置。

在make之前要配置编译查找目录,无非是CLFAGS、LDFLAG下面是我添加到config.mk的模板。

CFLAGS+=-I/home/${USER}/cross/imx287/include/ \
		-I/home/${USER}/cross/imx287/usr/local/include/ \
		-D_GNU_SOURC

LDFLAGS+=-L/home/${USER}/cross/imx287/usr/local/lib \
		-L/home/${USER}/cross/imx287/lib \
		-lssl -lrt\
		-Wl,-rpath=/home/${USER}/cross/imx287/lib:/home/${USER}/cross/imx287/usr/local/lib

4. 重定义mosquitto_plugin_id_t

../../src/mosquitto_broker_internal.h:254: error: redefinition of typedef 'mosquitto_plugin_id_t'
/home/llg/cross/imx287/usr/local/include/mosquitto_broker.h:181: note: previous declaration of 'mosquitto_plugin_id_t' was here

配置好config.mk开始执行make编译。

提示好几个数据类型重定义错误(mosquitto_plugin_id_t、struct mqtt5__property) 低版本编译器多数据类型重定义比较严格,只要typedef后的数据类型用括号 “{}” 围住就属于定义数据类型。去掉括号则属于声明,能正常编译。

修改方法,注释掉mosquitto_broker.h文件结构体定义,用typedef重新声明它。

// mosquitto_broker_internal.h
typedef struct mosquitto_plugin_id_t{
	struct mosquitto__listener *listener;
} mosquitto_plugin_id_t;

// mosquitto_broker.h
// struct mosquitto_plugin_id_t{
// 	struct mosquitto__listener *listener;
// };
typedef struct mosquitto_plugin_id_t mosquitto_plugin_id_t;

同时替换mosquitto_broker.h文件里所有mosquitto_property为struct mqtt5__property。

struct mosquitto_evt_acl_check {
	void *future;
	struct mosquitto *client;
	const char *topic;
	const void *payload;
//  mosquitto_property *properties;
	struct mqtt5__property *properties;

5. prctl替换pthread_setname_np

mosquitto_client_sub.c: In function 'my_message_callback':
mosquitto_client_sub.c:18: warning: format '%s' expects type 'char *', but argument 3 has type 'void * const'
~/cross/imx287/usr/local/lib/libmosquitto.so: undefined reference to `pthread_setname_np'

mosquitto工程不仅仅生成libmosquitt.so已经生成,还包括几个配套内置应用程序,包括mosquitto代理服务器、mosquitto_pub发布者、mosquitto_sub订阅者。

在生成mosquitto代理服务器时提示pthread库未定义函数pthread_setname_np。我的交叉编译环境pthread太老pthread-2.11.so没有实现函数pthread_setname_np,x86版本pthread-2.31.0存在。

怎么解决新老版本的差异呢?

mosquitto总不会发布之初就用上pthread_setname_np函数的吧,于是我去 官网 逐个下载mosquitto历史版本,看看早期版本里,官方如何实现pthread_setname_np函数的功能。

在1.6.0版本里没有用到pthread_setname_np,查看mosquitto_loop_start如何实现的,恩~? 老版本里居然没任何替代函数,莫非它不重要吗?

// mosquitto-2.0.14
int mosquitto_loop_start(struct mosquitto *mosq)
{
	if(!mosq || mosq->threaded != mosq_ts_none) return MOSQ_ERR_INVAL;

	mosq->threaded = mosq_ts_self;
	if(!pthread_create(&mosq->thread_id, NULL, mosquitto__thread_main, mosq)){
#if defined(__linux__)
		pthread_setname_np(mosq->thread_id, "mosquitto loop");
#endif
		return MOSQ_ERR_SUCCESS;
	}else{
		return MOSQ_ERR_ERRNO;
	}
}

// mosquitto-1.6.0
int mosquitto_loop_start(struct mosquitto *mosq)
{
	mosq->threaded = mosq_ts_self;
	if(!pthread_create(&mosq->thread_id, NULL, mosquitto__thread_main, mosq)){
		return MOSQ_ERR_SUCCESS;
	}else{
		return MOSQ_ERR_ERRNO;
	}
}

遇到问题找男人 “man pthread_setname_np”

哟西,Got it。它目的是给线程命名,当某线程异常时top能根据线程名字知道哪个线程出问题,否则所有线程都继承父线程的名字。类似的用法几年前在某工程用过,再翻阅以前工程源码,那个API叫prctl。

By default, all the threads created using pthread_create() inherit the program name. The pthread_setname_np() function can be used to set a unique name for a thread, which can be useful for debugging multithreaded applications.

ptctl函数不能像pthread_setname_np那样放在父线程,否则给父线程取名了。 我把它放在线程处理函数的mosquitto__thread_main开头。

// lib/thread_mosq.c
void *mosquitto__thread_main(void *obj)
{
	struct mosquitto *mosq = obj;
	prctl(PR_SET_NAME, "mosquitto loop");  // 添加这里
    // ....
}

6、总结

解决以上2个问题后mosquitto编译成功了,后面运行测试时候还有几个小插曲:

  • 编写发布者、订阅者测试程序连接mosquitto代理服务器,只能 向环回地址127.0.0.1发起连接才能成功,向其他本地地址192.168.x.x却没有响应,即使wireshark抓包都看不到,是测试程序编写有缺陷吗?
  • 向环回地址连接成功后,却一直提示错误 “Client <unknown> disconnected due to out of memory” 是怎么回事。

两个问题我将在下一篇文章逐一排查。

我将要展示 “strace跟踪mosquitto,找到执行错误原因” 敬请期待。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表