01
前言
我可能是最懶的半吊子程序員博主了 ,從過年到現(xiàn)在,將近2個(gè)月了,沒發(fā)過一篇文章,但公眾號每天都有新增的兄弟姐妹關(guān)注,非常感謝大家的信任,我呢,就做好自己的事情,該干嘛干嘛,文章嘛,偶爾抽空寫一下發(fā)一發(fā),相對于寫文章,我更喜歡看書和寫代碼呢,我每天都有寫代碼的,這不,今天給大家?guī)硪环萆虡I(yè)級穩(wěn)定性的代碼——mqttclient。
02
關(guān)于mqttclient的誕生
談?wù)勥@份代碼的誕生緣由吧! 從上一年的5月份左右吧,我花了一個(gè)多星期去移植阿里的SDK,想要將它用起來,但是奈何一直沒能用成功,歸根結(jié)底我還是太菜了,10月份分析完了騰訊的SDK中的MQTT協(xié)議部分的代碼,我就想自己寫一個(gè)好用的代碼,要非常簡單API接口,還要有很好的穩(wěn)定性,然后嘛,也是為開源做貢獻(xiàn),希望有緣人能簡單快速用起來,都說程序員有些奇奇怪怪的理想,且不說改變世界吧,像我這種,我是很希望某些產(chǎn)品中能跑著我寫的代碼,即使我無緣知道甚至遇見它。。。
兜兜轉(zhuǎn)轉(zhuǎn),從11月份開始設(shè)計(jì)框架,LwIP的源碼與阿里的SDK框架給了我很大的影響,所以整體的設(shè)計(jì)框架都采用分層設(shè)計(jì),此外騰訊的SDK呢讓我決定了使用異步處理的思想來設(shè)計(jì)整個(gè)代碼,然后設(shè)計(jì)完整體框架之后就愉快地板磚了,沒錯(cuò),是愉快地!寫代碼其實(shí)是很好玩的事情,特別是當(dāng)年的想法可以實(shí)現(xiàn)的時(shí)候,遇到不懂的時(shí)候有資料,有人討論,在此特別感謝幾位網(wǎng)上的好基友,在大半夜的還與我討論,解答我的問題~非常nice。
03
介紹一下mqttclient
這一個(gè)基于socket API之上的跨平臺MQTT客戶端,擁有非常簡潔的API接口,以極少的資源實(shí)現(xiàn)QOS2的服務(wù)質(zhì)量,并且無縫銜接了mbedtls加密庫。
04
談?wù)剝?yōu)勢
- 基于標(biāo)準(zhǔn)BSD socket之上開發(fā) ,只要是兼容BSD socket的系統(tǒng)均可使用。
- 穩(wěn)定 :無論是
掉線重連
,丟包重發(fā)
,都是嚴(yán)格遵循MQTT協(xié)議標(biāo)準(zhǔn)
執(zhí)行,除此之外對大數(shù)據(jù)量的測試無論是收是發(fā),都是非常穩(wěn)定(一次發(fā)送135K
數(shù)據(jù),3秒一次),高頻測試也是非常穩(wěn)定(7個(gè)主題同時(shí)收發(fā),每秒一次,也就是1秒14個(gè)mqtt報(bào)文,服務(wù)質(zhì)量QoS0、QoS1、QoS2都有)。因?yàn)樽髡咭詷O少的資源設(shè)計(jì)了記錄機(jī)制
,對采用QoS1服務(wù)質(zhì)量的報(bào)文必須保證到達(dá)一次,當(dāng)發(fā)布的主題(qos1、qos2都適用)沒有被服務(wù)器收到時(shí)會自動重發(fā),而對QoS2服務(wù)質(zhì)量的報(bào)文保證有且只有處理一次(如果不相信它穩(wěn)定性的同學(xué)可以自己去修改源碼,專門為QoS2服務(wù)質(zhì)量去做測試,故意不回復(fù)PUBREC
包,讓服務(wù)器重發(fā)QoS2報(bào)文,且看看客戶端是否有且只有處理一次),而對于掉線重連的穩(wěn)定性,這種則是基本操作了,沒啥好說的,在自動重連后還會自動重新訂閱主題,保證主題不會丟失,因此在測試中穩(wěn)定性極好。 - 輕量級 :整個(gè)代碼工程極其簡單,不使用mbedtls情況下,占用資源極少,作者曾使用esp8266模組與云端通信,整個(gè)工程代碼消耗的RAM不足15k(包括系統(tǒng)占用的開銷,對數(shù)據(jù)的處理開銷,而此次還是未優(yōu)化的情況下,還依舊完美保留了掉線重連的穩(wěn)定性,但是對應(yīng)qos1、qos2服務(wù)質(zhì)量的報(bào)文則未做測試,因?yàn)?a href="http://www.ttokpm.com/v/tag/751/" target="_blank">STM32F103C8T6芯片資源實(shí)在是太少了,折騰不起)。
- 無縫銜接mbedtls加密傳輸 ,讓網(wǎng)絡(luò)傳輸更加安全,而且接口層完全不需要用戶理會,無論是否加密,mqttclient對用戶提供的API接口是沒有變化的,這就很好的兼容了一套代應(yīng)用層的碼可以加密傳輸也可以不加密傳輸。
- 擁有極簡的API接口 ,總的來說,mqttclient的配置都有默認(rèn)值,基本無需配置都能使用的,也可以隨意配置,對配置都有健壯性檢測,這樣子設(shè)計(jì)的API接口也是非常簡單。
- 有非常好的代碼風(fēng)格與思想 :整個(gè)代碼采用分層式設(shè)計(jì),代碼實(shí)現(xiàn)采用異步處理的思想,降低耦合,提高性能,具體體現(xiàn)在什么地方呢?很簡單,目前市面上很多MQTT客戶端發(fā)布主題都是要阻塞等待ack,這是非常暴力的行為,阻塞當(dāng)前線程等待服務(wù)器的應(yīng)答,那如果我想要發(fā)送數(shù)據(jù)怎么辦,或者我要重復(fù)檢測數(shù)據(jù)怎么辦,你可能會說,指定阻塞時(shí)間等待,那如果網(wǎng)絡(luò)延遲,ack遲遲不來,我就白等了嗎,對于qos1、qos2的服務(wù)質(zhì)量怎么辦,所以說這種還是要異步處理的思想,我發(fā)布主題,那我發(fā)布出去就好了,不需要等待,對于qos1、qos2服務(wù)質(zhì)量的MQTT報(bào)文,如果服務(wù)器沒收到,那我重發(fā)就可以,這種重發(fā)也是異步的處理,完全不會阻塞當(dāng)前線程。
- MQTT協(xié)議支持主題通配符
“#”、“+”
。 - 訂閱的主題與消息處理完全分離 ,讓編程邏輯更加簡單易用,用戶無需理會錯(cuò)綜復(fù)雜的邏輯關(guān)系。
- mqttclient內(nèi)部已實(shí)現(xiàn)?;钐幚頇C(jī)制 ,無需用戶過多關(guān)心理會,用戶只需專心處理應(yīng)用功能即可。
- 無縫銜接salof: 它是一個(gè)同步異步日志輸出框架,在空閑時(shí)候輸出對應(yīng)的日志信息,也可以將信息寫入flash中保存,方便調(diào)試。
- 不對外產(chǎn)生依賴。
05
mqttclient整體框架
擁有非常明確的分層框架。
06
mqttclient適配的平臺
目前已實(shí)現(xiàn)了Linux、TencentOS tiny、RT-Thread平臺(已做成軟件包,這個(gè)名字比較騷氣,叫kawaii-mqtt( 卡哇伊?( ′???` )****)),除此之外TencentOS tiny的AT框架亦可以使用(RAM消耗不足15K),并且穩(wěn)定性極好!歡迎下載并且測試。
平臺 | 代碼位置 |
---|---|
Linux | https://github.com/jiejieTop/mqttclient |
TencentOS tiny | https://github.com/Tencent/TencentOS-tiny/tree/master/board/Fire_STM32F429 |
TencentOS tiny AT 框架 | https://github.com/jiejieTop/gokit3-board-mqttclient |
RT-Thread | https://github.com/jiejieTop/kawaii-mqtt |
07
mqttclient測試(Linux平臺)
1. 安裝cmake
sudo apt-get install cmake
2. 配置連接參數(shù)
在mqttclient/test/test.c
文件中修改以下內(nèi)容:
init_params.connect_params.network_params.network_ssl_params.ca_crt = test_ca_get(); /* CA證書 */
init_params.connect_params.network_params.addr = "xxxxxxx"; /* 服務(wù)器域名 */
init_params.connect_params.network_params.port = "8883"; /* 服務(wù)器端口號 */
init_params.connect_params.user_name = "xxxxxxx"; /* 用戶名 */
init_params.connect_params.password = "xxxxxxx"; /* 密碼 */
init_params.connect_params.client_id = "xxxxxxx"; /* 客戶端id */
3. 編譯運(yùn)行
./build.sh
運(yùn)行build.sh
腳本后會在 ./build/bin/
目錄下生成可執(zhí)行文件mqtt-client
,直接運(yùn)行即可。
08
mqttclient的配置
mbedtls
默認(rèn)不打開mbedtls,當(dāng)然只需要配置一個(gè)宏定義即可打開mbedtls加密。
salof
salof 全稱是:Synchronous Asynchronous Log Output Framework
(同步異步日志輸出框架),它是一個(gè)同步異步日志輸出框架,在空閑時(shí)候輸出對應(yīng)的日志信息,并且該庫與mqttclient無縫銜接。
配置對應(yīng)的日志輸出級別:
#define BASE_LEVEL (0)
#define ASSERT_LEVEL (BASE_LEVEL + 1) /* 日志輸出級別:斷言級別(非常高優(yōu)先級) */
#define ERR_LEVEL (ASSERT_LEVEL + 1) /* 日志輸出級別:錯(cuò)誤級別(高優(yōu)先級) */
#define WARN_LEVEL (ERR_LEVEL + 1) /* 日志輸出級別:警告級別(中優(yōu)先級) */
#define INFO_LEVEL (WARN_LEVEL + 1) /* 日志輸出級別:信息級別(低優(yōu)先級) */
#define DEBUG_LEVEL (INFO_LEVEL + 1) /* 日志輸出級別:調(diào)試級別(更低優(yōu)先級) */
#define LOG_LEVEL WARN_LEVEL /* 日志輸出級別 */
日志其他選項(xiàng):
- 終端帶顏色
- 時(shí)間戳
- 標(biāo)簽
mqttclient的基本配置
配置mqtt等待應(yīng)答列表的最大值,對于qos1 qos2服務(wù)質(zhì)量有要求的可以將其設(shè)置大一點(diǎn),當(dāng)然也必須資源跟得上,它主要是保證qos1 qos2的mqtt報(bào)文能準(zhǔn)確到達(dá)服務(wù)器。
#define MQTT_ACK_HANDLER_NUM_MAX 64
選擇MQTT協(xié)議的版本,默認(rèn)為4,表示使用MQTT 3.1.1版本,而3則表示為MQTT 3.1版本。
#define MQTT_VERSION 4 // 4 is mqtt 3.1.1
設(shè)置默認(rèn)的?;顣r(shí)間,它主要是保證MQTT客戶端與服務(wù)器的保持活性連接,單位為 秒 ,比如MQTT客戶端與服務(wù)器100S沒有發(fā)送數(shù)據(jù)了,有沒有接收到數(shù)據(jù),此時(shí)MQTT客戶端會發(fā)送一個(gè)ping包,確認(rèn)一下這個(gè)會話是否存在,如果收到服務(wù)器的應(yīng)答,那么說明這個(gè)會話還是存在的,可以隨時(shí)收發(fā)數(shù)據(jù),而如果不存在了,就清除會話。
#define MQTT_KEEP_ALIVE_INTERVAL 100 // unit: second
默認(rèn)的命令超時(shí),它主要是用于socket讀寫超時(shí),在MQTT初始化時(shí)可以指定:
#define MQTT_DEFAULT_CMD_TIMEOUT 4000
默認(rèn)主題的長度,主題是支持通配符的,如果主題太長則會被截?cái)啵?/p>
#define MQTT_TOPIC_LEN_MAX 64
默認(rèn)的算法數(shù)據(jù)緩沖區(qū)的大小,如果要發(fā)送大量數(shù)據(jù)則修改大一些,在MQTT初始化時(shí)可以指定:
#define MQTT_DEFAULT_BUF_SIZE 1024
線程相關(guān)的配置,如線程棧,線程優(yōu)先級,線程時(shí)間片等:
在linux環(huán)境下可以是不需要理會這些參數(shù)的,而在RTOS平臺則需要配置,如果不使用mbedtls,線程棧2048字節(jié)已足夠,而使用mbedtls加密后,需要配置4096字節(jié)以上。
#define MQTT_THREAD_STACK_SIZE 2048 // 線程棧
#define MQTT_THREAD_PRIO 5 // 線程優(yōu)先級
#define MQTT_THREAD_TICK 50 // 線程時(shí)間片
默認(rèn)的重連時(shí)間間隔,當(dāng)發(fā)生掉線時(shí),會以這個(gè)時(shí)間間隔嘗試重連:
#define MQTT_RECONNECT_DEFAULT_DURATION 1000
其他不需要怎么配置的東西:
#define MQTT_MAX_PACKET_ID (0xFFFF - 1) // mqtt報(bào)文id
#define MQTT_MAX_CMD_TIMEOUT 20000 //最大的命令超時(shí)參數(shù)
#define MQTT_MIN_CMD_TIMEOUT 1000 //最小的命令超時(shí)參數(shù)
ps:以上參數(shù)基本不需要怎么配置的,直接用即可~
09
mqttclient設(shè)計(jì)思想
設(shè)計(jì)思想
- 整體采用分層式設(shè)計(jì),代碼實(shí)現(xiàn)采用異步設(shè)計(jì)方式,降低耦合。
- 消息的處理使用回調(diào)的方式處理:用戶指定
[訂閱的主題]
與指定[消息的處理函數(shù)]
- 不對外產(chǎn)生依賴
方便大家更容易理解mqttclient的代碼與設(shè)計(jì)思想,讓大家能夠修改源碼與使用,還可以提交pr或者issues,開源的世界期待各位大神的參與,感謝!
除此之外以下代碼的記錄機(jī)制與其超時(shí)處理機(jī)制是非常好的編程思想,大家有興趣一定要看源代碼!
不是所有人都喜歡看代碼的,整個(gè)mqttclient的實(shí)現(xiàn)在今天的第二篇推文中講解,此處就不再多說了,源碼地址是:https://github.com/jiejieTop/mqttclient。
-
框架
+關(guān)注
關(guān)注
0文章
396瀏覽量
17270 -
LwIP
+關(guān)注
關(guān)注
2文章
84瀏覽量
26935 -
SDK
+關(guān)注
關(guān)注
3文章
1006瀏覽量
45422
發(fā)布評論請先 登錄
相關(guān)推薦
評論