前词

由于前些天做的mqtt连接云平台项目测试过程中,发现了自身的代码在不良环境下,例如:网络断开、服务器断开的情况下,mqtt客户端无法感知连接已失效,仍然会继续向对端publish success。且在重新连接网络成功后,一下子重新往对端发送在连接失效的时间段内的发送数据,从而造成接收重复。

所以,自己去下载了mosquitto的源码,进行了系列分析。但是,由于本身能力有限,也不算很理解,如果有大哥是有了解的,还望私聊一起探索。

附上源码地址:https://github.com/eclipse/mosquitto

主要目录

主要需要关注的有/mosquitto/src、/mosquitto/lib、/mosquitto/client。

其中/src和/lib目录下主要放置服务端(Broker)的实现代码以及部分底层与网络相关的操作;client目录主要是订阅客户端和发布客户端的实现源码。

mosquitto_internal.h定义各种数据结构
mosquitto:外部调用接口
memory_mosq:内存分配处理,可记录内存用量
net_mosq:网络基础操作,tcp 创建,关闭等;打包/解包数据,向_mosquitto_packet 中写入/读取各种数据
send_mosq:主要实现发送请求逻辑(协议组包),实际命令请求实现组包
send_client_mosq:与 send_mosq 类似,主要实现客户端常用高频使用接口;
messages_mosq:主要针对消息的实现(PUBLISH,PUBACK,PUBREL…)
read_handle:处理收到的数据包,根据数据包类型做相应处理。

重要的数据结构

会话相关属性(上下文):主要用于保存客户端连接的所有信息,例如用户id,用户名,客户端socket,ip地址,密码,保持连接的时间值等

