mosquitto客户端配置参数管理(mosquitto2.0.15客户端源码分析之一)

前言

struct mosq_configstruct mosquitto 是mosquitto客户端源码中的最重要的两个数据结构,分别用于存储mosquitto客户端的配置信息和运行时状态信息。在mosquitto客户端的运行过程中,它们都起着至关重要的作用。struct mosq_config 存储mosquitto客户端的配置信息,其中的选项和参数决定了mosquitto客户端的行为和功能,而 struct mosquitto 则记录了客户端的运行时状态,包括客户端的连接状态、订阅的主题、接收和发送的消息等等,以便客户端能够正确地处理和响应来自服务器的消息和事件。
本文详细介绍了其中struct mosq_config 定义的对象cfg的设置过程,为正在阅读mosquitto源码的读者提供一点参考。

1 参数配置基本流程

配置参数设置分三步进行:
(1)清空struct mosq_config对象cfg数值,并对部分参数赋初值;
(2)按照环境变量指示的路径或默认路径,逐行读出配置文件的配置信息,赋值给对象cfg的相应参数
(3)用命令行参数设置的参数给对象cfg的相应参数赋值,如果与上面的设置有冲突,直接覆盖。

(1)定义全局变量cfg
   struct mosq_config cfg;
(2)在入口程序main中调用client_config_load
int main(int argc, char *argv[])
{
...
rc = client_config_load(&cfg, CLIENT_PUB, argc, argv);
...
}
(3)在client_config_load函数中调用init_config函数,清空对象cfg后,对部分参数赋初值
int client_config_load(struct mosq_config *cfg, int pub_or_sub, int argc, char *argv[])
{
...
	init_config(cfg, pub_or_sub);
...
}

