一、Eclipse Mosquitto :http://mosquitto.org/
源码地址
https://github.com/eclipse/mosquitto
本文章目前只是对于客户端publish代码的运行逻辑分析,后面会根据代码的阅读慢慢补充上来,其中对结构体的描述篇幅比较大,如果不想看,可直接跳到末尾观看运行流程图,如果文中有错误的地方请指出,我会马上修改
二、在阅读此文章的代码解析时,需要先阅读MQTT协议的文档,了解基本的MQTT协议通信框架。
三、源码解析
3.1主要的源码目录
源码目录层级比较多,主要关注的目录有/mosquitto/src、/mosquitto/lib、/mosquitto/client三个目录,
其中/src和/lib目录下主要放置服务端(Broker)的实现代码以及部分底层与网络相关的操作,
client目录主要是订阅客户端和发布客户端的实现源码。
3.2了解基本的结构体定义
在/lib/property_mosq.h文件:
定义struct mqtt5__property
在/lib/connect.c文件中定义struct mqtt5__property;
typedef struct mqtt5__property mosquitto_property;
struct mqtt5__property {
struct mqtt5__property *next;
union {
uint8_t i8;
uint16_t i16;
uint32_t i32;
uint32_t varint;
struct mqtt__string bin;
struct mqtt__string s;
} value;
struct mqtt__string name;
int32_t identifier;
bool client_generated;
};
在/include/mosquitto.h文件:
定义 struct mosquitto_message
struct mosquitto_message{
int mid;
char *topic;
void *payload;
int payloadlen;
int qos;
bool retain;
};
在/lib/mosquitto_internal.h文件:
定义enum mosquitto_msg_state
enum mosquitto_msg_state {
mosq_ms_invalid = 0,
mosq_ms_publish_qos0 = 1,
mosq_ms_publish_qos1 = 2,
mosq_ms_wait_for_puback = 3,
mosq_ms_publish_qos2 = 4,
mosq_ms_wait_for_pubrec = 5,
mosq_ms_resend_pubrel = 6,
mosq_ms_wait_for_pubrel = 7,
mosq_ms_resend_pubcomp = 8,
mosq_ms_wait_for_pubcomp = 9,
mosq_ms_send_pubrec = 10,
mosq_ms_queued = 11
};
定义enum mosquitto_client_stat
enum mosquitto_client_state {
mosq_cs_new = 0,
mosq_cs_connected = 1,
mosq_cs_disconnecting = 2,
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 */
};
定义enum mosquitto__protocol
enum mosquitto__protocol {
mosq_p_invalid = 0,
mosq_p_mqtt31 = 1,
mosq_p_mqtt311 = 2,
mosq_p_mqtts = 3,
mosq_p_mqtt5 = 5,
};
定义enum mosquitto__threaded_state
enum mosquitto__threaded_state {
mosq_ts_none, /* No threads in use */
mosq_ts_self, /* Threads started by libmosquitto */
mosq_ts_external /* Threads started by external code */
};
定义enum mosquitto__transport
enum mosquitto__transport {
mosq_t_invalid = 0,
mosq_t_tcp = 1,
mosq_t_ws = 2,
mosq_t_sctp = 3
};
定义struct mosquitto__alias
struct mosquitto__alias{
char *topic;
uint16_t alias;
};
定义struct session_expiry_list
struct session_expiry_list {
struct mosquitto *context;
struct session_expiry_list *prev;
struct session_expiry_list *next;
};
定义struct mosquitto__packet
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;
uint32_t pos;
uint16_t mid;
uint8_t command;
int8_t remaining_count;
};
定义struct mosquitto_message_all
struct mosquitto_message_all{
struct mosquitto_message_all *next;
struct mosquitto_message_all *prev;
mosquitto_property *properties;
time_t timestamp;
enum mosquitto_msg_state state;
bool dup;
struct mosquitto_message msg;
uint32_t expiry_interval;
};
定义struct mosquitto_msg_data
struct mosquitto_msg_data{
#ifdef WITH_BROKER
struct mosquitto_client_msg *inflight;
struct mosquitto_client_msg *queued;
long inflight_bytes;
long inflight_bytes12;
int inflight_count;
int inflight_count12;
long queued_bytes;
long queued_bytes12;
int queued_count;
int queued_count12;
#else
struct mosquitto_message_all *inflight;
int queue_len;
# ifdef WITH_THREADING
pthread_mutex_t mutex;
# endif
#endif
int inflight_quota;
uint16_t inflight_maximum;
};
定义struct mosquitto
结构体struct mosquitto 主要用于保存客户端连接的所有信息,例如用户id,用户名,客户端socket,ip地址,密码,保持连接的时间值等
struct mosquitto {
#if defined(WITH_BROKER) && defined(WITH_EPOLL)
/* This *must* be the first element in the struct. */
int ident;
#endif
mosq_sock_t sock;
#ifndef WITH_BROKER
mosq_sock_t sockpairR, sockpairW;
#endif
uint32_t maximum_packet_size;
#if defined(__GLIBC__) && defined(WITH_ADNS)
struct gaicb *adns; /* For getaddrinfo_a */
#endif
enum mosquitto__protocol protocol;
char *address;
char *id;
char *username;
char *password;
uint16_t keepalive;
uint16_t last_mid;
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;
int alias_count;
int out_packet_count;
uint32_t will_delay_interval;
time_t will_delay_time;
#ifdef WITH_TLS
SSL *ssl;
SSL_CTX *ssl_ctx;
#ifndef WITH_BROKER
SSL_CTX *user_ssl_ctx;
#endif
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;
char *tls_engine;
char *tls_engine_kpass_sha1;
char *tls_alpn;
int tls_cert_reqs;
bool tls_insecure;
bool ssl_ctx_defaults;
bool tls_ocsp_required;
bool tls_use_os_certs;
enum mosquitto__keyform tls_keyform;
#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;
time_t session_expiry_time;
uint32_t session_expiry_interval;
#ifdef WITH_BROKER
bool in_by_id;
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__client_sub **subs;
char *auth_method;
int sub_count;
# ifndef WITH_EPOLL
int pollfd_index;
# endif
# ifdef WITH_WEBSOCKETS
struct lws *wsi;
# endif
bool ws_want_write;
bool assigned_id;
#else
# ifdef WITH_SOCKS
char *socks5_host;
uint16_t 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;
uint16_t 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;
mosquitto_property *connect_properties;
# ifdef WITH_SRV
ares_channel achan;
# endif
#endif
uint8_t max_qos;
uint8_t retain_available;
bool tcp_nodelay;
#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;
uint16_t remote_port;
#endif
uint32_t events;
};
其中sock表示mosquitto服务器程序与该客户端连接通信所用的socket描述符;address表示该客户端的IP地址;
id是该客户端登陆mosquitto程序时所提供的ID值,该值与其他的客户端不能重复;成员username和password用于
记录客户端登陆时所提供的用户名和密码;keepalive是该客户端需在此时间内向mosquitto服务器程序发送一条ping/pong消息。
参数last_msg_in和last_msg_out用于记录上次收发消息的时间;参数struct mosquitto_client_msg*msgs用于
暂时存储发往该context的消息。
在/src/mosquitto_broker_internal.h文件:
定义struct mosquitto__callback
struct mosquitto__callback{
UT_hash_handle hh; /* For callbacks that register for e.g. a specific topic */
struct mosquitto__callback *next, *prev; /* For typical callbacks */
MOSQ_FUNC_generic_callback cb;
void *userdata;
char *data; /* e.g. topic for control event */
};
定义struct plugin__callbacks
struct plugin__callbacks{
struct mosquitto__callback *tick;
struct mosquitto__callback *acl_check;
struct mosquitto__callback *basic_auth;
struct mosquitto__callback *control;
struct mosquitto__callback *disconnect;
struct mosquitto__callback *ext_auth_continue;
struct mosquitto__callback *ext_auth_start;
struct mosquitto__callback *message;
struct mosquitto__callback *psk_key;
struct mosquitto__callback *reload;
};
定义struct mosquitto__security_options
struct mosquitto__security_options {
/* Any options that get added here also need considering
* in config__read() with regards whether allow_anonymous
* should be disabled when these options are set.
*/
struct mosquitto__unpwd *unpwd;
struct mosquitto__unpwd *psk_id;
struct mosquitto__acl_user *acl_list;
struct mosquitto__acl *acl_patterns;
char *password_file;
char *psk_file;
char *acl_file;
struct mosquitto__auth_plugin_config *auth_plugin_configs;
int auth_plugin_config_count;
int8_t allow_anonymous;
bool allow_zero_length_clientid;
char *auto_id_prefix;
uint16_t auto_id_prefix_len;
struct plugin__callbacks plugin_callbacks;
mosquitto_plugin_id_t *pid; /* For registering as a "plugin" */
};
定义struct mosquitto__listener
struct mosquitto__listener {
uint16_t port;
char *host;
char *bind_interface;
int max_connections;
char *mount_point;
mosq_sock_t *socks;
int sock_count;
int client_count;
enum mosquitto_protocol protocol;
int socket_domain;
bool use_username_as_clientid;
uint8_t max_qos;
uint16_t max_topic_alias;
#ifdef WITH_TLS
char *cafile;
char *capath;
char *certfile;
char *keyfile;
char *tls_engine;
char *tls_engine_kpass_sha1;
char *ciphers;
char *ciphers_tls13;
char *psk_hint;
SSL_CTX *ssl_ctx;
char *crlfile;
char *tls_version;
char *dhparamfile;
bool use_identity_as_username;
bool use_subject_as_username;
bool require_certificate;
enum mosquitto__keyform tls_keyform;
#endif
#ifdef WITH_WEBSOCKETS
struct lws_context *ws_context;
bool ws_in_init;
char *http_dir;
struct lws_protocols *ws_protocol;
#endif
struct mosquitto__security_options security_options;
#ifdef WITH_UNIX_SOCKETS
char *unix_socket_path;
#endif
};
```c
定义 struct mosquitto__listener_sock
struct mosquitto__listener_sock{
#ifdef WITH_EPOLL
/* This *must* be the first element in the struct. */
int ident;
#endif
mosq_sock_t sock;
struct mosquitto__listener *listener;
};
定义mosquitto_plugin_id_t
typedef struct mosquitto_plugin_id_t{
struct mosquitto__listener *listener;
} mosquitto_plugin_id_t;
定义struct mosquitto__config
结构体struct mosquitto_config用于保存mosquitto的所有配置信息,mosquitto程序在启动时将初始化该结构体并从配置文件中读取配置信息保存于该结构体变量内。
struct mosquitto__config {
bool allow_duplicate_messages;
int autosave_interval;
bool autosave_on_changes;
bool check_retain_source;
char *clientid_prefixes;
bool connection_messages;
uint16_t cmd_port[CMD_PORT_LIMIT];
int cmd_port_count;
bool daemon;
struct mosquitto__listener default_listener;
struct mosquitto__listener *listeners;
int listener_count;
bool local_only;
unsigned int log_dest;
int log_facility;
unsigned int log_type;
bool log_timestamp;
char *log_timestamp_format;
char *log_file;
FILE *log_fptr;
size_t max_inflight_bytes;
size_t max_queued_bytes;
int max_queued_messages;
uint32_t max_packet_size;
uint32_t message_size_limit;
uint16_t max_inflight_messages;
uint16_t max_keepalive;
uint8_t max_qos;
bool persistence;
char *persistence_location;
char *persistence_file;
char *persistence_filepath;
time_t persistent_client_expiration;
char *pid_file;
bool queue_qos0_messages;
bool per_listener_settings;
bool retain_available;
bool set_tcp_nodelay;
int sys_interval;
bool upgrade_outgoing_qos;
char *user;
#ifdef WITH_WEBSOCKETS
int websockets_log_level;
uint16_t websockets_headers_size;
#endif
#ifdef WITH_BRIDGE
struct mosquitto__bridge *bridges;
int bridge_count;
#endif
struct mosquitto__security_options security_options;
};
定义struct mosquitto__subleaf
在mosquitto程序中,对某一topic的所有订阅者被组织成一个订阅列表,该订阅列表是一个双向链表,链表的每个节点都保存有一个订阅者,
该链表的节点即是:struct mosquitto__subleaf,定义形式为:
struct mosquitto__subleaf {
struct mosquitto__subleaf *prev;
struct mosquitto__subleaf *next;
struct mosquitto *context;
uint32_t identifier;
uint8_t qos;
bool no_local;
bool retain_as_published;
};
其中成员struct mosquitto *context表示一个订阅客户端,prev和next表示其前一个节点和后一个节点,identifier 表示标识符。
定义struct mosquitto__subshared
struct mosquitto__subshared {
UT_hash_handle hh;
char *name;
struct mosquitto__subleaf *subs;
};
定义struct mosquitto__subhier
数据结构struct mosquitto__subhier是用于保存订阅树的节点(包括叶子节点和中间节点),
mosquitto中对订阅树采用父亲–孩子链表法的方式进行存储,该存储方式主要借助与数据结构struct mosquitto__subhier来完成。
struct mosquitto__subhier {
UT_hash_handle hh;
struct mosquitto__subhier *parent;
struct mosquitto__subhier *children;
struct mosquitto__subleaf *subs;
struct mosquitto__subshared *shared;
char *topic;
uint16_t topic_len;
};
成员说明:
parent:该成员指针指向同结构体的父亲节点
children:该成员指针指向同结构的孩子节点;
subs:该成员指向的订阅列表;
shared:该成员指向的
topic:该节点对应的topic片段;
topic_len:该节点对应的topic片段长度
定义struct mosquitto__client_sub
struct mosquitto__client_sub {
struct mosquitto__subhier *hier;
struct mosquitto__subshared *shared;
char topic_filter[];
};
定义struct sub__token
struct sub__token {
struct sub__token *next;
char *topic;
uint16_t topic_len;
};
定义struct mosquitto__retainhier
struct mosquitto__retainhier {
UT_hash_handle hh;
struct mosquitto__retainhier *parent;
struct mosquitto__retainhier *children;
struct mosquitto_msg_store *retained;
char *topic;
uint16_t topic_len;
};
定义struct mosquitto_msg_store_load
struct mosquitto_msg_store_load{
UT_hash_handle hh;
dbid_t db_id;
struct mosquitto_msg_store *store;
};
定义struct mosquitto_msg_store
struct mosquitto_msg_store{
struct mosquitto_msg_store *next;
struct mosquitto_msg_store *prev;
dbid_t db_id;
char *source_id;
char *source_username;
struct mosquitto__listener *source_listener;
char **dest_ids;
int dest_id_count;
int ref_count;
char* topic;
mosquitto_property *properties;
void *payload;
time_t message_expiry_time;
uint32_t payloadlen;
enum mosquitto_msg_origin origin;
uint16_t source_mid;
uint16_t mid;
uint8_t qos;
bool retain;
};
定义struct mosquitto_client_msg
struct mosquitto_client_msg{
struct mosquitto_client_msg *prev;
struct mosquitto_client_msg *next;
struct mosquitto_msg_store *store;
mosquitto_property *properties;
time_t timestamp;
uint16_t mid;
uint8_t qos;
bool retain;
enum mosquitto_msg_direction direction;
enum mosquitto_msg_state state;
bool dup;
};
定义struct mosquitto__unpwd
struct mosquitto__unpwd{
UT_hash_handle hh;
char *username;
char *password;
char *clientid;
#ifdef WITH_TLS
unsigned char *salt;
unsigned int password_len;
unsigned int salt_len;
int iterations;
#endif
enum mosquitto_pwhash_type hashtype;
};
定义struct mosquitto__acl
struct mosquitto__acl{
struct mosquitto__acl *next;
char *topic;
int access;
int ucount;
int ccount;
};
定义struct mosquitto__acl_user
struct mosquitto__acl_user{
struct mosquitto__acl_user *next;
char *username;
struct mosquitto__acl *acl;
};
定义struct mosquitto_message_v5
struct mosquitto_message_v5{
struct mosquitto_message_v5 *next, *prev;
char *topic;
void *payload;
mosquitto_property *properties;
char *clientid; /* Used only by mosquitto_broker_publish*() to indicate
this message is for a specific client. */
int payloadlen;
int qos;
bool retain;
};
定义struct mosquitto_db
结构体struct mosquitto_db是mosquitto对所有内部数据的统一管理结构,
可以认为是其内部的一个内存数据库。它保存了所有的客户端,所有客户端的订阅关系等等。
struct mosquitto_db{
dbid_t last_db_id;
struct mosquitto__subhier *subs;
struct mosquitto__retainhier *retains;
struct mosquitto *contexts_by_id;
struct mosquitto *contexts_by_sock;
struct mosquitto *contexts_for_free;
#ifdef WITH_BRIDGE
struct mosquitto **bridges;
#endif
struct clientid__index_hash *clientid_index_hash;
struct mosquitto_msg_store *msg_store;
struct mosquitto_msg_store_load *msg_store_load;
time_t now_s; /* Monotonic clock, where possible */
time_t now_real_s; /* Read clock, for measuring session/message expiry */
#ifdef WITH_BRIDGE
int bridge_count;
#endif
int msg_store_count;
unsigned long msg_store_bytes;
char *config_file;
struct mosquitto__config *config;
int auth_plugin_count;
bool verbose;
#ifdef WITH_SYS_TREE
int subscription_count;
int shared_subscription_count;
int retained_count;
#endif
int persistence_changes;
struct mosquitto *ll_for_free;
#ifdef WITH_EPOLL
int epollfd;
#endif
struct mosquitto_message_v5 *plugin_msgs;
};
结构体成员struct mosquitto__subhier *subs保存了订阅树的总树根,mosquitto中对所有的topic都在该订阅树中维护,客户端的订阅关系
也在该订阅树中维护;成员结构体struct clientid__index_hash*clientid_index_hash用于保存一个hash表,该hash表可通过客户端的ID
快速找到该客户端在数组contexts中的索引;结构体成员struct mosquitto__config *config用于保存mosquitto的所有配置信息。
enum mosquitto__bridge_direction{
bd_out = 0,
bd_in = 1,
bd_both = 2
};
enum mosquitto_bridge_start_type{
bst_automatic = 0,
bst_lazy = 1,
bst_manual = 2,
bst_once = 3
};
定义struct mosquitto__bridge_topic
struct mosquitto__bridge_topic{
char *topic;
char *local_prefix;
char *remote_prefix;
char *local_topic; /* topic prefixed with local_prefix */
char *remote_topic; /* topic prefixed with remote_prefix */
enum mosquitto__bridge_direction direction;
uint8_t qos;
};
定义struct bridge_address
struct bridge_address{
char *address;
uint16_t port;
};
定义struct mosquitto__bridge
struct mosquitto__bridge{
char *name;
struct bridge_address *addresses;
int cur_address;
int address_count;
time_t primary_retry;
mosq_sock_t primary_retry_sock;
bool round_robin;
bool try_private;
bool try_private_accepted;
bool clean_start;
int8_t clean_start_local;
uint16_t keepalive;
struct mosquitto__bridge_topic *topics;
int topic_count;
bool topic_remapping;
enum mosquitto__protocol protocol_version;
time_t restart_t;
char *remote_clientid;
char *remote_username;
char *remote_password;
char *local_clientid;
char *local_username;
char *local_password;
char *notification_topic;
char *bind_address;
bool notifications;
bool notifications_local_only;
enum mosquitto_bridge_start_type start_type;
int idle_timeout;
int restart_timeout;
int backoff_base;
int backoff_cap;
int threshold;
uint32_t maximum_packet_size;
bool lazy_reconnect;
bool attempt_unsubscribe;
bool initial_notification_done;
bool outgoing_retain;
#ifdef WITH_TLS
bool tls_insecure;
bool tls_ocsp_required;
char *tls_cafile;
char *tls_capath;
char *tls_certfile;
char *tls_keyfile;
char *tls_version;
char *tls_alpn;
# ifdef FINAL_WITH_TLS_PSK
char *tls_psk_identity;
char *tls_psk;
# endif
#endif
};
#ifdef WITH_WEBSOCKETS
struct libws_mqtt_hack {
char *http_dir;
struct mosquitto__listener *listener;
};
struct libws_mqtt_data {
struct mosquitto *mosq;
};
四、客户端
4.1 publish客户端
在/examples/publish/basic-1.c文件:
main函数:
int main(int argc, char *argv[])
{
struct mosquitto *mosq;
int rc;
/* Required before calling other mosquitto functions */
mosquitto_lib_init();
/* Create a new client instance.
* id = NULL -> ask the broker to generate a client id for us
* clean session = true -> the broker should remove old sessions when we connect
* obj = NULL -> we aren't passing any of our private data for callbacks
*/
mosq = mosquitto_new(NULL, true, NULL);
if(mosq == NULL){
fprintf(stderr, "Error: Out of memory.\n");
return 1;
}
/* Configure callbacks. This should be done before connecting ideally. */
mosquitto_connect_callback_set(mosq, on_connect);
mosquitto_publish_callback_set(mosq, on_publish);
/* Connect to test.mosquitto.org on port 1883, with a keepalive of 60 seconds.
* This call makes the socket connection only, it does not complete the MQTT
* CONNECT/CONNACK flow, you should use mosquitto_loop_start() or
* mosquitto_loop_forever() for processing net traffic. */
rc = mosquitto_connect(mosq, "test.mosquitto.org", 1883, 60);
if(rc != MOSQ_ERR_SUCCESS){
mosquitto_destroy(mosq);
fprintf(stderr, "Error: %s\n", mosquitto_strerror(rc));
return 1;
}
/* Run the network loop in a background thread, this call returns quickly. */
rc = mosquitto_loop_start(mosq);
if(rc != MOSQ_ERR_SUCCESS){
mosquitto_destroy(mosq);
fprintf(stderr, "Error: %s\n", mosquitto_strerror(rc));
return 1;
}
/* At this point the client is connected to the network socket, but may not
* have completed CONNECT/CONNACK.
* It is fairly safe to start queuing messages at this point, but if you
* want to be really sure you should wait until after a successful call to
* the connect callback.
* In this case we know it is 1 second before we start publishing.
*/
while(1){
publish_sensor_data(mosq);
}
mosquitto_lib_cleanup();
return 0;
}
运行流程图,公司所用的是mqtt3.1.1,所以有些mqtt5.0的相关代码没有写入流程图中
4.2subscribe客户端
在/examples/subscribe/basic-1.c文件:
subscribe的流程和publish很多相似的地方,区别点:
在connect时,rc = mosquitto_connect(mosq, “test.mosquitto.org”, 1883, 60);
在此函数运行完成后,在正常情况下客户端会接收到服务端的connack包,再处理connack包的最后会去执行connack_callback这个回调函数,而这个回调函数内部会调用:
rc = mosquitto_subscribe(mosq, NULL, “example/temperature”, 1);
这也就是去发送了订阅。
在接收到服务端发送的publish消息或者pubrel消息时,会调用回调函数mosq->on_message。
4.3细节理解点
mosquitto_loop函数内部调用: mosquitto_loop_read和mosquitto_loop_write函数。
mosquitto_loop_read循环调用packet__read函数,从socket读取数据出来,最后调用handle__packet
函数,根据mosq->in_packet.command的值调用对应的解析函数解析packet。
mosquitto_loop_write循环调用packrt__write函数,只管往socket写入数据,也就是发送数据出去,不参与任何的组包。
所有的组包动作都是由send_xx函数来实现,有send__connect,send__publish,send__subscribe…等,并且这些函数内部都有在组包完成后都有调用packet_queue函数来发送组好的包
4.3.1publish的运行流程:
五、服务器
5.1 main代码:
int main(int argc, char *argv[])
{
struct mosquitto__config config;
...
memset(&db, 0, sizeof(struct mosquitto_db));
db.now_s = mosquitto_time();
db.now_real_s = time(NULL);
net__broker_init();
config__init(&config);
rc = config__parse_args(&config, argc, argv);
if(rc != MOSQ_ERR_SUCCESS) return rc;
db.config = &config;
/* Drop privileges permanently immediately after the config is loaded.
* This requires the user to ensure that all certificates, log locations,
* etc. are accessible my the `mosquitto` or other unprivileged user.
*/
rc = drop_privileges(&config);
if(rc != MOSQ_ERR_SUCCESS) return rc;
if(config.daemon){
mosquitto__daemonise();
}
if(pid__write()) return 1;
rc = db__open(&config);
if(rc != MOSQ_ERR_SUCCESS){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Couldn't open database.");
return rc;
}
/* Initialise logging only after initialising the database in case we're
* logging to topics */
if(log__init(&config)){
rc = 1;
return rc;
}
log__printf(NULL, MOSQ_LOG_INFO, "mosquitto version %s starting", VERSION);
if(db.config_file){
log__printf(NULL, MOSQ_LOG_INFO, "Config loaded from %s.", db.config_file);
}else{
log__printf(NULL, MOSQ_LOG_INFO, "Using default config.");
}
rc = mosquitto_security_module_init();
if(rc) return rc;
rc = mosquitto_security_init(false);
if(rc) return rc;
/* After loading persisted clients and ACLs, try to associate them,
* so persisted subscriptions can start storing messages */
HASH_ITER(hh_id, db.contexts_by_id, ctxt, ctxt_tmp){
if(ctxt && !ctxt->clean_start && ctxt->username){
rc = acl__find_acls(ctxt);
if(rc){
log__printf(NULL, MOSQ_LOG_WARNING, "Failed to associate persisted user %s with ACLs, "
"likely due to changed ports while using a per_listener_settings configuration.", ctxt->username);
}
}
}
#ifdef WITH_SYS_TREE
sys_tree__init();
#endif
if(listeners__start()) return 1;/*如果按默认配置,db.config->local_only为true,则只会创建一个本地的listeren,如果不为true,则根据db.config->listener_count的数目创建listenrer。
一个listeren可以监听多个socket*/
rc = mux__init(listensock, listensock_count);//将所有的listener的socket放入pollfds数组中
if(rc) return rc;
signal__setup();
#ifdef WITH_BRIDGE
bridge__start_all();
#endif
log__printf(NULL, MOSQ_LOG_INFO, "mosquitto version %s running", VERSION);
#ifdef WITH_SYSTEMD
sd_notify(0, "READY=1");
#endif
run = 1;
rc = mosquitto_main_loop(listensock, listensock_count);//重点函数
log__printf(NULL, MOSQ_LOG_INFO, "mosquitto version %s terminating", VERSION);
/* FIXME - this isn't quite right, all wills with will delay zero should be
* sent now, but those with positive will delay should be persisted and
* restored, pending the client reconnecting in time. */
HASH_ITER(hh_id, db.contexts_by_id, ctxt, ctxt_tmp){
context__send_will(ctxt);
}
will_delay__send_all();
#ifdef WITH_PERSISTENCE
persist__backup(true);
#endif
session_expiry__remove_all();
listeners__stop();
运行流程图:
重点注意3个函数:listeners__start、mux__init、mosquitto_main_loop,下面是对这3个函数的分析
5.2 函数分析
5.2.1 listeners__start函数
此函数作用:给所有的listener创建多个socket,这些socket全部放入
listener->socks[listener->sock_count]中。然后根据listener的数量
db.config->listener_count,通过2次for循环全部存入 listensock[listensock_index].sock中。
for(i=0; i<db.config->listener_count; i++);
{
...省略
for(i=0; i<listener->sock_count; i++);
{
...省略
listensock[listensock_index].sock = listener->socks[i];
...省略
}
...省略
}
注意,这些socket都是监听socket,并不是业务socket,都是用来监听外部的客户端,
真正数据交互socket在mosquitto_main_loop中创建。
5.2.2 mux__init 函数
5.2.3 mosquitto_main_loop函数(重中之重)
六、命令测试
6.1 订阅端
mosquitto_sub -h localhost -t test -v
用 -h 参数指定服务器 IP ,用 -t 参数指定订阅的话题。( localhost 和 127.0.0.1 代表本地IP )
6.1发布端
mosquitto_pub -h localhost -t test -m "Hello world"
用 -m 参数指定要发布的信息内容,然后在订阅者的终端就可以看到由 发布者 推送的信息