struct mosquitto {
	mosq_sock_t sock;//mosquitto服务器程序与该客户端连接通信所用的socket描述符
#ifndef WITH_BROKER
	mosq_sock_t sockpairR, sockpairW;// socket管道通知:非阻塞模式时,通知用,在mosquitto_loop 调用发送,
#endif
#if defined(__GLIBC__) && defined(WITH_ADNS)
	struct gaicb *adns; /* For getaddrinfo_a */
#endif
	enum mosquitto__protocol protocol;
	char *address;//该客户端的IP地址
	char *id;//该客户端登陆mosquitto程序时所提供的ID值,该值与其他的客户端不能重复
	char *username;//username和password用于记录客户端登陆时所提供的用户名和密码
	char *password;
	uint16_t keepalive;//客户端需在此时间内向mosquitto服务器程序发送一条ping/pong消息
	uint16_t last_mid;//最后一个消息id,发消息后++
	enum mosquitto_client_state state;
	time_t last_msg_in;//用于记录上次收发消息的时间
	time_t next_msg_out;
	time_t ping_t;
	struct mosquitto__packet in_packet;//接收数据包用
	struct mosquitto__packet *current_out_packet;
	struct mosquitto__packet *out_packet;//接收数据包用
	struct mosquitto_message_all *will;
	struct mosquitto__alias *aliases;
	struct will_delay_list *will_delay_entry;
	uint32_t maximum_packet_size;
	int alias_count;
	uint32_t will_delay_interval;
	time_t will_delay_time;
#ifdef WITH_TLS
	SSL *ssl;
	SSL_CTX *ssl_ctx;
	char *tls_cafile;
	char *tls_capath;
	char *tls_certfile;
	char *tls_keyfile;
	int (*tls_pw_callback)(char *buf, int size, int rwflag, void *userdata);
	char *tls_version;
	char *tls_ciphers;
	char *tls_psk;
	char *tls_psk_identity;
	int tls_cert_reqs;
	bool tls_insecure;
	bool ssl_ctx_defaults;
	bool tls_ocsp_required;
	char *tls_engine;
	char *tls_engine_kpass_sha1;
	enum mosquitto__keyform tls_keyform;
	char *tls_alpn;
#endif
	bool want_write;
	bool want_connect;
#if defined(WITH_THREADING) && !defined(WITH_BROKER)
	pthread_mutex_t callback_mutex;
	pthread_mutex_t log_callback_mutex;
	pthread_mutex_t msgtime_mutex;
	pthread_mutex_t out_packet_mutex;
	pthread_mutex_t current_out_packet_mutex;
	pthread_mutex_t state_mutex;
	pthread_mutex_t mid_mutex;
	pthread_t thread_id;
#endif
	bool clean_start;
	uint32_t session_expiry_interval;
	time_t session_expiry_time;
#ifdef WITH_BROKER
	bool removed_from_by_id; /* True if removed from by_id hash */
	bool is_dropping;
	bool is_bridge;
	struct mosquitto__bridge *bridge;
	struct mosquitto_msg_data msgs_in;//接收消息队列,保存收到的消息;加入该队列主要是因为整个消息流程未完成,还有后续交互需要处理
	struct mosquitto_msg_data msgs_out;//发送消息队列,保存发送的消息或者收到的消息;加入该队列主要是因为整个消息流程未完成,还有后续交互需要处理;
	struct mosquitto__acl_user *acl_list;
	struct mosquitto__listener *listener;
	struct mosquitto__packet *out_packet_last;
	struct mosquitto__subhier **subs;
	struct mosquitto__subshared_ref **shared_subs;
	char *auth_method;
	int sub_count;
	int shared_sub_count;
	int pollfd_index;
#  ifdef WITH_WEBSOCKETS
#    if defined(LWS_LIBRARY_VERSION_NUMBER)
	struct lws *wsi;
#    else
	struct libwebsocket_context *ws_context;
	struct libwebsocket *wsi;
#    endif
#  endif
	bool ws_want_write;
	bool assigned_id;
#else
#  ifdef WITH_SOCKS
	char *socks5_host;
	int socks5_port;
	char *socks5_username;
	char *socks5_password;
#  endif
	void *userdata;
	bool in_callback;
	struct mosquitto_msg_data msgs_in;
	struct mosquitto_msg_data msgs_out;
	void (*on_connect)(struct mosquitto *, void *userdata, int rc);
	void (*on_connect_with_flags)(struct mosquitto *, void *userdata, int rc, int flags);
	void (*on_connect_v5)(struct mosquitto *, void *userdata, int rc, int flags, const mosquitto_property *props);
	void (*on_disconnect)(struct mosquitto *, void *userdata, int rc);
	void (*on_disconnect_v5)(struct mosquitto *, void *userdata, int rc, const mosquitto_property *props);
	void (*on_publish)(struct mosquitto *, void *userdata, int mid);
	void (*on_publish_v5)(struct mosquitto *, void *userdata, int mid, int reason_code, const mosquitto_property *props);
	void (*on_message)(struct mosquitto *, void *userdata, const struct mosquitto_message *message);
	void (*on_message_v5)(struct mosquitto *, void *userdata, const struct mosquitto_message *message, const mosquitto_property *props);
	void (*on_subscribe)(struct mosquitto *, void *userdata, int mid, int qos_count, const int *granted_qos);
	void (*on_subscribe_v5)(struct mosquitto *, void *userdata, int mid, int qos_count, const int *granted_qos, const mosquitto_property *props);
	void (*on_unsubscribe)(struct mosquitto *, void *userdata, int mid);
	void (*on_unsubscribe_v5)(struct mosquitto *, void *userdata, int mid, const mosquitto_property *props);
	void (*on_log)(struct mosquitto *, void *userdata, int level, const char *str);
	//void (*on_error)();
	char *host;
	int port;
	char *bind_address;
	unsigned int reconnects;
	unsigned int reconnect_delay;
	unsigned int reconnect_delay_max;
	bool reconnect_exponential_backoff;
	char threaded;
	struct mosquitto__packet *out_packet_last;
#  ifdef WITH_SRV
	ares_channel achan;
#  endif
#endif
	uint8_t maximum_qos;

#ifdef WITH_BROKER
	UT_hash_handle hh_id;
	UT_hash_handle hh_sock;
	struct mosquitto *for_free_next;
	struct session_expiry_list *expiry_list_item;
#endif
#ifdef WITH_EPOLL
	uint32_t events;
#endif
};

消息状态
消息发送与接收流程用,关注 mosq_ms_wait_for_xxxx 状态,客户端处理此类
消息

enum mosquitto_msg_state {
	mosq_ms_invalid = 0,
	mosq_ms_publish_qos0 = 1,
	mosq_ms_publish_qos1 = 2,
	mosq_ms_wait_for_puback = 3,//Oos==1时,发送PUBLISH后等待PUBACK返回
	mosq_ms_publish_qos2 = 4,
	mosq_ms_wait_for_pubrec = 5,//Oos==2时,发送PUBLISH后,等待PUBREC返回
	mosq_ms_resend_pubrel = 6,
	mosq_ms_wait_for_pubrel = 7,//Oos==2时,发送PUBREC后等待PUBREL返回
	mosq_ms_resend_pubcomp = 8,
	mosq_ms_wait_for_pubcomp = 9,//Oos==2时,发送PUBREL后等待PUBCOMP返回
	mosq_ms_send_pubrec = 10,
	mosq_ms_queued = 11
};

