二、基于 Socket 通信的優(yōu)點
三、MQTT 消息總線
四、嵌入式系統(tǒng)中如何利用 MQTT 消息總線
五、Mosquitto: 一個簡單的測試代碼
六、總結
一、Linux 系統(tǒng)中的進程之間通信(IPC)作為一名嵌入式軟件開發(fā)人員來說,處理進程之間的通信是很常見的事情。從通信目的的角度來看,我們可以把進程之間的通信分成 3 種:
為了進程的調(diào)度: 可以通過信號來實現(xiàn);
為了共享資源:可以通過互斥鎖、信號量、讀寫鎖、文件鎖等來實現(xiàn);
為了傳遞數(shù)據(jù):可以通過共享內(nèi)存、命名管道、消息隊列、Socket來實現(xiàn)。
關于上面提到的這些、操作系統(tǒng)為我們提供的通信原語,網(wǎng)絡上的各種資料、文章滿天飛,在這里就不啰嗦了。在這些方法中應該如何選擇呢?根據(jù)我個人的經(jīng)驗,貴精不貴多,認真挑選三四樣東西就能完全滿足日常的工作需要。
我們今天想討論的問題主要是第 3 個:傳遞數(shù)據(jù),在上面這幾種傳遞數(shù)據(jù)的方法中,我最喜歡、最常用的就是 Socket 通信。
有些小伙伴可能會說:Socket 通信就是 TCP/IP 的那一套東西,還需要自己管理連接、對數(shù)據(jù)進行組包、分包,也是挺麻煩的。
沒錯,Socket 通信本身的確需要手動來處理這些底層的東西,但是我們可以給 Socket 穿上一層“外衣”:利用 MQTT 消息總線,在系統(tǒng)的各進程之間進行數(shù)據(jù)交互,下面我們就一一道來。
二、基于 Socket 通信的優(yōu)點這里我就不自己發(fā)揮了,直接引用陳碩老師的那本書《Linux 多線程服務端編程》這本書中的觀點(第 65 頁,3.4小節(jié)):
1. 跨主機,具有伸縮性
反正都是多進程了,如果一臺機器的處理能力不夠,就能用多臺主機來處理。把進程分散到同一臺局域網(wǎng)的多臺機器上,程序改改 Host:Port 配置就能繼續(xù)用。相反,文章開頭部分列出的那些進程之間通信方式都不能跨機器,這就限制了可擴展性。
2. 操作系統(tǒng)會自動回收資源
TCP port 由一個進程獨占,當程序意外退出時,操作系統(tǒng)會自動回收資源,不會給系統(tǒng)留下垃圾,程序重啟之后能比較容易地恢復。
3. 可記錄、可重現(xiàn)
兩個進程通過 TCP 通信,如果一個崩潰了,操作系統(tǒng)會關閉連接,另一個進程幾乎立刻就能感受到,可以快速 failover。當然應用層的心跳是必不可少的。(補充:操作系統(tǒng)本身對于 TCP 連接有一個?;顣r間,默認是 2 個小時,而且是針對全局的。)
4. 跨語言
服務端和客戶端不必使用同一種編程語言。
1. 陳碩老師描述的是通用的 Socket 通信,因此客戶端和服務端一般位于不同的物理機器上。
2. 在嵌入式開發(fā)中,一般都是用同一種編程語言,因此,跨語言這個有點可以忽略不計了。
三、MQTT 消息總線1. MQTT 是一個通信的機制
對物聯(lián)網(wǎng)領域熟悉的小伙伴,對于 MQTT 消息總線一定非常熟悉,目前幾大物聯(lián)網(wǎng)云平臺(亞馬孫、阿里云、華為云)都提供了 MQTT 協(xié)議的接入方式。
目前,學習 MQTT 最好的文檔是 IBM 的在線手冊:https://developer.ibm.com/zh/technologies/messaging/articles/iot-mqtt-why-good-for-iot/。
這里,我直接把一些重點信息列出來:
MQTT協(xié)議輕量、簡單、開放和易于實現(xiàn);
MQTT 是基于發(fā)布 (Publish)/訂閱 (Subscribe)范式的消息協(xié)議;
MQTT 工作在 TCP/IP協(xié)議族上;
有三種消息發(fā)布服務質(zhì)量;
小型傳輸,開銷很小(固定長度的頭部是 2 字節(jié)),協(xié)議交換最小化,以降低網(wǎng)絡流量;
MQTT 消息傳輸需要一個中間件,稱為:Broker,其實也就是一個 Server。通信模型如下:
MQTT Broker 需要首先啟動;
ClientA 和 ClientB 需要連接到 Broker;
ClientA 訂閱主題 topic_1,ClientB 訂閱主題 topic_2;
ClientA 往 topic_2 這個主題發(fā)送消息,就會被 ClientB 接收到;
ClientB 往 topic_1 這個主題發(fā)送消息,就會被 ClientA 接收到;
基于 topic 主題的通信方式有一個很大的好處就是解耦,一個客戶端可以訂閱多個 topic,任何接入到總線的其他客戶端都可以往這些 topic 中發(fā)送信息(一個客戶端發(fā)送消息給自己也是可以的)。
2. MQTT 的實現(xiàn)
MQTT 只是一個協(xié)議而已,在 IBM 的在線文檔中可以看到,有很多語言都實現(xiàn)了 MQTT 協(xié)議,包括:C/C++、Java、Python、C#、JavaScript、Go、Objective-C等等。那么對于嵌入式開發(fā)來說,使用比較多的是這幾個實現(xiàn):
Mosquitto;
Paho MQTT;
wolfMQTT;
MQTTRoute。
在下面,我們會重點介紹 Mosquitto 這個開源實現(xiàn)的編譯和使用方式,這也是我在項目中使用最多的。
3. 在 MQTT 之上,設計自己的通信協(xié)議
從上面的描述中可以看出,MQTT 消息總線就是一個通信機制,為通信主體提供了一個傳遞數(shù)據(jù)的通道而已。
在這個通道之上,我們可以根據(jù)實際項目的需要,發(fā)送任何格式、編碼的數(shù)據(jù)。在項目中,我們最常用的就是 json 格式的純文本,這也是各家物聯(lián)網(wǎng)云平臺所推薦的方式。如果在文本數(shù)據(jù)中需要包含二進制數(shù)據(jù),那就轉(zhuǎn)成 BASE64 編碼之后再發(fā)送。
四、嵌入式系統(tǒng)中如何利用 MQTT 消息總線從上面的描述中可以看到,只要在服務端運行著一個 MQTT Broker 服務,每個連接到總線的客戶端都可以靈活地相互收發(fā)數(shù)據(jù)。
我們可以把這個機制應用在嵌入式應用程序的設計中:MQTT Broker 作為一個獨立的服務運行在嵌入式系統(tǒng)本地,其他需要交互的進程,只要連接到本地的這個 Broker,就可以相互發(fā)送數(shù)據(jù)了。運行模型如下:
每一個進程只需要訂閱一個固定的 topic(比如:自己的 client Id),那么其他進程如果想要發(fā)送數(shù)據(jù)給它,就直接發(fā)送到這個 topic 即可。
1. 一個嵌入式系統(tǒng)的通信框架
我之前開發(fā)過一個環(huán)境監(jiān)測系統(tǒng),采集大氣中的 PM2.5、PM10等污染物參數(shù),在 Contex A8 平臺下開發(fā),需要實現(xiàn)數(shù)據(jù)記錄(數(shù)據(jù)庫)、UI 監(jiān)控界面等功能。
污染物的數(shù)據(jù)采樣硬件模塊是第三方公司提供的,我們只需要通過該模塊提供的串口協(xié)議去控制采樣設備、接收采樣數(shù)據(jù)即可。最終設計的通信模型如下:
UI 進程通過消息總線,發(fā)送控制指令給采樣控制進程,采樣控制進程接收到后通過串口發(fā)送控制指令給采樣模塊;
采樣控制進程從串口接收采樣模塊發(fā)來的PM2.5等數(shù)據(jù)后,把所有的數(shù)據(jù)發(fā)送到消息總線上指定的 topic 中;
UI 進程程訂閱該 topic,接收到數(shù)據(jù)后,顯示在屏幕上;
數(shù)據(jù)庫進程也訂閱該 topic,接收到數(shù)據(jù)后,把數(shù)據(jù)存儲在 SQLite 數(shù)據(jù)庫中;
在這個產(chǎn)品中,核心進程是采樣控制進程,負責與采樣模塊的交互。通過把 UI 處理、數(shù)據(jù)庫處理設計成獨立的進程,降低了系統(tǒng)的復雜性,即使這 2 個進程崩潰了,也不會影響到核心的采樣控制進程。
比如:如果 UI 進程出現(xiàn)錯誤崩潰了,會立刻重啟,啟動之后通過緩存信息知道此刻正在執(zhí)行采樣工作,于是 UI 進程立刻連接到消息總線、進入采樣數(shù)據(jù)顯示界面,繼續(xù)接收、顯示采樣控制進程發(fā)出的PM2.5等數(shù)據(jù)。
這個通信模型還有另外一個有點:可擴展性。
在項目開發(fā)的后期,甲方說需要集成一個第三方的氣體模塊,用來采集大氣中NO、SO2等參數(shù),通信方式是 RS485。
此時擴展這個功能模塊就異常簡單了,直接寫一個獨立的氣體參數(shù)進程,接入到消息總線上。這個進程通過 RS485,從第三方氣體模塊接收到NO、SO2等氣體參數(shù)時,直接往消息總線上的某個 topic 一丟,UI進程、數(shù)據(jù)庫進程訂閱這個 topic,就可以立刻接收到氣體相關的數(shù)據(jù)了。
此外,這個設計模型還有其他一些優(yōu)點:
并行開發(fā):每個進程可以由不同的人員并行開發(fā),只要相互之間定義好通信協(xié)議即可;
調(diào)試方便:由于發(fā)送的數(shù)據(jù)都是 manual readable,在開發(fā)階段,可以在 PC 機上專門寫一個監(jiān)控程序,接入到嵌入式系統(tǒng)中的 MQTT Broker 之后,這樣就可以接收到所有進程發(fā)出的消息;
通信安全:在產(chǎn)品 release 之后,為了防止其他人偷聽數(shù)據(jù)(比如 2 中的調(diào)試進程),可以為 MQTT Broker 指定一個配置文件,只能允許本地進程(127.0.0.1)連接到消息總線上。
2. 稍微復雜一點的通信模型
在剛才描述的嵌入式系框架設計中,每一個進程都是運行在本地的,所有的消息也都是在系統(tǒng)內(nèi)進行收發(fā)。那么,如果需要把數(shù)據(jù)傳輸?shù)皆贫恕⒒蛘咝枰獜脑贫私邮找恍┛刂浦噶?,又該如何設計呢?
加入一個 MQTT Bridge 橋接模塊即可!也就是再增加一個進程,這個進程同時連接到云端的 MQTT Broker 和本地的 MQTT Broker,通信模型如下:
MQTT Bridge 接收到云端發(fā)來的指令時,轉(zhuǎn)發(fā)到本地的消息總線上;
MQTT Bridge 接收到本地的消息時,轉(zhuǎn)發(fā)到云端的消息總線上。
五、Mosquitto: 一個簡單的測試代碼上面的內(nèi)容主要討論的是設計的思想,具體到代碼層面,我一般使用的是 Mosquitto 這個開源的實現(xiàn)。
在 Linux 系統(tǒng)中安裝、測試都非常方便,下面就簡單說明一下。
1. 直接通過 apt 來安裝、測試
可以參考這個文檔(https://www.vultr.com/docs/how-to-install-mosquitto-mqtt-broker-server-on-ubuntu-16-04)來安裝測試。
(1) 安裝
sudo apt-add-repository ppa:mosquitto-dev/mosquitto-ppasudo apt-get updatesudo apt-get install mosquittosudo apt-get install mosquitto-clients
(2) 測試
mosquitto broker 在安裝之后會自動啟動,可以用 netstat 查看 1883 端口來確認一下。
接收端:連接到 broker 之后,訂閱 “test” 這個 topic。
mosquitto_sub -t “test”
發(fā)送端:連接到 broker 之后,往 “test” 這個 topic 發(fā)送字符串 “hello”。
mosquitto_pub -m “hello” -t “test”
當發(fā)送端執(zhí)行 mosquitto_pub 時,在接收端的終端窗口中,就可以接收到 “hello” 這個字符串。
2. 通過源碼來手動編譯、測試
通過 apt 來安裝主要是用來簡單的學習和測試,如果要在項目開發(fā)中使用 Mosquitto,肯定需要手動編譯,得到頭文件和庫文件,然后復制到應用程序中使用。
(1) 手動編譯、安裝 Mosquitto
我的開發(fā)環(huán)境是:
編譯器:gcc (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609
Mosquitto 版本:mosquitto-1.4.9
mosquitto-1.4.9 可以到官方網(wǎng)站下載,也可以從文末的網(wǎng)盤中下載,你也可以嘗試更高的版本。
編譯、安裝指令:
makemake install prefix=$PWD/install
成功安裝之后,可以在當前目錄的 install 文件夾下看到輸出文件:
bin:mqtt 客戶端程序;
include:應用程序需要 include 的頭文件;
lib:應用程序需要鏈接的庫文件;
sbin:mqtt broker 服務程序。
在編譯過程中,如果遇到一些諸如:ares.h、uuid.h 等依賴文件找不到的錯誤,只需要通過 apt 指令安裝響應的開發(fā)包即可。
(2) 最簡單的 mosquitto 客戶端代碼
在 mosquitto 源碼中,提供了豐富的 Sample 示例。如果你不樂意去探索,可以直接下載文末的這個網(wǎng)盤中的 Demo 示例程序,這個程序連接到消息總線上之后,訂閱 “topic_01” 這個主題。當然,你也可以修改代碼去發(fā)送消息(調(diào)用:mosquitto_publish 這個函數(shù))。
進入 c_mqtt 示例代碼目錄之后,可以看到已經(jīng)包含了 bin、include 和 lib 目錄,它們就是從上面(1)中安裝目錄 install 中復制過來的。
執(zhí)行 make 指令之后,即可編譯成功,得到可執(zhí)行文件:mqtt_client。
測試過程如下:
Step1: 啟動 MQTT Broker
在第 1 個終端窗口中,啟動 sbin/mosquitto 這個 Broker 程序。如果你在上面測試中已經(jīng)啟動了一個 broker,需要先 kill 掉之前的那個 broker,因為它們默認都使用 1883 這個端口,無法共存。
Step2: 啟動接收端程序 mqtt_client
在第 2 個終端窗口中,啟動 mqtt_client 也就是我們的示例代碼編譯得到的可執(zhí)行程序,它訂閱的 topic 是 “topic_01”。
。/mqtt_client 127.0.0.1 1883
參數(shù) 1: Broker 服務的 IP 地址,因為都是在本地系統(tǒng)中,所以是 127.0.0.1;
參數(shù) 2: 端口號,一般默認是1883。
Step3: 啟動發(fā)送端程序 bin/mosquitto_pub
在第 3 個終端窗口中,啟動 bin/mosquitto_pub,命令如下:
。/mosquitto_pub -h 127.0.0.1 -p 1883 -m “hello123” -t “topic_01”
參數(shù) -h:Broker 服務的 IP 地址,因為都是在本地系統(tǒng)中,所以是 127.0.0.1;參數(shù) -p:端口號 1883;
參數(shù) -m:發(fā)送的消息內(nèi)容;
參數(shù) -t:發(fā)送的主題 topic。
此時,可以在第 2 個終端窗口(mqtt_client)中打印出接收到的消息。
六、總結這篇文章主要介紹了嵌入式系統(tǒng)中的一個設計模式:通過消息總線來實現(xiàn)進程之間的通信,并介紹了 Mosquitto 這個開源實現(xiàn)。
在實際的項目中,還需要更加嚴格的權限控制,比如:在接入消息總線時提供用戶名、密碼、設備證書,客戶端的名稱必須滿足指定的格式,訂閱的 topic 必須符合一定的格式等等。
在下一篇文章中,我們繼續(xù)討論這個話題,給出一個更具體、更實用的 Demo 例程。
原文標題:進程之間通信方式:最喜歡消息總線
文章出處:【微信公眾號:玩轉(zhuǎn)單片機】歡迎添加關注!文章轉(zhuǎn)載請注明出處。
責任編輯:haq
-
嵌入式
+關注
關注
5094文章
19189瀏覽量
307943 -
通信
+關注
關注
18文章
6075瀏覽量
136467 -
Linux
+關注
關注
87文章
11351瀏覽量
210493
原文標題:進程之間通信方式:最喜歡消息總線
文章出處:【微信號:mcu168,微信公眾號:硬件攻城獅】歡迎添加關注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關推薦
評論