static void init_config(struct mosq_config *cfg, int pub_or_sub)
{
	memset(cfg, 0, sizeof(*cfg));
	cfg->port = PORT_UNDEFINED;
	cfg->max_inflight = 20;
	cfg->keepalive = 60;
	cfg->clean_session = true;
	cfg->eol = true;
	cfg->repeat_count = 1;
	cfg->repeat_delay.tv_sec = 0;
	cfg->repeat_delay.tv_usec = 0;
	cfg->random_filter = 10000;
	if(pub_or_sub == CLIENT_RR){
		cfg->protocol_version = MQTT_PROTOCOL_V5;
		cfg->msg_count = 1;
	}else{
		cfg->protocol_version = MQTT_PROTOCOL_V311;
	}
	cfg->session_expiry_interval = -1; /* -1 means unset here, the user can't set it to -1. */
}
(4)在client_config_load函数中,从读出配置文件中的配置信息,调用client_config_line_proc逐行处理,赋值给struct mosq_config对象cfg的相应参数
int client_config_load(struct mosq_config *cfg, int pub_or_sub, int argc, char *argv[])
{
...
	if(loc){//loc:放置配置文件的文件路径
		fptr = fopen(loc, "rt");
		if(fptr){
			while(fgets(line, 1024, fptr)){
				if(line[0] == '#') continue; /* Comments */

				while(line[strlen(line)-1] == 10 || line[strlen(line)-1] == 13){//逐行取出配置参数
					line[strlen(line)-1] = 0;
				}
				/* All offset by one "args" here, because real argc/argv has
				 * program name as the first entry. */
				args[1] = strtok(line, " ");
				if(args[1]){
					args[2] = strtok(NULL, "");
					if(args[2]){
						count = 3;
					}else{
						count = 2;
					}
					rc = client_config_line_proc(cfg, pub_or_sub, count, args);//将配置文件里的配置信息赋值到相应的参数
					if(rc){
						fclose(fptr);
						free(loc);
						return rc;
					}
				}
			}
			fclose(fptr);
		}
		free(loc);
	}
	...
(5)在client_config_load函数中,用命令行中设置的参数对struct mosq_config对象cfg的相应参数进行覆盖
int client_config_load(struct mosq_config *cfg, int pub_or_sub, int argc, char *argv[])
{
  ...
  	/* Deal with real argc/argv */
	rc = client_config_line_proc(cfg, pub_or_sub, argc, argv);
	if(rc) return rc;
	...
(6)客户端 ID生成

在基本参数里,客户端 ID 在 MQTT 协议中是用于标识唯一客户端的标识符,因此生成客户端 ID 时需要注意避免重复。
如果配置文件中指定了 clientid_prefix 选项,则将该选项指定的前缀添加到客户端 ID 前面,客户端 ID 加最后一个字符null 总共由23 个字符组成。
2. 如果没有指定前缀,则生成
通常建议使用具有一定随机性的字符串作为前缀,来增加生成的客户端 ID 的唯一性的保证。同时,还需要注意不要过度依赖客户端 ID 来确保消息的传递,应该使用其他机制辅助保证消息的可靠传递,例如 QoS(服务质量)机制等。
源代码里面生成客户端ID的函数client_id_generate,使用了前缀+Pid(进程编号),可以看成只是一个示例,实际使用时,只要能够保证唯一性,可以自行调整。

int client_id_generate(struct mosq_config *cfg)
{
	if(cfg->id_prefix){
		cfg->id = malloc(strlen(cfg->id_prefix)+10);
		if(!cfg->id){
			err_printf(cfg, "Error: Out of memory.\n");
			mosquitto_lib_cleanup();
			return 1;
		}
		snprintf(cfg->id, strlen(cfg->id_prefix)+10, "%s%d", cfg->id_prefix, getpid());
	}
	return MOSQ_ERR_SUCCESS;
}
...
}

客户端 ID 在MQTT协议的通信中是非常重要的。为了保证客户端 ID 的正确,mosquitto源码做了很多容错工作,比如在连接服务器前的初始化mosquitto__connect_init()函数里有这样一段程序代码片段:

static int mosquitto__connect_init(struct mosquitto *mosq, const char *host, int port, int keepalive)
{
	...
	/* Only MQTT v3.1 requires a client id to be sent */
	if(mosq->id == NULL && (mosq->protocol == mosq_p_mqtt31)){
		mosq->id = (char *)mosquitto__calloc(24, sizeof(char));
		if(!mosq->id){
			return MOSQ_ERR_NOMEM;
		}
		mosq->id[0] = 'm';
		mosq->id[1] = 'o';
		mosq->id[2] = 's';
		mosq->id[3] = 'q';
		mosq->id[4] = '-';

		rc = util__random_bytes(&mosq->id[5], 18);
		if(rc) return rc;

		for(i=5; i<23; i++){
			mosq->id[i] = alphanum[(mosq->id[i]&0x7F)%(sizeof(alphanum)-1)];
		}
	}

	...
}

当客户端使用MQTT 3.1协议且未提供客户端ID时,为客户端生成一个默认的客户端ID(mosq->id)。
这段代码生成的客户端ID遵循mosq-前缀后跟18个随机字符的格式。由于使用了随机字符,这种客户端ID具有一定程度的唯一性,减少了在没有提供客户端ID时的ID冲突。然而,在实际应用中,为了更好地跟踪和管理客户端,建议用户自行提供有意义的客户端ID。

(7)函数client_config_load的第二个参数pub_or_sub代表3个不同的客户端,会在参数设置的过程中体出来
  1. 发布者(Publisher)
    在MQTT协议中,发布者是指向MQTT代理服务器发布消息的客户端。发布者使用MQTT客户端库连接到MQTT代理服务器,并将消息发布到一个或多个主题(Topic)中。MQTT代理服务器接收到消息后会将其传递给订阅了相应主题的订阅者。在发布消息时,发布者需要指定消息的主题、消息内容、消息质量等级以及其他可选参数。
  2. 订阅者(Subscriber)
    在MQTT协议中,订阅者是指向MQTT代理服务器订阅主题的客户端。订阅者使用MQTT客户端库连接到MQTT代理服务器,并指定要订阅的主题。当MQTT代理服务器接收到发布者发布的消息时,会将消息传递给订阅了相应主题的订阅者。订阅者可以通过MQTT客户端库来接收消息并处理它们。
  3. 请求-响应三种客户端
    在MQTT协议中,请求-响应三种客户端是指能够在MQTT代理服务器和应用程序之间进行请求和响应的客户端。这种客户端可以在MQTT代理服务器上注册回调函数,以便在收到应用程序发送的请求时进行处理,并向应用程序返回响应。请求-响应客户端通常用于在MQTT网络中进行设备管理、传输配置信息和进行远程命令执行等操作。常见的请求-响应客户端包括MQTT-SN(MQTT for Sensor Networks)和MQTT-RPC(MQTT Remote Procedure Call)等。

2 client_config_line_proc–初始化参数函数详解

client_config_line_proc 函数是 Mosquitto MQTT 代理程序中的一个函数,它的作用是逐行解析配置文件或命令行参数,并将解析后的值保存在 mosq_config 结构体对象cfg中,以便在程序的其他部分使用。
函数参数说明:

  • cfg:指向 mosq_config 结构体的指针,表示要保存解析结果的结构体;
  • pub_or_sub:表示当前程序是发布者还是订阅者,取值可以是 CLIENT_PUBCLIENT_SUB
  • argc:表示命令行参数的个数;
  • argv:表示命令行参数的数组。

该函数会循环遍历所有命令行参数,并根据参数的名称对应地设置结构体中的对应成员变量的值。同时,该函数也支持读取配置文件,如果 argv 数组中包含 -c 参数,则函数会从指定的配置文件中读取配置信息,并将配置信息保存到结构体中。


int client_config_line_proc(struct mosq_config *cfg, int pub_or_sub, int argc, char *argv[])
{
	int i;
	int tmpi;
	float f;
	size_t szt;

	for(i=1; i<argc; i++){
		if(!strcmp(argv[i], "-A")){
			if(i==argc-1){
				fprintf(stderr, "Error: -A argument given but no address specified.\n\n");
				return 1;
			}else{
				cfg->bind_address = strdup(argv[i+1]);
			}
			i++;

上面这段代码的功能是:
cfg->bind_address 变量在 mosquitto 客户端库中表示客户端程序要绑定的 IP 地址(不是服务器的地址)。具体来说,当客户端程序调用 mosquitto_connect() 函数连接 MQTT 服务器时,如果设置了 bind_address,则客户端程序会尝试绑定指定的 IP 地址,以便与 MQTT 服务器建立连接。
例如,如果客户端程序需要在本机的 IP 地址为 192.168.0.100 的网卡上与 MQTT 服务器建立连接,则可以将 bind_address 设置为 192.168.0.100。在连接 MQTT 服务器时,客户端程序会首先绑定指定的 IP 地址,然后使用该 IP 地址与 MQTT 服务器进行通信。
需要注意的是,cfg->bind_address 变量在 mosquitto 客户端库中是可选的,如果没有设置该变量,则客户端程序会默认使用本机的任意可用 IP 地址与 MQTT 服务器建立连接。如果 MQTT 服务器绑定了特定的 IP 地址,则客户端程序必须使用与服务器相同的 IP 地址进行连接,否则连接可能会失败。

#ifdef WITH_TLS
		}else if(!strcmp(argv[i], "--cafile")){
			if(i==argc-1){
				fprintf(stderr, "Error: --cafile argument given but no file specified.\n\n");
				return 1;
			}else{
				cfg->cafile = strdup(argv[i+1]);
			}
			i++;
		}

这段代码是 client_config_line_proc 函数中用于处理 --cafile 参数的逻辑。--cafile 参数用于指定客户端与 MQTT 服务器建立连接时使用的 CA(Certificate Authority)证书文件,以验证服务器证书的合法性。解析 --cafile 参数,并将指定的证书文件的路径保存在 mosq_config 结构体中的 cafile 变量中。如果没有指定证书文件的路径,则输出错误信息并返回 1。当客户端程序需要建立加密连接时,会使用该证书文件来验证 MQTT 服务器的身份,以确保通信安全。

    else if(!strcmp(argv[i], "--capath")){
			if(i==argc-1){
				fprintf(stderr, "Error: --capath argument given but no directory specified.\n\n");
				return 1;
			}else{
				cfg->capath = strdup(argv[i+1]);
			}
			i++;
		}

这段代码是 client_config_line_proc 函数中用于处理 --capath 参数的逻辑。--capath 参数用于指定客户端与 MQTT 服务器建立连接时使用的 CA(Certificate Authority)证书文件夹路径,以验证服务器证书的合法性。解析 --capath 参数,并将指定的证书文件夹的路径保存在 mosq_config 结构体中的 capath 变量中。如果没有指定证书文件夹的路径,则输出错误信息并返回 1。当客户端程序需要建立加密连接时,会使用该证书文件夹中的证书来验证 MQTT 服务器的身份,以确保通信安全。

     else if(!strcmp(argv[i], "--cert")){
			if(i==argc-1){
				fprintf(stderr, "Error: --cert argument given but no file specified.\n\n");
				return 1;
			}else{
				cfg->certfile = strdup(argv[i+1]);
			}
			i++;
		}
else if(!strcmp(argv[i], "--ciphers")){
			if(i==argc-1){
				fprintf(stderr, "Error: --ciphers argument given but no ciphers specified.\n\n");
				return 1;
			}else{
				cfg->ciphers = strdup(argv[i+1]);
			}
			i++;
#endif
		}

这段代码是 client_config_line_proc 函数中用于处理 --cert 参数的逻辑。--cert 参数用于指定客户端与 MQTT 服务器建立加密连接时使用的客户端证书文件。解析 --cert 参数,并将指定的客户端证书文件的路径保存在 mosq_config 结构体中的 certfile 变量中。如果没有指定证书文件的路径,则输出错误信息并返回 1。当客户端程序需要建立加密连接时,会使用该客户端证书来验证自己的身份,并且发送给服务器进行验证,以确保通信安全。

else if(!strcmp(argv[i], "-C")){
			if(pub_or_sub != CLIENT_SUB){
				goto unknown_option;
			}else{
				if(i==argc-1){
					fprintf(stderr, "Error: -C argument given but no count specified.\n\n");
					return 1;
				}else{
					cfg->msg_count = atoi(argv[i+1]);
					if(cfg->msg_count < 1){
						fprintf(stderr, "Error: Invalid message count \"%d\".\n\n", cfg->msg_count);
						return 1;
					}
				}
				i++;
			}
		}

这段代码是 client_config_line_proc 函数中用于处理 -C 参数的逻辑。-C 参数用于指定客户端订阅主题时最多接收的消息数。如果不是订阅客户端,则这个参数无效。解析 -C 参数,并将指定的消息最大接收数保存在 mosq_config 结构体中的 msg_count 变量中。如果没有指定消息最大接收数,则输出错误信息并返回 1。在订阅客户端中,如果已经接收到指定数量的消息,则该客户端会自动断开连接。如果该参数是在发布客户端中使用的,则会跳转到 unknown_option 进行错误处理。如果命令行中存在其他无法识别的选项,则也会跳转到 unknown_option 进行错误处理。


else if(!strcmp(argv[i], "-c") || !strcmp(argv[i], "--disable-clean-session")){
			cfg->clean_session = false;
		}else if(!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")){
			cfg->debug = true;
		}

该代码段通过判断命令行参数的值,分别对配置结构体cfg中的clean_session和debug成员变量进行设置。
如果命令行参数为"-c"或"–disable-clean-session",则将cfg->clean_session设置为false,表示禁用清除会话(clean session)。MQTT协议中的清除会话机制是指当客户端与服务器断开连接时,是否需要保留之前发布或订阅的消息状态。如果设置为false,则不会清除会话,当客户端重新连接到服务器时,仍能接收到之前的消息状态。
如果命令行参数为"-d"或"–debug",则将cfg->debug设置为true,表示启用调试模式。启用调试模式后,mosquitto会将详细的调试信息打印到控制台上,方便调试。

else if(!strcmp(argv[i], "-D") || !strcmp(argv[i], "--property")){
			i++;
			if(cfg_parse_property(cfg, argc, argv, &i)){
				return 1;
			}
			cfg->protocol_version = MQTT_PROTOCOL_V5;
		}

这段代码是 client_config_line_proc 函数中用于解析mosquitto命令行参数。其中,该函数会判断参数是否为"-D"或"–property",如果是则调用cfg_parse_property函数解析参数,同时将协议版本设置为MQTT_PROTOCOL_V5。
在MQTT v5协议中,引入了属性(property)的概念,允许在消息头和消息体中添加多种不同类型的属性信息。因此,在使用属性时需要将协议版本设置为MQTT v5。通过将cfg->protocol_version设置为MQTT_PROTOCOL_V5,mosquitto就会以MQTT v5协议进行通信,从而支持属性的使用。
总之,该代码段的作用是识别并解析命令行参数中的属性信息,并将协议版本设置为MQTT v5,以支持属性的使用。


else if(!strcmp(argv[i], "-e")){
			if(pub_or_sub != CLIENT_RR){
				goto unknown_option;
			}
			if(i==argc-1){
				fprintf(stderr, "Error: -e argument given but no response topic specified.\n\n");
				return 1;
			}else{
				if(cfg_add_topic(cfg, CLIENT_RESPONSE_TOPIC, argv[i+1], "-e")){
					return 1;
				}
			}
			i++;
		}

该代码段通过判断命令行参数的值,对配置结构体cfg中的响应主题进行设置。
如果命令行参数为"-e",则需要进一步判断pub_or_sub参数的值,该参数定义了客户端的类型。如果pub_or_sub不等于CLIENT_RR,则说明当前命令行参数无效,跳转到unknown_option标签处理。如果pub_or_sub等于CLIENT_RR,则需要检查下一个参数是否为响应主题,如果是,则通过cfg_add_topic函数将响应主题添加到cfg结构体中。
需要注意的是,如果没有指定响应主题,则会输出错误信息,然后返回1表示出现错误。
总之,该代码段的作用是判断并解析命令行参数"-e",将响应主题添加到mosquitto配置结构体cfg中。该功能通常用于实现请求响应模式的通信。

else if(!strcmp(argv[i], "-E")){
			if(pub_or_sub != CLIENT_SUB){
				goto unknown_option;
			}
			cfg->exit_after_sub = true;
		}

该代码段通过判断命令行参数的值,对配置结构体cfg中的exit_after_sub成员变量进行设置。
如果命令行参数为"-E",则需要进一步判断pub_or_sub参数(客户端的类型)的值。如果pub_or_sub不等于CLIENT_SUB,则说明当前命令行参数无效,跳转到unknown_option标签处理。如果pub_or_sub等于CLIENT_SUB,则将exit_after_sub设置为true,表示订阅完指定主题后,mosquitto客户端将自动退出。
需要注意的是,该选项只适用于Subscriber客户端,用于在完成订阅后自动退出程序,而不必等待用户手动终止。如果该选项被启用,mosquitto客户端将完成订阅后自动退出,而不会等待其他任何事件或命令。
总之,该代码段的作用是判断并解析命令行参数"-E",将mosquitto Subscriber客户端的自动退出选项设置为true。这个选项用于订阅完成后自动退出mosquitto Subscriber客户端。

	else if(!strcmp(argv[i], "-f") || !strcmp(argv[i], "--file")){
			if(pub_or_sub == CLIENT_SUB){
				goto unknown_option;
			}
			if(cfg->pub_mode != MSGMODE_NONE){
				fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n");
				return 1;
			}else if(i==argc-1){
				fprintf(stderr, "Error: -f argument given but no file specified.\n\n");
				return 1;
			}else{
				cfg->pub_mode = MSGMODE_FILE;
				cfg->file_input = strdup(argv[i+1]);
				if(!cfg->file_input){
					err_printf(cfg, "Error: Out of memory.\n");
					return 1;
				}
			}
			i++;
		}

这段代码用于解析mosquitto命令行参数。其中,该代码段通过判断命令行参数的值,对配置结构体cfg中的消息模式(msg_mode)进行设置。
如果命令行参数为"-f"或"–file",则需要进一步判断pub_or_sub参数的值。如果pub_or_sub等于CLIENT_SUB,则说明当前命令行参数无效,跳转到unknown_option标签处理。如果pub_or_sub等于CLIENT_PUB,则将msg_mode设置为MSGMODE_FILE,表示使用文件方式发送消息。
如果在该选项之前已经设置了消息模式,则会输出错误信息,然后返回1表示出现错误。如果没有指定文件名,则会输出错误信息,然后返回1表示出现错误。如果文件名正确,则将文件名保存到cfg结构体中的file_input成员变量中。
需要注意的是,如果使用文件方式发送消息,则消息内容将从指定的文件中读取。
总之,该代码段的作用是判断并解析命令行参数"-f"或"–file",将mosquitto Publisher客户端的消息模式设置为MSGMODE_FILE,并指定从指定的文件中读取消息内容。


else if(!strcmp(argv[i], "-F")){
			if(pub_or_sub == CLIENT_PUB){
				goto unknown_option;
			}
			if(i==argc-1){
				fprintf(stderr, "Error: -F argument given but no format specified.\n\n");
				return 1;
			}else{
				cfg->format = strdup(argv[i+1]);
				if(!cfg->format){
					fprintf(stderr, "Error: Out of memory.\n");
					return 1;
				}
				if(check_format(cfg->format)){
					return 1;
				}
			}
			i++;
		}

该代码段通过判断命令行参数的值,对配置结构体cfg中的消息格式(format)进行设置。
如果命令行参数为"-F",则需要进一步判断pub_or_sub参数的值。如果pub_or_sub等于CLIENT_PUB,则说明当前命令行参数无效,跳转到unknown_option标签处理。如果pub_or_sub等于CLIENT_SUB,则将format设置为指定的格式。
如果没有指定格式,则会输出错误信息,然后返回1表示出现错误。如果指定了格式,则将格式保存到cfg结构体中的format成员变量中。同时,检查所指定的格式是否合法。如果不合法,则会输出错误信息并返回1表示出现错误。
需要注意的是,所指定的格式通常用于发布或订阅消息的有效负载(payload)中。mosquitto支持的消息格式包括JSON、XML等,用户也可以自定义格式。
总之,该代码段的作用是判断并解析命令行参数"-F",将mosquitto Subscriber客户端的消息格式设置为指定的格式。这个选项用于设置订阅的消息格式,以便在接收到消息后能够正确解析。

else if(!strcmp(argv[i], "--help")){
			return 2;
		}

该代码段的作用是判断并解析命令行参数"–help",如果命令行参数为"–help",则返回2,以便在主函数中显示帮助信息。
需要注意的是,返回值为2的目的是在主函数中调用print_usage函数输出帮助信息,而不是终止程序运行。如果返回值为1,则表示出现错误,需要输出错误信息并终止程序运行。


else if(!strcmp(argv[i], "-h") || !strcmp(argv[i], "--host")){
			if(i==argc-1){
				fprintf(stderr, "Error: -h argument given but no host specified.\n\n");
				return 1;
			}else{
				cfg->host = strdup(argv[i+1]);
			}
			i++;
			}

该代码段通过判断命令行参数的值,对配置结构体cfg中的服务器主机名(host)进行设置。
如果命令行参数为"-h"或"–host",则需要进一步判断是否指定了服务器主机名。如果没有指定主机名,则会输出错误信息,然后返回1表示出现错误。如果指定了主机名,则将主机名保存到cfg结构体中的host成员变量中。
需要注意的是,该选项通常用于设置MQTT服务器的主机名或IP地址。如果未指定主机名,则mosquitto客户端无法连接到服务器。
总之,该代码段的作用是判断并解析命令行参数"-h"或"–host",将mosquitto客户端连接的服务器主机名设置为指定的主机名。这个选项用于指定mosquitto客户端连接的MQTT服务器。

#ifdef WITH_TLS
		else if(!strcmp(argv[i], "--insecure")){
			cfg->insecure = true;
#endif
		}

该代码段通过判断命令行参数的值,对配置结构体cfg中的安全(security)选项进行设置。
在这段代码中,先通过条件编译#ifdef WITH_TLS判断是否启用了TLS安全协议。如果启用了TLS,则可以使用"–insecure"选项设置mosquitto客户端的安全性。
如果命令行参数为"–insecure",则将cfg结构体中的insecure成员变量设置为true。表示在连接MQTT服务器时,不验证服务器的SSL/TLS证书,这可能会存在安全风险,因此默认情况下mosquitto客户端会验证服务器证书。
需要注意的是,该选项通常用于测试环境或临时的开发环境中,如果在生产环境中使用,可能会受到恶意攻击。
总之,该代码段的作用是判断并解析命令行参数"–insecure",将mosquitto客户端的安全选项设置为非安全模式,即不验证MQTT服务器的SSL/TLS证书。如果启用了TLS,则可以使用该选项来设置mosquitto客户端的安全性。


else if(!strcmp(argv[i], "-i") || !strcmp(argv[i], "--id")){
			if(cfg->id_prefix){
				fprintf(stderr, "Error: -i and -I argument cannot be used together.\n\n");
				return 1;
			}
			if(i==argc-1){
				fprintf(stderr, "Error: -i argument given but no id specified.\n\n");
				return 1;
			}else{
				cfg->id = strdup(argv[i+1]);
			}
			i++;
		}

该代码段通过判断命令行参数的值,对配置结构体cfg中的客户端ID(id)进行设置。
如果命令行参数为"-i"或"–id",则需要进一步判断是否指定了客户端ID。如果同时指定了ID前缀(-I选项),则会输出错误信息,然后返回1表示出现错误。如果没有指定ID,则会输出错误信息,然后返回1表示出现错误。如果指定了ID,则将ID保存到cfg结构体中的id成员变量中。
需要注意的是,客户端ID必须是唯一的。如果没有指定ID,则mosquitto客户端将随机生成一个唯一的客户端ID。
总之,该代码段的作用是判断并解析命令行参数"-i"或"–id",将mosquitto客户端的ID设置为指定的ID。这个选项用于指定mosquitto客户端的唯一标识符,以便在MQTT服务器上进行识别和连接。


else if(!strcmp(argv[i], "-I") || !strcmp(argv[i], "--id-prefix")){
			if(cfg->id){
				fprintf(stderr, "Error: -i and -I argument cannot be used together.\n\n");
				return 1;
			}
			if(i==argc-1){
				fprintf(stderr, "Error: -I argument given but no id prefix specified.\n\n");
				return 1;
			}else{
				cfg->id_prefix = strdup(argv[i+1]);
			}
			i++;
		}

该代码段通过判断命令行参数的值,对配置结构体cfg中的客户端ID前缀(id_prefix)进行设置。
如果命令行参数为"-I"或"–id-prefix",则需要进一步判断是否指定了客户端ID前缀。如果同时指定了ID(-i选项),则会输出错误信息,然后返回1表示出现错误。如果没有指定ID前缀,则会输出错误信息,然后返回1表示出现错误。如果指定了ID前缀,则将ID前缀保存到cfg结构体中的id_prefix成员变量中。
需要注意的是,客户端ID必须是唯一的。如果指定了ID前缀,则mosquitto客户端将在前缀后面加上一个唯一的数字,以生成一个唯一的客户端ID。如果未指定ID前缀,则mosquitto客户端将随机生成一个唯一的客户端ID。
总之,该代码段的作用是判断并解析命令行参数"-I"或"–id-prefix",将mosquitto客户端的ID前缀设置为指定的前缀。这个选项用于生成mosquitto客户端的唯一标识符,以便在MQTT服务器上进行识别和连接。如果未指定ID前缀,则mosquitto客户端将随机生成一个唯一的客户端ID。


else if(!strcmp(argv[i], "-k") || !strcmp(argv[i], "--keepalive")){
			if(i==argc-1){
				fprintf(stderr, "Error: -k argument given but no keepalive specified.\n\n");
				return 1;
			}else{
				cfg->keepalive = atoi(argv[i+1]);
				if(cfg->keepalive<5 || cfg->keepalive>UINT16_MAX){
					fprintf(stderr, "Error: Invalid keepalive given, it must be between 5 and 65535 inclusive.\n\n");
					return 1;
				}
			}
			i++;
		}

该代码段通过判断命令行参数的值,对配置结构体cfg中的心跳保持时间(keepalive)进行设置。

如果命令行参数为"-k"或"–keepalive",则需要进一步判断是否指定了心跳保持时间。如果没有指定保持时间,则会输出错误信息,然后返回1表示出现错误。如果指定了保持时间,则将保持时间转换成整数类型,并保存到cfg结构体中的keepalive成员变量中。

需要注意的是,心跳保持时间是指mosquitto客户端与MQTT服务器之间保持连接的时间。如果在保持时间内没有收到来自MQTT服务器的任何消息,则mosquitto客户端会自动向服务器发送心跳包以维持连接。

总之,该代码段的作用是判断并解析命令行参数"-k"或"–keepalive",将mosquitto客户端与MQTT服务器之间的心跳保持时间设置为指定的时间。这个选项用于设置mosquitto客户端与MQTT服务器之间的连接保持时间。如果没有指定保持时间,则mosquitto客户端会使用默认的保持时间。如果指定的保持时间不在5和65535之间,则会输出错误信息并返回1表示出现错误。

#ifdef WITH_TLS
		else if(!strcmp(argv[i], "--key")){
			if(i==argc-1){
				fprintf(stderr, "Error: --key argument given but no file specified.\n\n");
				return 1;
			}else{
				cfg->keyfile = strdup(argv[i+1]);
			}
			i++;
		}
		else if(!strcmp(argv[i], "--keyform")){
			if(i==argc-1){
				fprintf(stderr, "Error: --keyform argument given but no keyform specified.\n\n");
				return 1;
			}else{
				cfg->keyform = strdup(argv[i+1]);
			}
			i++;
#endif
		}

该代码段通过条件编译#ifdef WITH_TLS判断是否启用了TLS安全协议。如果启用了TLS,则可以使用"–key"和"–keyform"选项设置mosquitto客户端连接MQTT服务器时使用的SSL/TLS证书和密钥。

如果命令行参数为"–key",则需要进一步判断是否指定了SSL/TLS密钥文件。如果没有指定密钥文件,则会输出错误信息,然后返回1表示出现错误。如果指定了密钥文件,则将密钥文件保存到cfg结构体中的keyfile成员变量中。

如果命令行参数为"–keyform",则需要进一步判断是否指定了密钥格式。如果没有指定密钥格式,则会输出错误信息,然后返回1表示出现错误。如果指定了密钥格式,则将密钥格式保存到cfg结构体中的keyform成员变量中。

需要注意的是,这两个选项都用于设置SSL/TLS连接时使用的密钥文件和密钥格式。如果未指定这些选项,则mosquitto客户端会使用默认的SSL/TLS设置。

总之,该代码段的作用是判断并解析命令行参数"–key"和"–keyform",将mosquitto客户端连接MQTT服务器时使用的SSL/TLS证书和密钥格式设置为指定的值。如果未指定这些选项,则mosquitto客户端会使用默认的SSL/TLS设置。如果未启用TLS,则该代码段将被忽略。

else if(!strcmp(argv[i], "-L") || !strcmp(argv[i], "--url")){
			if(i==argc-1){
				fprintf(stderr, "Error: -L argument given but no URL specified.\n\n");
				return 1;
			} else {
				char *url = argv[i+1];
				char *topic;
				char *tmp;

				if(!strncasecmp(url, "mqtt://", 7)) {
					url += 7;
					cfg->port = 1883;
				} 
				else if(!strncasecmp(url, "mqtts://", 8)) {
#ifdef WITH_TLS
					url += 8;
					cfg->port = 8883;
					cfg->tls_use_os_certs = true;
#else
					fprintf(stderr, "Error: TLS support not available.\n\n");
					return 1;
#endif
				} else {
					fprintf(stderr, "Error: unsupported URL scheme.\n\n");
					return 1;
				}
				topic = strchr(url, '/');
				if(!topic){
					fprintf(stderr, "Error: Invalid URL for -L argument specified - topic missing.\n");
					return 1;
				}
				*topic++ = 0;

				if(cfg_add_topic(cfg, pub_or_sub, topic, "-L topic"))
					return 1;

				tmp = strchr(url, '@');
				if(tmp) {
					char *colon;
					*tmp++ = 0;
					colon = strchr(url, ':');
					if(colon) {
						*colon = 0;
						cfg->password = strdup(colon + 1);
					}
					cfg->username = strdup(url);
					url = tmp;
				}
				cfg->host = url;

				tmp = strchr(url, ':');
				if(tmp) {
					*tmp++ = 0;
					cfg->port = atoi(tmp);
				}
				/* Now we've removed the port, time to get the host on the heap */
				cfg->host = strdup(cfg->host);
			}
			i++;
		}

该代码段通过判断命令行参数的值,解析URL并将其保存到mosquitto客户端的配置结构体cfg中。

如果命令行参数为"-L"或"–url",则需要进一步判断是否指定了URL。如果没有指定URL,则会输出错误信息,然后返回1表示出现错误。如果指定了URL,则按照URL的格式解析并保存到cfg结构体中。

首先,该代码段会根据URL的协议类型(mqtt://或mqtts://)设置mosquitto客户端连接MQTT服务器的端口号。然后,该代码段会将URL解析成主机地址、端口号、用户名和密码等部分,并保存到cfg结构体中的相应成员变量中。

需要注意的是,该选项可以用于指定mosquitto客户端连接的MQTT服务器的URL地址。如果未指定此选项,则mosquitto客户端将使用默认的MQTT服务器地址和端口号。

总之,该代码段的作用是判断并解析命令行参数"-L"或"–url",将mosquitto客户端连接的MQTT服务器的URL地址和相关配置信息保存到mosquitto客户端的配置结构体cfg中。这个选项用于指定mosquitto客户端连接MQTT服务器的URL地址,以便在连接时使用。如果未指定此选项,则mosquitto客户端将使用默认的MQTT服务器地址和端口号。


else if(!strcmp(argv[i], "-l") || !strcmp(argv[i], "--stdin-line")){
			if(pub_or_sub != CLIENT_PUB){
				goto unknown_option;
			}
			if(cfg->pub_mode != MSGMODE_NONE){
				fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n");
				return 1;
			}else{
				cfg->pub_mode = MSGMODE_STDIN_LINE;
			}
		}

这个代码块处理 -l--stdin-line 参数。如果 pub_or_sub 不是 CLIENT_PUB(即不是发布客户端),则跳转到 unknown_option 标签,表示这个参数只能在发布模式下使用。如果 cfg->pub_mode 不是 MSGMODE_NONE,则表示已经有一种消息类型被发送,因此将错误信息输出到标准错误并返回 1。如果没有任何问题,将 cfg->pub_mode 设置为 MSGMODE_STDIN_LINE,表示要从标准输入中读取一行作为要发送的消息。换句话说,这段代码的作用是检查参数的正确性,并在参数正确时将客户端的发布模式设置为从标准输入中读取一行进行发送。

else if(!strcmp(argv[i], "-m") || !strcmp(argv[i], "--message")){
			if(pub_or_sub == CLIENT_SUB){
				goto unknown_option;
			}
			if(cfg->pub_mode != MSGMODE_NONE){
				fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n");
				return 1;
			}else if(i==argc-1){
				fprintf(stderr, "Error: -m argument given but no message specified.\n\n");
				return 1;
			}else{
				cfg->message = strdup(argv[i+1]);
				if(cfg->message == NULL){
					fprintf(stderr, "Error: Out of memory.\n\n");
					return 1;
				}
				szt = strlen(cfg->message);
				if(szt > MQTT_MAX_PAYLOAD){
					fprintf(stderr, "Error: Message length must be less than %u bytes.\n\n", MQTT_MAX_PAYLOAD);
					return 1;
				}
				cfg->msglen = (int )szt;
				cfg->pub_mode = MSGMODE_CMD;
			}
			i++;
		}

这段代码处理参数 -m--message。如果 pub_or_subCLIENT_SUB,表示在订阅模式下使用了这个参数,所以跳转到 unknown_option 标签,表示这个参数只能在发布模式下使用。
如果 cfg->pub_mode 不是 MSGMODE_NONE,表示已经有一种消息类型被发送,因此将错误信息输出到标准错误并返回 1。
否则,检查下一个参数 argv[i+1] 是否为空。如果是,则输出错误信息并返回 1。如果不是,则将其复制到 cfg->message 中,并计算其长度。如果消息的长度超过了 MQTT 消息最大允许的长度(MQTT_MAX_PAYLOAD),则输出错误信息并返回 1。否则,将 cfg->msglen 设置为消息的长度,并将发布模式设置为 MSGMODE_CMD,表示要从命令行参数中读取消息。
换句话说,这段代码的作用是检查参数的正确性,并在参数正确时将客户端的发布模式设置为从命令行参数中读取一条消息。如果参数错误或超过了允许的长度,则输出错误信息并返回 1。

else if(!strcmp(argv[i], "-M")){
			if(i==argc-1){
				fprintf(stderr, "Error: -M argument given but max_inflight not specified.\n\n");
				return 1;
			}else{
				tmpi = atoi(argv[i+1]);
				if(tmpi < 1){
					fprintf(stderr, "Error: Maximum inflight messages must be greater than 0.\n\n");
					return 1;
				}
				cfg->max_inflight = (unsigned int )tmpi;
			}
			i++;
		}

在 Mosquitto 中,inflight 消息是指客户端已经发送但未收到确认的消息数量,用于限制客户端可以发送的消息数量,并确保消息的可靠传递。
这段代码处理参数 -M。如果没有给出最大的 inflight 消息数,则输出错误信息并返回 1。如果给出了最大的 inflight 消息数,则将其转换为整数并保存到 cfg->max_inflight 变量中。如果指定的最大值小于 1,则输出错误信息并返回 1。也就是说,这段代码的作用是检查参数的正确性,并将最大的 inflight 消息数保存到 cfg->max_inflight 变量中。如果参数错误,则输出错误信息并返回 1。

else if(!strcmp(argv[i], "--nodelay")){
			cfg->tcp_nodelay = true;
		}

这段代码用于解析命令行参数 --nodelay 并将其保存到配置参数中。--nodelay 是一个布尔型选项,它用于设置 TCP 连接是否启用 Nagle 算法。Nagle 算法是一种网络优化算法,用于减少网络拥塞和减少网络带宽的浪费,但是它会增加数据传输的延迟,因此在某些情况下可能会影响性能。如果设置了 --nodelay 选项,Mosquitto 将禁用 Nagle 算法,从而降低延迟,提高性能。strcmp() 函数用于比较字符串,!strcmp() 表示字符串相等时返回 0,再加上取反操作 ! 后,就表示字符串不相等时返回 true。因此,当命令行参数等于 --nodelay 时,就将 tcp_nodelay 标志设置为 true。cfg->tcp_nodelay 是保存在 struct mosq_config 结构体中的一个布尔型标志,它用于表示是否启用 TCP NoDelay。如果设置了 --nodelay 参数,则将 tcp_nodelay 标志设置为 true,表示启用 TCP NoDelay。

else if(!strcmp(argv[i], "-n") || !strcmp(argv[i], "--null-message")){
			if(pub_or_sub == CLIENT_SUB){
				goto unknown_option;
			}
			if(cfg->pub_mode != MSGMODE_NONE){
				fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n");
				return 1;
			}else{
				cfg->pub_mode = MSGMODE_NULL;
			}
		}

这段代码用于解析命令行参数 -n--null-message 并将其保存到配置参数中。
-n--null-message 是一个布尔型选项,它用于指定要发送一个空消息。当指定了该选项时,Mosquitto 将发送一个空消息到指定的 MQTT 主题。
首先,检查 pub_or_sub 标志是否为 CLIENT_SUB。如果 pub_or_sub 标志为 CLIENT_SUB,则说明这是一个订阅客户端,不支持发送消息,因此跳转到 unknown_option 标签,输出错误信息。如果 pub_or_sub 标志为 CLIENT_PUB,则表示这是一个发布客户端,可以继续执行下面的代码。然后,检查 pub_mode 标志是否已经设置为某种消息类型。如果已经设置了消息类型,则输出错误信息,表示只能同时发送一种类型的消息。如果 pub_mode 标志没有设置消息类型,则将其设置为 MSGMODE_NULL,表示发送一个空消息。

else if(!strcmp(argv[i], "-N")){
			if(pub_or_sub == CLIENT_PUB){
				goto unknown_option;
			}
			cfg->eol = false;
		}

这段代码用于解析命令行参数 -N 并将其保存到配置参数中。-N 是一个布尔型选项,它用于在消息的结尾添加换行符。当指定了该选项时,Mosquitto 将在发送的每条消息的结尾添加一个换行符。
首先,检查 pub_or_sub 标志是否为 CLIENT_PUB。如果 pub_or_sub 标志为 CLIENT_PUB,则说明这是一个发布客户端,不支持在消息末尾添加换行符,因此跳转到 unknown_option 标签,输出错误信息。如果 pub_or_sub 标志为 CLIENT_SUB,则表示这是一个订阅客户端,执行cfg->eol = false。

else if(!strcmp(argv[i], "-p") || !strcmp(argv[i], "--port")){
			if(i==argc-1){
				fprintf(stderr, "Error: -p argument given but no port specified.\n\n");
				return 1;
			}else{
				cfg->port = atoi(argv[i+1]);
				if(cfg->port<0 || cfg->port>65535){
					fprintf(stderr, "Error: Invalid port given: %d\n", cfg->port);
					return 1;
				}
			}
			i++;
		}

这段代码是 client_config_line_proc 函数中用于处理 -p--port 命令行参数的部分。具体来说:

  • 如果当前命令行参数是 -p 或者 --port,那么检查下一个参数是否为端口号。如果是,则将其转换为整数并保存到 cfg->port 字段中。如果端口号不在 0-65535 之间,则打印错误信息并返回。
    这段代码的作用是根据命令行参数设置相应的配置参数,如果参数无效或者不符合要求,则会打印错误信息并返回。可以看出,client_config_line_proc 函数用于设置 MQTT 服务器的端口号,客户端可以通过此参数来指定连接的服务器端口号。这样做的好处是提高了客户端的灵活性和可配置性,使得客户端可以连接到不同的 MQTT 服务器。
else if(!strcmp(argv[i], "--pretty")){
			if(pub_or_sub == CLIENT_PUB){
				goto unknown_option;
			}
			cfg->pretty = true;
		}else if(!strcmp(argv[i], "-P") || !strcmp(argv[i], "--pw")){
			if(i==argc-1){
				fprintf(stderr, "Error: -P argument given but no password specified.\n\n");
				return 1;
			}else{
				cfg->password = strdup(argv[i+1]);
			}
			i++;
			}

这段代码是 client_config_line_proc 函数中用于处理 --pretty-P/--pw 命令行参数的部分。具体来说:

  • 如果当前命令行参数是 --pretty,那么首先判断当前配置的是发布者还是订阅者。如果是发布者,则打印错误信息并返回。如果是订阅者,则将 cfg->pretty 字段设为 true,表示在输出接收到的消息时使用漂亮格式(即格式化输出)。
  • 如果当前命令行参数是 -P 或者 --pw,那么检查下一个参数是否为密码。如果是,则将其保存到 cfg->password 字段中。

这段代码的作用是根据命令行参数设置相应的配置参数,如果参数无效或者不符合要求,则会打印错误信息并返回。可以看出,client_config_line_proc 函数根据命令行参数设置密码和消息输出格式化等配置参数。密码是在 MQTT 连接过程中进行身份验证的关键参数,格式化输出可使接收到的消息更容易阅读。这些配置参数能够使得 Mosquitto 客户端更加灵活和易用。

#ifdef WITH_SOCKS

		else if(!strcmp(argv[i], "--proxy")){
			if(i==argc-1){
				fprintf(stderr, "Error: --proxy argument given but no proxy url specified.\n\n");
				return 1;
			}else{
				if(mosquitto__parse_socks_url(cfg, argv[i+1])){
					return 1;
				}
				i++;
			}
#endif

这段代码片段中的逻辑是处理 --proxy 参数。其中,宏定义 WITH_SOCKS 是用于判断是否支持SOCKS5代理的宏定义。
--proxy 参数出现时,首先判断是否有代理URL字符串指定。如果没有,则在标准错误输出上输出错误信息并返回1。否则,调用 mosquitto__parse_socks_url 函数解析代理URL,并将解析结果保存到全局配置参数变量 cfg->psk 中。
需要注意的是,由于 --proxy 参数仅在支持SOCKS5代理时才会被处理,因此该代码段需要被包含在 #ifdef WITH_SOCKS#endif 之间。如果当前的Mosquitto客户端实例不支持SOCKS5代理,则这段代码不会被编译和执行,避免了不必要的开销和错误。

#ifdef FINAL_WITH_TLS_PSK
		}else if(!strcmp(argv[i], "--psk")){
			if(i==argc-1){
				fprintf(stderr, "Error: --psk argument given but no key specified.\n\n");
				return 1;
			}else{
				cfg->psk = strdup(argv[i+1]);
			}
			i++;
		}
	

这段代码片段是用于处理 --psk 参数的,其中 #ifdef FINAL_WITH_TLS_PSK 宏定义用于判断是否支持预共享密钥(PSK)的TLS连接。
当命令行参数为 --psk 时,首先判断该参数是否有密钥字符串指定。如果没有,则在标准错误输出上输出错误信息并返回1;否则,将密钥字符串赋值给 cfg->psk
需要注意的是,根据编译条件, --psk 参数仅在支持PSK的TLS连接时才会被处理,

#ifdef FINAL_WITH_TLS_PSK
else if(!strcmp(argv[i], "--psk-identity")){
			if(i==argc-1){
				fprintf(stderr, "Error: --psk-identity argument given but no identity specified.\n\n");
				return 1;
			}else{
				cfg->psk_identity = strdup(argv[i+1]);
			}
			i++;
			}
#endif

这段代码用于处理 --psk-identity 命令行参数。具体来说:

  • 如果当前命令行参数是 --psk-identity,那么首先判断是否启用了 TLS-PSK。如果未启用,则直接跳过。如果已启用,则检查下一个参数是否为 PSK identity,如果是,则将其保存到 cfg->psk_identity 字段中。
    这段代码的作用是根据命令行参数设置相应的配置参数,如果参数无效或者不符合要求,则会打印错误信息并返回。可以看出,client_config_line_proc 函数用于处理 TLS-PSK 相关的配置参数。在 TLS-PSK 模式中,客户端和服务器通过预共享密钥来完成身份验证和安全通信。此处通过命令行参数设置 PSK identity,用于 TLS-PSK 模式下的客户端身份验证。如果未启用 TLS-PSK,则此处代码不会执行。

		else if(!strcmp(argv[i], "-q") || !strcmp(argv[i], "--qos")){
			if(i==argc-1){
				fprintf(stderr, "Error: -q argument given but no QoS specified.\n\n");
				return 1;
			}else{
				cfg->qos = atoi(argv[i+1]);
				if(cfg->qos<0 || cfg->qos>2){
					fprintf(stderr, "Error: Invalid QoS given: %d\n", cfg->qos);
					return 1;
				}
			}
			i++;
		}

这段代码用于处理 -q--qos 命令行参数。具体来说:

  • 如果当前命令行参数是 -q 或者 --qos,那么检查下一个参数是否为 QoS 等级值。如果是,则将其转换为整数并保存到 cfg->qos 字段中。还需要检查 QoS 等级值,如果不在 0-2 之间,则打印错误信息并返回。
else if(!strcmp(argv[i], "--quiet")){
			cfg->quiet = true;
		}

这段代码用来解析 --quiet 命令行参数的部分。
代码首先通过 strcmp 函数判断当前处理的参数是否是 --quiet,如果是,则将 mosq_config 结构体中的 quiet 成员变量设置为 true,表示需要禁用所有除错误消息之外的输出。

else if(!strcmp(argv[i], "-r") || !strcmp(argv[i], "--retain")){
			if(pub_or_sub != CLIENT_PUB){
				goto unknown_option;
			}
			cfg->retain = 1;
		}

这段代码用于处理 -r--retain 命令行参数。具体来说:

  • 如果当前命令行参数是 -r 或者 --retain,那么首先判断当前配置的是发布者还是订阅者。如果是订阅者,则打印错误信息并返回。如果是发布者,则将 cfg->retain 字段设为 1,表示发布消息时保存保留消息。
else if(!strcmp(argv[i], "-R")){
			if(pub_or_sub == CLIENT_PUB){
				goto unknown_option;
			}
			cfg->no_retain = true;
			cfg->sub_opts |= MQTT_SUB_OPT_SEND_RETAIN_NEVER;
		}

这段代码用于处理 -R 命令行参数。具体来说:

  • 如果当前命令行参数是 -R,那么首先判断当前配置的是发布者还是订阅者。如果是发布者,则打印错误信息并返回。如果是订阅者,则将 cfg->no_retain 标志位设为 true,表示不保存保留消息。同时,将 cfg->sub_opts 字段的 MQTT_SUB_OPT_SEND_RETAIN_NEVER 位设为 1,表示在订阅主题时不接收保留消息。
else if(!strcmp(argv[i], "--random-filter")){
			if(pub_or_sub != CLIENT_SUB){
				goto unknown_option;
			}
			if(i==argc-1){
				fprintf(stderr, "Error: --random-filter argument given but no chance specified.\n\n");
				return 1;
			}else{
				cfg->random_filter = (int)(10.0*atof(argv[i+1]));
				if(cfg->random_filter > 10000 || cfg->random_filter < 1){
					fprintf(stderr, "Error: --random-filter chance must be between 0.1-100.0\n\n");
					return 1;
				}
			}
			i++;
		}

这段代码用于处理 --random-filter 命令行参数。具体来说:

  • 如果当前命令行参数是 --random-filter,那么首先判断当前配置的是发布者还是订阅者。如果是订阅者,则打印错误信息并返回。如果是发布者,则检查下一个参数是否为机率值。如果是,则将机率值乘以 10 并转换为整数保存到 cfg->random_filter 字段中。如果机率值不在 0.1-100.0 之间,则打印错误信息并返回。
else if(!strcmp(argv[i], "--remove-retained")){
			if(pub_or_sub != CLIENT_SUB){
				goto unknown_option;
			}
			cfg->remove_retained = true;
		}else if(!strcmp(argv[i], "--repeat")){
			if(pub_or_sub != CLIENT_PUB){
				goto unknown_option;
			}
			if(i==argc-1){
				fprintf(stderr, "Error: --repeat argument given but no count specified.\n\n");
				return 1;
			}else{
				cfg->repeat_count = atoi(argv[i+1]);
				if(cfg->repeat_count < 1){
					fprintf(stderr, "Error: --repeat argument must be >0.\n\n");
					return 1;
				}
			}
			i++;
		}

这段代码用于处理 --remove-retained--repeat 命令行参数的部分。具体来说:

  • 如果当前命令行参数是 --remove-retained,那么首先判断当前配置的是发布者还是订阅者。如果是订阅者,则打印错误信息并返回。如果是发布者,则将 cfg->remove_retained 标志位设为 true,表示在发布消息时移除保留消息。
  • 如果当前命令行参数是 --repeat,那么首先判断当前配置的是发布者还是订阅者。如果是订阅者,则打印错误信息并返回。如果是发布者,则检查下一个参数是否是重复发送消息的次数,如果是则将其转换为整数并保存到 cfg->repeat_count 字段中。如果下一个参数不是整数,则打印错误信息并返回。
else if(!strcmp(argv[i], "--repeat-delay")){
			if(pub_or_sub != CLIENT_PUB){
				goto unknown_option;
			}
			if(i==argc-1){
				fprintf(stderr, "Error: --repeat-delay argument given but no time specified.\n\n");
				return 1;
			}else{
				f = (float )atof(argv[i+1]);
				if(f < 0.0f){
					fprintf(stderr, "Error: --repeat-delay argument must be >=0.0.\n\n");
					return 1;
				}
				f *= 1.0e6f;
				cfg->repeat_delay.tv_sec = (int)f/1000000;
				cfg->repeat_delay.tv_usec = (int)f%1000000;
			}
			i++;
		}

这段代码用于处理 --retain-as-published 命令行参数的部分。具体来说:

  • 如果当前命令行参数是 --retain-as-published,那么首先判断当前配置的是发布者还是订阅者。如果是发布者,则打印错误信息并返回。如果是订阅者,则将 cfg->sub_opts 字段的 MQTT_SUB_OPT_RETAIN_AS_PUBLISHED 位设为 1,表示接收到保留消息后将其视为新发布的消息。
else if(!strcmp(argv[i], "--retain-as-published")){
			if(pub_or_sub == CLIENT_PUB){
				goto unknown_option;
			}
			cfg->sub_opts |= MQTT_SUB_OPT_RETAIN_AS_PUBLISHED;
		}

这段代码用于处理 --retain-as-published 命令行参数的部分。具体来说:

  • 如果当前命令行参数是 --retain-as-published,那么首先判断当前配置的是发布者还是订阅者。如果是发布者,则打印错误信息并返回。如果是订阅者,则将 cfg->sub_opts 字段的 MQTT_SUB_OPT_RETAIN_AS_PUBLISHED 位设为 1,表示接收到保留消息后将其视为新发布的消息。
else if(!strcmp(argv[i], "--retained-only")){
			if(pub_or_sub != CLIENT_SUB){
				goto unknown_option;
			}
			cfg->retained_only = true;
		}

这段代码处理命令行参数中的 --retained-only 选项,用于指示 MQTT 客户端只接收保留消息。
如果 MQTT 客户端是发布者(即 pub_or_subCLIENT_PUB),则跳转到 unknown_option 标签处。这是因为 --retained-only 选项只适用于订阅者(即 pub_or_subCLIENT_SUB)。
如果命令行参数为 --retained-only,则将 cfg->retained_only 设置为 true,表示 MQTT 客户端只接收保留消息。

else if(!strcmp(argv[i], "-s") || !strcmp(argv[i], "--stdin-file")){
			if(pub_or_sub == CLIENT_SUB){
				goto unknown_option;
			}
			if(cfg->pub_mode != MSGMODE_NONE){
				fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n");
				return 1;
			}else{
				cfg->pub_mode = MSGMODE_STDIN_FILE;
			}
			}

这段代码处理命令行参数中的 -s--stdin-file 选项,用于指示 MQTT 客户端从标准输入中读取消息内容。
如果 MQTT 客户端是订阅者(即 pub_or_subCLIENT_SUB),则跳转到 unknown_option 标签处。这是因为 -s--stdin-file 选项只适用于发布者(即 pub_or_subCLIENT_PUB)。
如果 cfg->pub_mode 不等于 MSGMODE_NONE,则说明已经指定了一种消息模式,不能同时指定多种模式。在这种情况下,将输出错误信息并返回错误代码。
如果命令行参数为 -s--stdin-file,则将 cfg->pub_mode 设置为 MSGMODE_STDIN_FILE,表示 MQTT 客户端将从标准输入中读取消息内容。

#ifdef WITH_SRV
		else if(!strcmp(argv[i], "-S")){
			cfg->use_srv = true;
#endif
		}

这段代码处理命令行参数中的 -S 选项,用于指示 MQTT 客户端使用 DNS SRV 记录来查找 MQTT 代理服务器的地址和端口。
如果命令行参数为 -S,则将 cfg->use_srv 设置为 true,这个字段在 struct mosq_config 中定义。当 cfg->use_srvtrue 时,MQTT 客户端会使用 DNS SRV 记录查找 MQTT 代理服务器的地址和端口。
这段代码在编译选项 WITH_SRV 启用时才会被编译。如果编译选项中未启用 WITH_SRV,则编译器会忽略这段代码。
WITH_SRV 是 Mosquitto 的编译选项之一,用于启用 MQTT 客户端使用 DNS SRV 记录查找 MQTT 代理服务器的地址和端口。


else if(!strcmp(argv[i], "-t") || !strcmp(argv[i], "--topic")){
			if(i==argc-1){
				fprintf(stderr, "Error: -t argument given but no topic specified.\n\n");
				return 1;
			}else{
				if(cfg_add_topic(cfg, pub_or_sub, argv[i + 1], "-t"))
					return 1;
				i++;
			}
		}

这段代码处理命令行参数中的 -t--topic 选项,用于指定MQTT客户端订阅或发布的主题名称。
如果命令行参数为 -t--topic,则会检查下一个参数是否为主题名称,如果没有则会打印错误信息并返回错误代码。否则,会将主题名称添加到 cfg->topics 数组中,该数组在 struct mosq_config 中定义。cfg_add_topic() 函数将负责向 cfg->topics 数组中添加主题。

else if(!strcmp(argv[i], "-T") || !strcmp(argv[i], "--filter-out")){
			if(pub_or_sub != CLIENT_SUB){
				goto unknown_option;
			}
			if(i==argc-1){
				fprintf(stderr, "Error: -T argument given but no topic filter specified.\n\n");
				return 1;
			}else{
				if(mosquitto_validate_utf8(argv[i+1], (int )strlen(argv[i+1]))){
					fprintf(stderr, "Error: Malformed UTF-8 in -T argument.\n\n");
					return 1;
				}
				if(mosquitto_sub_topic_check(argv[i+1]) == MOSQ_ERR_INVAL){
					fprintf(stderr, "Error: Invalid filter topic '%s', are all '+' and '#' wildcards correct?\n", argv[i+1]);
					return 1;
				}
				cfg->filter_out_count++;
				cfg->filter_outs = realloc(cfg->filter_outs, (size_t )cfg->filter_out_count*sizeof(char *));
				if(!cfg->filter_outs){
					fprintf(stderr, "Error: Out of memory.\n");
					return 1;
				}
				cfg->filter_outs[cfg->filter_out_count-1] = strdup(argv[i+1]);
			}
			i++;
			}

这段代码处理命令行参数中的 -T--filter-out 选项,用于订阅时指定需要过滤掉的主题名称。

如果命令行参数为 -T--filter-out,则会检查下一个参数是否为要过滤掉的主题名称,如果没有则会打印错误信息并返回错误代码。否则,会将要过滤掉的主题名称添加到 cfg->filter_outs 数组中,该数组在 struct mosq_config 中定义。

注意,这段代码只能在 MQTT 客户端订阅时使用。如果在发布时使用,则会跳转到 unknown_option 标签处并返回错误代码。

#ifdef WITH_TLS
		else if(!strcmp(argv[i], "--tls-alpn")){
			if(i==argc-1){
				fprintf(stderr, "Error: --tls-alpn argument given but no protocol specified.\n\n");
				return 1;
			}else{
				cfg->tls_alpn = strdup(argv[i+1]);
			}
			i++;
		}

这段代码是在编译时启用了TLS支持的情况下,对命令行参数中的 --tls-alpn 选项进行解析,该选项用于指定TLS ALPN协议。
如果命令行参数为 --tls-alpn,则会检查 --tls-alpn 后面是否跟着一个ALPN协议的值。如果没有,则会打印错误信息并返回错误代码。否则,会将ALPN协议保存到 cfg->tls_alpn 字段中。

else if(!strcmp(argv[i], "--tls-engine")){
			if(i==argc-1){
				fprintf(stderr, "Error: --tls-engine argument given but no engine_id specified.\n\n");
				return 1;
			}else{
				cfg->tls_engine = strdup(argv[i+1]);
			}
			i++;
		}else if(!strcmp(argv[i], "--tls-engine-kpass-sha1")){
			if(i==argc-1){
				fprintf(stderr, "Error: --tls-engine-kpass-sha1 argument given but no kpass sha1 specified.\n\n");
				return 1;
			}else{
				cfg->tls_engine_kpass_sha1 = strdup(argv[i+1]);
			}
			i++;
		}

这段代码是在编译时启用了TLS支持的情况下,对命令行参数中的 --tls-engine--tls-engine-kpass-sha1 选项进行解析,这两个选项都与使用加密引擎相关。
如果命令行参数为 --tls-engine,则会检查 --tls-engine 后面是否跟着一个引擎ID的值。如果没有,则会打印错误信息并返回错误代码。否则,会将引擎ID保存到 cfg->tls_engine 字段中。
如果命令行参数为 --tls-engine-kpass-sha1,则会检查 --tls-engine-kpass-sha1 后面是否跟着一个密钥口令SHA1值的值。如果没有,则会打印错误信息并返回错误代码。否则,会将密钥口令SHA1值保存到 cfg->tls_engine_kpass_sha1 字段中。

else if(!strcmp(argv[i], "--tls-use-os-certs")){
			cfg->tls_use_os_certs = true;
		}else if(!strcmp(argv[i], "--tls-version")){
			if(i==argc-1){
				fprintf(stderr, "Error: --tls-version argument given but no version specified.\n\n");
				return 1;
			}else{
				cfg->tls_version = strdup(argv[i+1]);
			}
			i++;
			}
#endif

这段代码是在编译时启用了TLS支持的情况下,对命令行参数中的 --tls-use-os-certs--tls-version 选项进行解析,这两个选项都与使用加密协议相关。
如果命令行参数为 --tls-use-os-certs,则会将 cfg->tls_use_os_certs 设置为 true,表示使用操作系统提供的SSL证书。
如果命令行参数为 --tls-version,则会检查 --tls-version 后面是否跟着一个TLS协议版本的值。如果没有,则会打印错误信息并返回错误代码。否则,会将TLS协议版本保存到 cfg->tls_version 字段中。

		else if(!strcmp(argv[i], "-U") || !strcmp(argv[i], "--unsubscribe")){
			if(pub_or_sub != CLIENT_SUB){
				goto unknown_option;
			}
			if(i==argc-1){
				fprintf(stderr, "Error: -U argument given but no unsubscribe topic specified.\n\n");
				return 1;
			}else{
				if(mosquitto_validate_utf8(argv[i+1], (int )strlen(argv[i+1]))){
					fprintf(stderr, "Error: Malformed UTF-8 in -U argument.\n\n");
					return 1;
				}
				if(mosquitto_sub_topic_check(argv[i+1]) == MOSQ_ERR_INVAL){
					fprintf(stderr, "Error: Invalid unsubscribe topic '%s', are all '+' and '#' wildcards correct?\n", argv[i+1]);
					return 1;
				}
				cfg->unsub_topic_count++;
				cfg->unsub_topics = realloc(cfg->unsub_topics, (size_t )cfg->unsub_topic_count*sizeof(char *));
				if(!cfg->unsub_topics){
					fprintf(stderr, "Error: Out of memory.\n");
					return 1;
				}
				cfg->unsub_topics[cfg->unsub_topic_count-1] = strdup(argv[i+1]);
			}
			i++;
		}

这段代码是对命令行参数中的 -U--unsubscribe 选项进行解析,该选项用于指定取消订阅的主题列表。

如果命令行参数为 -U--unsubscribe,则会检查 pub_or_sub 的值,如果不是订阅者(SUBSCRIBE)则跳转到 unknown_option 处理。然后,会检查 -U--unsubscribe 后面是否跟着一个主题的值。如果没有,则会打印错误信息并返回错误代码。否则,会将主题保存到 cfg->unsub_topics 字段中,并增加 cfg->unsub_topic_count 的值。

在处理多个取消订阅主题时,每个主题都会被分配一个字符串内存,并将其保存到 cfg->unsub_topics 数组中。这个数组的大小将根据需要动态增加。

else if(!strcmp(argv[i], "-u") || !strcmp(argv[i], "--username")){
			if(i==argc-1){
				fprintf(stderr, "Error: -u argument given but no username specified.\n\n");
				return 1;
			}else{
				cfg->username = strdup(argv[i+1]);
			}
			i++;
		}

这段代码是对命令行参数中的 -u--username 选项进行解析,该选项用于指定MQTT客户端的用户名。
如果命令行参数为 -u--username,则会检查 -u--username 后面是否跟着一个用户名的值。如果没有,则会打印错误信息并返回错误代码。否则,会将用户名保存到 cfg->username 字段中。

else if(!strcmp(argv[i], "--unix")){
			if(i==argc-1){
				fprintf(stderr, "Error: --unix argument given but no socket path specified.\n\n");
				return 1;
			}else{
				cfg->host = strdup(argv[i+1]);
				cfg->port = 0;
			}
			i++;
		}

这段代码是对命令行参数中的 --unix 选项进行解析,该选项用于指定使用UNIX域套接字进行连接。
如果命令行参数为 --unix,则会检查 --unix 后面是否跟着一个套接字路径的值。如果没有,则会打印错误信息并返回错误代码。否则,会将套接字路径保存到 cfg->host 字段中,并将端口号设置为0。

else if(!strcmp(argv[i], "-V") || !strcmp(argv[i], "--protocol-version")){
			if(i==argc-1){
				fprintf(stderr, "Error: --protocol-version argument given but no version specified.\n\n");
				return 1;
			}else{
				if(!strcmp(argv[i+1], "mqttv31") || !strcmp(argv[i+1], "31")){
					cfg->protocol_version = MQTT_PROTOCOL_V31;
				}else if(!strcmp(argv[i+1], "mqttv311") || !strcmp(argv[i+1], "311")){
					cfg->protocol_version = MQTT_PROTOCOL_V311;
				}else if(!strcmp(argv[i+1], "mqttv5") || !strcmp(argv[i+1], "5")){
					cfg->protocol_version = MQTT_PROTOCOL_V5;
				}else{
					fprintf(stderr, "Error: Invalid protocol version argument given.\n\n");
					return 1;
				}
				i++;
			}
		}

这段代码是对命令行参数中的 -V--protocol-version 选项进行解析,该选项用于指定MQTT协议的版本号。
如果命令行参数为 -V--protocol-version,则会检查 -V--protocol-version 后面是否跟着一个MQTT协议版本号的值。如果没有,则会打印错误信息并返回错误代码。否则,会将MQTT协议版本号保存到 cfg->protocol_version 字段中。
该段代码会先将命令行参数的值转换为标准的MQTT协议版本号,即 MQTT_PROTOCOL_V31MQTT_PROTOCOL_V311MQTT_PROTOCOL_V5,然后保存到 cfg->protocol_version 字段中。如果转换失败,则会打印错误信息并返回错误代码。

else if(!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose")){
			if(pub_or_sub == CLIENT_PUB){
				goto unknown_option;
			}
			cfg->verbose = 1;
		}else if(!strcmp(argv[i], "--version")){
			return 3;
		}

这段代码是对命令行参数中的 -v--verbose 选项以及 --version 选项进行解析。
如果命令行参数为 -v--verbose,则会检查 pub_or_sub 的值,如果是发布者(PUBLISH)则跳转到 unknown_option 处理,否则会将 cfg->verbose 设置为1,表示需要详细输出调试信息。
如果命令行参数为 --version,则会直接返回3,表示需要打印Mosquitto版本信息。

else if(!strcmp(argv[i], "-W")){
			if(pub_or_sub == CLIENT_PUB){
				goto unknown_option;
			}else{
				if(i==argc-1){
					fprintf(stderr, "Error: -W argument given but no timeout specified.\n\n");
					return 1;
				}else{
					tmpi = atoi(argv[i+1]);
					if(tmpi < 1){
						fprintf(stderr, "Error: Invalid timeout \"%d\".\n\n", tmpi);
						return 1;
					}
					cfg->timeout = (unsigned int )tmpi;
				}
				i++;
			}
		}

这段代码是对命令行参数中的 -W 选项进行解析,该选项用于指定客户端的超时时间。
如果命令行参数为 -W,则会检查 pub_or_sub 的值,如果是发布者(PUBLISH)则跳转到 unknown_option 处理,否则会检查 -W 后面是否跟着一个超时时间的值。如果没有,则会打印错误信息并返回错误代码。否则,会将超时时间保存到 cfg->timeout 字段中。

else if(!strcmp(argv[i], "--will-payload")){
			if(i==argc-1){
				fprintf(stderr, "Error: --will-payload argument given but no will payload specified.\n\n");
				return 1;
			}else{
				cfg->will_payload = strdup(argv[i+1]);
				cfg->will_payloadlen = (int )strlen(cfg->will_payload);
			}
			i++;
		}

这段代码是对命令行参数中的 --will-payload 选项进行解析,该选项用于指定客户端的遗嘱消息的负载内容。
如果命令行参数为 --will-payload,则函数会检查 --will-payload 后面是否跟着一个负载内容的值。如果没有,则会打印错误信息并返回错误代码。否则,会将负载内容保存到 cfg->will_payload 字段中,并将负载内容长度保存到 cfg->will_payloadlen 字段中。


else if(!strcmp(argv[i], "--will-qos")){
			if(i==argc-1){
				fprintf(stderr, "Error: --will-qos argument given but no will QoS specified.\n\n");
				return 1;
			}else{
				cfg->will_qos = atoi(argv[i+1]);
				if(cfg->will_qos < 0 || cfg->will_qos > 2){
					fprintf(stderr, "Error: Invalid will QoS %d.\n\n", cfg->will_qos);
					return 1;
				}
			}
			i++;
		}

这段代码是对命令行参数中的 --will-qos 选项进行解析,该选项用于指定客户端的遗嘱消息的服务质量等级(QoS)。
如果命令行参数为 --will-qos,则函数会检查 --will-qos 后面是否跟着一个QoS值。如果没有,则会打印错误信息并返回错误代码。否则,会使用 atoi 函数将参数值转换为整数,赋值给cfg->will_qos,之后检查转换后的值是否合法,即是否在0~2之间。如果不合法,则会打印错误信息并返回错误代码。

else if(!strcmp(argv[i], "--will-retain")){
			cfg->will_retain = true;
		}else if(!strcmp(argv[i], "--will-topic")){
			if(i==argc-1){
				fprintf(stderr, "Error: --will-topic argument given but no will topic specified.\n\n");
				return 1;
			}else{
				if(mosquitto_validate_utf8(argv[i+1], (int )strlen(argv[i+1]))){
					fprintf(stderr, "Error: Malformed UTF-8 in --will-topic argument.\n\n");
					return 1;
				}
				if(mosquitto_pub_topic_check(argv[i+1]) == MOSQ_ERR_INVAL){
					fprintf(stderr, "Error: Invalid will topic '%s', does it contain '+' or '#'?\n", argv[i+1]);
					return 1;
				}
				cfg->will_topic = strdup(argv[i+1]);
			}
			i++;
		}

这段代码是对命令行参数中的 --will-retain--will-topic 选项进行解析,这两个选项用于指定客户端的遗嘱消息的保留标志和主题。如果命令行参数为 --will-retain,则函数会将 cfg->will_retain 设置为 true,表示遗嘱消息需要保留。如果命令行参数为 --will-topic,则函数会检查 --will-topic 后面是否跟着一个遗嘱消息主题的值。如果没有,则会打印错误信息并返回错误代码。否则,会检查遗嘱消息主题是否符合UTF-8编码规范和MQTT主题格式规范。如果不符合,则会打印错误信息并返回错误代码。如果符合,则会将遗嘱消息主题保存到 cfg->will_topic 字段中。

else if(!strcmp(argv[i], "-x")){
			if(i==argc-1){
				fprintf(stderr, "Error: -x argument given but no session expiry interval specified.\n\n");
				return 1;
			}else{
				if(!strcmp(argv[i+1], "∞")){
					cfg->session_expiry_interval = UINT32_MAX;
				}else{
					char *endptr = NULL;
					cfg->session_expiry_interval = strtol(argv[i+1], &endptr, 0);
					if(endptr == argv[i+1] || endptr[0] != '\0'){
						/* Entirety of argument wasn't a number */
						fprintf(stderr, "Error: session-expiry-interval not a number.\n\n");
						return 1;
					}
					if(cfg->session_expiry_interval > UINT32_MAX || cfg->session_expiry_interval < -1){
						fprintf(stderr, "Error: session-expiry-interval out of range.\n\n");
						return 1;
					}
					if(cfg->session_expiry_interval == -1){
						/* Convenience value for infinity. */
						cfg->session_expiry_interval = UINT32_MAX;
					}
				}
			}
			i++;
		}

这段代码是对命令行参数中的 -x 选项进行解析,该选项用于指定客户端的会话过期时间。如果命令行参数为 -x,则函数会检查 -x 后面是否跟着一个会话过期时间的值。如果没有,则会打印错误信息并返回错误代码。否则,会解析 -x 后面的参数值,并将其保存到 cfg->session_expiry_interval 字段中。如果 -x 后面的参数值为无限大,则会将 cfg->session_expiry_interval 设置为 UINT32_MAX。否则,会使用 strtol 函数将参数值转换为长整型,并检查转换后的值是否合法,即是否在可接受的范围内。如果不合法,则会打印错误信息并返回错误代码。如果转换后的值为 -1,则会将 cfg->session_expiry_interval 设置为 UINT32_MAX,即无限大。这是为了方便使用而设置的。

else{
			goto unknown_option;
		}
	}

	return MOSQ_ERR_SUCCESS;

unknown_option:
	fprintf(stderr, "Error: Unknown option '%s'.\n",argv[i]);
	return 1;
}

3.struct mosq_config成员变量分类

在mosquitto 2.0.15客户端源码中,struct mosq_config 是一个重要的数据结构,它包含了mosquitto客户端配置的所有选项和参数。下面是 struct mosq_config 中的成员变量,大致可以分为以下几类:

(1)基本信息:包括客户端id、用户名和密码等基本的连接信息。

struct mosq_config 中的基本信息类成员变量包括:

  1. id:客户端标识符,用于标识客户端和在MQTT网络中唯一标识客户端。通常是一个字符串,不能超过23个字符。
  2. usernamepassword:连接的用户名和密码,用于进行身份验证。
  3. protocol_version:MQTT协议的版本号,可以是 MQTTv31 或 MQTTv311。
  4. clean_session:一个bool值,指示客户端连接时是否应该清除之前的会话信息。
  5. keepalive:指示客户端应该多长时间发送一次心跳包以保持连接的时间。
  6. will_topicwill_payloadwill_payloadlenwill_qoswill_retain:遗嘱消息的相关信息,即在客户端异常断开连接时发送的消息信息。

这些基本信息类成员变量用于配置mosquitto客户端的基本连接信息和参数,例如设置客户端的唯一标识符、用户名和密码,指定MQTT协议的版本和心跳包的间隔时间等。其中一些成员变量如遗嘱消息的相关信息,可以用于实现一些特定的场景和需求,例如当客户端意外断开连接时发送通知消息。

(2)连接选项:包括连接的协议、质量等级、保持连接时间等选项。

struct mosq_config 中的连接选项类成员变量包括:

  1. debug:一个bool值,指示是否启用调试模式,启用后会输出调试信息。
  2. max_inflight:指示允许同时发送给服务器的最大消息数量。
  3. msg_counttopicstopic_count:允许客户端订阅多个主题。
  4. qos:客户端的默认QoS级别,可选值为0、1和2。
  5. retain:一个bool值,指示是否在发布消息时应该保留它们。
  6. no_retain_available:一个bool值,指示是否应禁用 MQTTv5 中的保留消息功能。
  7. verbose:一个bool值,指示是否启用详细模式,启用后会输出更多信息。
  8. retry_intervalretry_max_interval:客户端断线重连时的重连间隔时间和最大重连间隔时间。
  9. connection_messages:一个bool值,指示是否发送MQTT CONNECT消息。
  10. quiet:一个bool值,指示是否启用静默模式,启用后会禁用除错误消息之外的所有消息输出。
  11. bind_address:绑定客户端网络接口的IP地址。
  12. clientid_prefixes:允许客户端指定一个或多个客户端ID前缀,用于生成唯一的客户端ID。
  13. unclean_shutdown:一个bool值,指示客户端是否在非正常断开连接时尝试清理遗留的会话信息。
  14. notification_topic:客户端断开连接时发送通知消息的主题。
  15. max_packet_size:指定发送和接收的MQTT数据包的最大大小。
  16. recv_maximumsend_maximum:指定客户端接收和发送的MQTT数据包数量的最大值。

在连接选项类成员变量中,连接的协议部分涉及到客户端订阅主题的相关参数,包括 msg_counttopicstopic_count。其中 msg_count 表示客户端已经接收到的消息数量,topics 表示客户端订阅的主题列表,topic_count 表示客户端订阅的主题数量。这些参数允许客户端订阅多个主题,并在订阅的主题上接收到消息。此外,客户端的默认QoS级别、是否保留发布的消息以及是否禁用MQTTv5中的保留消息功能等参数,也都影响连接的协议。

(3)日志记录选项:包括是否记录日志以及日志输出的级别等选项。

struct mosq_config 中的日志记录选项类成员变量包括:

  1. log_timestamp:一个bool值,指示是否在日志中包括时间戳。
  2. log_type:一个bool值,指示是否在日志中包括消息类型。
  3. log_dest_set:一个bool值,指示是否在日志中包括日志输出目标。
  4. log_dest:指定日志输出目标,可选值为 MQTT3_LOG_NONE(无日志)、MQTT3_LOG_SYSLOG(系统日志)和MQTT3_LOG_STDERR(标准错误输出)。
  5. log_filelog_fptr:指定日志文件名和文件指针,用于将日志输出到文件中。
  6. log_facility:指定系统日志使用的设施。
  7. log_level:指定日志输出的级别,可选值为MQTT3_LOG_INFOMQTT3_LOG_NOTICEMQTT3_LOG_WARNINGMQTT3_LOG_ERRMQTT3_LOG_DEBUG
  8. log_priorities_countlog_priorities:允许客户端指定优先级列表,这些优先级将被记录到日志中。

这些日志记录选项成员变量用于控制mosquitto客户端的日志记录行为。例如,通过启用 log_typelog_timestamp 成员变量,可以在日志中记录消息的类型和时间戳,以便更好地理解和跟踪消息流。通过指定 log_destlog_filelog_fptr 成员变量,可以将日志输出到不同的目标中,例如标准错误输出或日志文件。此外,还可以通过指定日志输出的级别和优先级列表,来过滤和控制日志输出的内容和格式。

(4)SSL/TLS选项:包括证书、密钥文件以及加密算法等选项。

struct mosq_config 中的 SSL/TLS 选项成员变量包括:

  1. require_certificate:一个bool值,指示是否需要服务器发送其SSL/TLS证书。
  2. tls_use_os_certs:一个bool值,指示是否使用操作系统提供的SSL/TLS证书。
  3. tls_engine_providertls_keyform_enginetls_disable_pkcs11tls_keyform_pref:用于设置 SSL/TLS 引擎和密钥存储的相关选项。
  4. cafilecapathcertfilekeyfiletls_versiontls_keyformtls_enginetls_engine_kpass_sha1tls_cipherstls_psk_identitytls_psktls_insecuretls_ocsp_required:用于设置 SSL/TLS 的相关参数和选项,包括证书、密钥文件、加密算法、协议版本等等。

SSL/TLS 选项成员变量用于配置mosquitto客户端在与MQTT服务器之间建立安全连接时所需的SSL/TLS证书、密钥文件和加密算法等选项和参数。其中一些成员变量用于指定证书和密钥文件的路径,例如 cafilecapathcertfilekeyfile,它们分别指定 CA 证书文件、CA 证书目录、客户端证书文件和客户端私钥文件的路径。另一些成员变量用于指定加密算法和协议版本,例如 tls_versiontls_ciphers,它们分别指定所使用的 SSL/TLS 协议版本和加密算法的优先级。此外,一些成员变量还允许客户端指定是否需要服务器发送其SSL/TLS证书,以及是否使用操作系统提供的 SSL/TLS 证书等。这些选项和参数在建立安全连接时起到关键作用,以确保通信的机密性、完整性和可靠性。

以上是 struct mosq_config 中的主要成员变量。它们覆盖了mosquitto客户端的所有选项和参数,允许开发人员在编译和运行mosquitto客户端时设置和修改这些选项和参数,以便满足特定的需求和场景。其中一些选项和参数可能会在特定情况下变得特别重要,例如 SSL/TLS 选项对于安全连接至关重要。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值