客户端状态:该状态为用户连接成功并通讯 CONNECT 之后结果;

enum mosquitto_client_state {
	mosq_cs_new = 0,
	mosq_cs_connected = 1,
	mosq_cs_disconnecting = 2,// mosquitto_disconnect时设置
	mosq_cs_active = 3,
	mosq_cs_connect_pending = 4,//没用到
	mosq_cs_connect_srv = 5,
	mosq_cs_disconnect_ws = 6,
	mosq_cs_disconnected = 7,
	mosq_cs_socks5_new = 8,
	mosq_cs_socks5_start = 9,
	mosq_cs_socks5_request = 10,
	mosq_cs_socks5_reply = 11,
	mosq_cs_socks5_auth_ok = 12,
	mosq_cs_socks5_userpass_reply = 13,
	mosq_cs_socks5_send_userpass = 14,
	mosq_cs_expiring = 15,
	mosq_cs_duplicate = 17, /* client that has been taken over by another with the same id */
	mosq_cs_disconnect_with_will = 18,
	mosq_cs_disused = 19, /* client that has been added to the disused list to be freed */
	mosq_cs_authenticating = 20, /* Client has sent CONNECT but is still undergoing extended authentication */
	mosq_cs_reauthenticating = 21, /* Client is undergoing reauthentication and shouldn't do anything else until complete */
};

数据包、数据包队列:发送数据(组包后)或者接受数据后(解包前)状态

struct mosquitto__packet{
	uint8_t *payload;
	struct mosquitto__packet *next;
	uint32_t remaining_mult;
	uint32_t remaining_length;
	uint32_t packet_length;
	uint32_t to_process;//发送进度,记录还未发送多少字节,缺省为packet_length
	uint32_t pos;//组包或者发送时用到,发送时记录发送到什么位置
	uint16_t mid;//消息id,当Qos==0 时回调on_publish时用
	uint8_t command;
	int8_t remaining_count;
};

消息队列 专指用户消息(包PUBLISH,PUBACK,PUBREC,PUBREL,PUBCOMP)

struct mosquitto_message_all{
	struct mosquitto_message_all *next;//下一个
	struct mosquitto_message_all *prev;//上一个
	mosquitto_property *properties;//属性
	time_t timestamp;//时间戳,记录本地软件tick时间
	//enum mosquitto_msg_direction direction;
	enum mosquitto_msg_state state;//状态,比如publish报文头的发送、待收到等
	bool dup;
	struct mosquitto_message msg;//消息的message主题部分结构体,mid,topic,payload,payloadlen,qos,retain,expiry_interval
};

主要处理收发消息时的缓存队列
注:
l 该队列与数据包队列没有直接关系;
l 数据包队列为网络层发送数据策略;
l 该队列为协议层处理逻辑;

struct mosquitto_msg_data{ 
#ifdef WITH_BROKER
	struct mosquitto_client_msg *inflight;
	struct mosquitto_client_msg *queued;
	unsigned long msg_bytes;
	unsigned long msg_bytes12;
	int msg_count;
	int msg_count12;
#else
	struct mosquitto_message_all *inflight; //对于Qos>0的消息,记录没有完成交互记录
	int queue_len;
#  ifdef WITH_THREADING
	pthread_mutex_t mutex;
#  endif
#endif
	int inflight_quota;//队列下标
	uint16_t inflight_maximum;//队列的最大值
};

部分常用函数内部含义

int mosquitto_lib_version(int *major, int *minor, int *revision) 查看mosquitto源码的系统版本号

int mosquitto_lib_init(void) 初始化需要的网络资源

int mosquitto_lib_cleanup(void) 将mosquitto_lib_init函数开启的各项服务关闭,释放一些使用到的内存空间

struct mosquitto *mosquitto_new(const char *id, bool clean_start, void *userdata) 给struct mosquitto *mosq指针分配资源。再mosquitto_reinitialise,也就是给结构体指针里面的变量重新赋初始默认值

void mosquitto_destroy(struct mosquitto *mosq) 释放线程资源,摧毁线程锁,释放上下文中的资源