鴻蒙分布式軟總線技術研究


一、HarmonyOS概述

1.1系統定義

1.1.1 系統定位

  HarmonyOS是一款“面向未來”、面向全場景(移動辦公、運動健康、社交通信、媒體娛樂等)的分布式操作系統。在傳統的單設備系統能力的基礎上,HarmonyOS提出了基於同一套系統能力、適配多種終端形態的分布式理念,能夠支持手機、平板、智能穿戴、智慧屏、車機等多種終端設備。

  • 對消費者而言,HarmonyOS能夠將生活場景中的各類終端進行能力整合,可以實現不同的終端設備之間的快速連接、能力互助、資源共享,匹配合適的設備、提供流暢的全場景體驗。
  • 對應用開發者而言,HarmonyOS采用了多種分布式技術,使得應用程序的開發實現與不同終端設備的形態差異無關。這能夠讓開發者聚焦上層業務邏輯,更加便捷、高效地開發應用。
  • 對設備開發者而言,HarmonyOS采用了組件化的設計方案,可以根據設備的資源能力和業務特征進行靈活裁剪,滿足不同形態的終端設備對於操作系統的要求。

  HarmonyOS提供了支持多種開發語言的API,供開發者進行應用開發。支持的開發語言包括Java、XML(Extensible Markup Language)、C/C++ 、 JS(JavaScript)、CSS(Cascading Style Sheets)和HML(HarmonyOS Markup Language)。

1.1.2 技術架構

  HarmonyOS整體遵從分層設計,從下向上依次為:內核層、系統服務層、框架層和應用層。系統功能按照“系統 > 子系統 > 功能/模塊”逐級展開,在多設備部署場景下,支持根據實際需求裁剪某些非必要的子系統或功能/模塊。HarmonyOS技術架構如下所示。

內核層

  • 內核子系統:HarmonyOS采用多內核設計,支持針對不同資源受限設備選用適合的OS內核。內核抽象層(KAL,Kernel Abstract Layer)通過屏蔽多內核差異,對上層提供基礎的內核能力,包括進程/線程管理、內存管理、文件系統、網絡管理和外設管理等。
  • 驅動子系統:硬件驅動框架(HDF)HarmonyOS硬件生態開放的基礎,提供統一外設訪問能力和驅動開發、管理框架。

系統服務層

  系統服務層是HarmonyOS的核心能力集合,通過框架層對應用程序提供服務。該層包含以下幾個部分:

  • 系統基本能力子系統集:為分布式應用在HarmonyOS多設備上的運行、調度、遷移等操作提供了基礎能力,由分布式軟總線、分布式數據管理、分布式任務調度、方舟多語言運行時、公共基礎庫、多模輸入、圖形、安全、AI等子系統組成。其中,方舟運行時提供了C/C++/JS多語言運行時和基礎的系統類庫,也為使用方舟編譯器靜態化的Java程序(即應用程序或框架層中使用Java語言開發的部分)提供運行時。
  • 基礎軟件服務子系統集:為HarmonyOS提供公共的、通用的軟件服務,由事件通知、電話、多媒體、DFX(Design For X) 、MSDP&DV等子系統組成。
  • 增強軟件服務子系統集:為HarmonyOS提供針對不同設備的、差異化的能力增強型軟件服務,由智慧屏專有業務、穿戴專有業務、IoT專有業務等子系統組成。
  • 硬件服務子系統集:為HarmonyOS提供硬件服務,由位置服務、生物特征識別、穿戴專有硬件服務、IoT專有硬件服務等子系統組成。

  根據不同設備形態的部署環境,基礎軟件服務子系統集、增強軟件服務子系統集、硬件服務子系統集內部可以按子系統粒度裁剪,每個子系統內部又可以按功能粒度裁剪。

框架層

  框架層為HarmonyOS應用開發提供了Java/C/C++/JS等多語言的用戶程序框架和Ability框架,兩種UI框架(包括適用於Java語言的Java UI框架、適用於JS語言的JS UI框架),以及各種軟硬件服務對外開放的多語言框架API。根據系統的組件化裁剪程度,HarmonyOS設備支持的API也會有所不同。

應用層

  應用層包括系統應用和第三方非系統應用。HarmonyOS的應用由一個或多個FA(Feature Ability)PA(Particle Ability)組成。其中,FA有UI界面,提供與用戶交互的能力;而PA無UI界面,提供后台運行任務的能力以及統一的數據訪問抽象。FA在進行用戶交互時所需的后台數據訪問也需要由對應的PA提供支撐。基於FA/PA開發的應用,能夠實現特定的業務功能,支持跨設備調度與分發,為用戶提供一致、高效的應用體驗。

1.2 技術特性

  Harmony的技術特性主要是多種設備之間能夠實現硬件互助,資源共享。 依賴的技術主要包括分布式軟總線、分布式設備虛擬化、分布式數據管理、分布式任務調度等。本報告將主要調研分布式軟總線技術。

1.2.1 分布式軟總線

  分布式軟總線是手機、平板、智能穿戴、智慧屏、車機等分布式設備的通信基座,為設備之間的互聯互通提供了統一的分布式通信能力,為設備之間的無感發現和零等待傳輸創造了條件。開發者只需聚焦於業務邏輯的實現,無需關注組網方式與底層協議。分布式軟總線示意圖如下: 

典型應用場景舉例:

  • 智能家居場景:在制作粉蒸肉時,手機可以通過碰一碰和烤箱連接,並將自動設置粉蒸肉的制作參數,控制烤箱來制作菜餚。與此類似,料理機、油煙機、空氣凈化器、空調、燈、窗簾等都可以在手機端顯示並通過手機控制。設備之間即連即用,無需繁瑣的配置。
  • 多屏聯動課堂:老師通過智慧屏授課,與學生開展互動,營造課堂氛圍;學生通過手機完成課程學習和隨堂問答。統一、全連接的邏輯網絡確保了傳輸通道的高帶寬、低時延、高可靠。

1.2.2 分布式設備虛擬化

  分布式設備虛擬化平台可以實現不同設備的資源融合、設備管理、數據處理,多種設備共同形成一個超級虛擬終端。針對不同類型的任務,為用戶匹配並選擇能力合適的執行硬件,讓業務連續地在不同設備間流轉,充分發揮不同設備的能力優勢,如顯示能力、攝像能力、音頻能力、交互能力以及傳感器能力等。分布式設備虛擬化示意圖如下:

1.2.3 分布式數據管理

  分布式數據管理基於分布式軟總線的能力,實現應用程序數據和用戶數據的分布式管理。用戶數據不再與單一物理設備綁定,業務邏輯與數據存儲分離,跨設備的數據處理如同本地數據處理一樣方便快捷,讓開發者能夠輕松實現全場景、多設備下的數據存儲、共享和訪問,為打造一致、流暢的用戶體驗創造了基礎條件。分布式數據管理示意圖如下:

1.2.4 分布式任務調度

  分布式任務調度基於分布式軟總線、分布式數據管理、分布式Profile等技術特性,構建統一的分布式服務管理(發現、同步、注冊、調用)機制,支持對跨設備的應用進行遠程啟動、遠程調用、遠程連接以及遷移等操作,能夠根據不同設備的能力、位置、業務運行狀態、資源使用情況,以及用戶的習慣和意圖,選擇合適的設備運行分布式任務。

  下圖以應用遷移為例,簡要地展示了分布式任務調度能力。

  除了硬件互助,資源共享外,HarmonyOS還支持一次開發、多端部署,統一OS、彈性部署,HarmonyOS提供了用戶程序框架、Ability框架以及UI框架,支持應用開發過程中多終端的業務邏輯和界面邏輯進行復用,能夠實現應用的一次開發、多端部署,提升了跨設備應用的開發效率。一次開發、多端部署示意圖如下:

  HarmonyOS通過組件化和小型化等設計方法,支持多種終端設備按需彈性部署,能夠適配不同類別的硬件資源和功能需求。支撐通過編譯鏈關系去自動生成組件化的依賴關系,形成組件樹依賴圖,支撐產品系統的便捷開發,降低硬件設備的開發門檻。

  • 支持各組件的選擇(組件可有可無):根據硬件的形態和需求,可以選擇所需的組件。
  • 支持組件內功能集的配置(組件可大可小):根據硬件的資源情況和功能需求,可以選擇配置組件中的功能集。例如,選擇配置圖形框架組件中的部分控件。
  • 支持組件間依賴的關聯(平台可大可小):根據編譯鏈關系,可以自動生成組件化的依賴關系。例如,選擇圖形框架組件,將會自動選擇依賴的圖形引擎組件等。 

二、分布式軟總線介紹

2.1 總線 

  總線(Bus)是一個非常廣泛的概念,在傳統計算機硬件體系中應用的非常廣泛。它是一種內部結構,是cpu、內存、輸入、輸出設備傳遞信息的公用通道,主機的各個部件通過總線相連接,外部設備通過相應的接口電路再與總線相連接,從而形成了計算機硬件系統。在計算機系統中,各個部件之間傳送信息的公共通路叫總線,微型計算機是以總線結構來連接各個功能部件的。按照計算機所傳輸的信息種類,計算機的總線可以划分為數據總線、地址總線和控制總線,分別用來傳輸數據、數據地址和控制信號。

2.2 分布式軟總線定義

  分布式軟總線技術是基於華為多年的通信技術積累,參考計算機硬件總線,在1+8+N(1-手機、8-車機/音箱/耳機/手表/)設備間搭建一條“無形”的總線,具備自發現、自組網、高帶寬低時延的特點。

  全場景設備間可以基於軟總線完成設備虛擬化、跨設備服務調用、多屏協同、文件分享等分布式業務。

  分布式軟總線的典型特征:

  • 自動發現/即連即用

  • 高帶寬

  • 低時延

  • 高可靠

  • 開放/標准

2.3 分布式軟總線的功能和原理

2.3.1 分布式軟總線的架構

  通過協議貨架和軟硬協同層屏蔽各種設備的協議差別,總線中樞模塊負責解析命令完成設備間發現和連接,通過任務和數據兩條總線實現設備間文件傳輸、消息傳輸等功能。

  分布式總線的總體目標是實現設備間無感發現,零等待傳輸。實現這個目標需要解決三個問題:

(1)設備間的發現和連接:從手動發現,進化成自發現

  比如手機上有很多照片需要傳到個人PC上,我們可以采用藍牙傳輸,首先要打開手機和PC的藍牙發現功能,手機或者PC點擊搜索設備,然后互相配對授權即可連接上,成功連上后就可以發送照片了。

  在分享照片這個場景中有很多人為的動作:開啟藍牙發現功能、搜索設備、配對授權,這確實有點麻煩,耗費了很多時間,可能會降低分享的意願。

  軟總線提出了自動發現的概念,實現用戶零等待的自發現體驗,附近同賬號的設備自動發現無需等待。

(2)多設備互聯后的組網技術:軟總線組網-異構網絡組網

  上面的例子中手機傳照片是通過藍牙,假如PC沒有藍牙功能只有WIFI,在傳統的場景中這種可能就不能實現分享傳輸了。因為不同的組網方式之間是隔離的,所以我們要解決很多異構網絡之間的組網問題

  軟總線提出的異構網絡組網可以很好解決設備間不同協議的交互問題,就解決了手機通過藍牙傳輸,而PC通過WIFI接收照片。解決方案如下圖所示。

 

  設備上線后會向網絡層注冊,同時網絡層會與設備建立通道連接,實時檢測設備的變換。網絡層負責管理設備的上線下線變換,設備間可以監聽自己感興趣的設備,設備上線后可以立即與其建立連接,實現零等待體驗。軟總線可以自動構建一個邏輯全連接網絡,用戶或者業務開發者無需關心組網方式與物理協議。對於軟件開發者來說軟總線異構組網可以大大降低其開發成本。

  傳統開發模式:在傳統開發模式中開發者需要適配不同網絡協議和標准規范。

  分布式開發模式:在HarmonyOS分布式開發模式中開發不再需要關心網絡協議差異,業務開發與設備組網解耦,業務僅需監聽設備上下線,開發成本大大降低。

(3)多設備多協議間的傳輸技術

  傳統協議的傳輸速率差異非常大,時延也難以得到保證。所以軟總線傳輸要實現的目標有:高帶寬(High Speed)、低時延(Low Latency)、高可靠(High Reliability)

  軟總線要實現的這三大目標的尖刀武器是:極簡協議。

  極簡協議將中間的四層協議棧精簡為一層提升有效載荷,有效傳輸帶寬提升20%,並且在傳統網絡協議的基礎上進行增強:

  • 流式傳輸:基於UDP實現數據的保序和可靠傳輸;

  • 雙輪驅動:顛覆傳統TCP每包確認機制;

  • 不懼網損:摒棄傳統滑動窗口機制,丟包快速恢復,避免阻塞;

  • 不懼抖動:智能感知網絡變化,自適應流量控制和擁塞控制;

三、Harmony內核編譯

3.1系統環境

Ubuntu 20.0.4.1 LTS
gccc 9.3.0
gdb 9.1

 

 

 

 

3.2編譯

3.2.1 鴻蒙os開源代碼下載

  鴻蒙OS代碼已開源,可以方便的從代碼倉庫下載源代碼;分布式軟中線代碼倉庫地址如下:

  也可以通過OpenHarmony官方開發者文檔獲取,地址:OpenHarmony開發者文檔,通過鏡像站點獲取OpenHarmony全量代碼進行實驗。

  下載完畢后,拷貝到ubuntu中,並解壓。

  解壓完成后,可以找到軟總線的源代碼。softbus_lite。

3.2.2 系統環境准備

3.2.2.1 python 3.8.2 及環境配置

(1)安裝python

(2)設置python3軟鏈接

(3)安裝包管理工具pip

 

(4)安裝python模塊setuptools

(5)安裝GUI menuconfig工具(Kconfiglib)

(6)安裝升級文件簽名依賴的Python組件包,包括:pycrytodome、six、esdsa。安裝ecdsa依賴six,所以先安裝six再安裝ecdsa。

 

 3.2.2.2 安裝Scons

 3.2.2.3 安裝gn

(1)下載gn工具 gn下載地址

(2)在ubuntu中解壓gn安裝包到自己gn的目錄

(3)設置環境變量 vim ~/.bashrc,並在最后一行添加gn的路徑。

(4)使用source讓環境變量生效 

 

3.2.2.4 安裝ninja

(1)下載ninja工具 ninja下載地址 

(2)在ubuntu中解壓ninja安裝包到自己ninja的目錄

(3)設置環境變量並讓起生效

3.2.2.6 將linux shell改為bash

3.2.3 內核編譯

  首先配置MakeFile,主要代碼如下所示:

  將MakeFile放在OpenHarmony文件夾,在終端中執行make。

  第一次編譯時遇到了錯誤,沒有找到unit16_t的數據類型,根據阮旭東同學的調研報告找到解決方法,在出現錯誤的tcp_socket.c文件中加入一行數據類型的聲明。修改后成功完成編譯。

  至此,鴻蒙軟總線編譯完成。

四、示例程序運行

4.1 用於測試的demo

  1 #include<discovery_service.h>
  2 #include<session.h>
  3 #include<coap_discover.h>
  4 #include<tcp_session_manager.h>
  5 #include<nstackx.h>
  6 
  7 #include<stdio.h>
  8 #include<string.h>
  9 
 10 // 定義業務⾃身的業務名稱,會話名稱及相關回調
 11 const char *g_pkgName = "BUSINESS_NAME";
 12 const char *g_sessionName = "SESSION_NAME";
 13 struct ISessionListener * g_sessionCallback= NULL;
 14 
 15 #define NAME_LENGTH 64
 16 #define TRANS_FAILED -1
 17 
 18 // 回調實現:接收對方通過SendBytes發送的數據,此示例實現是接收到對端發送的數據后回復固定消息
 19 void OnBytesReceivedTest(int sessionId, const void* data, unsigned int dataLen)
 20 {
 21     printf("OnBytesReceivedTest\n");
 22     printf("Recv Data: %s\n", (char *)data);
 23     printf("Recv Data dataLen: %d\n", dataLen);
 24     char *testSendData = "Hello World, Hello!";
 25     SendBytes(sessionId, testSendData, strlen(testSendData));
 26     return;
 27 }
 28 
 29 // 回調實現:用於處理會話關閉后的相關業務操作,如釋放當前會話相關的業務資源,會話無需業務主動釋放
 30 void OnSessionClosedEventTest(int sessionId)
 31 {
 32     printf("Close session successfully, sessionId=%d\n", sessionId);
 33 }
 34 
 35 // 回調實現:用於處理會話打開后的相關業務操作。返回值為0,表示接收;反之,非0表示拒絕。此示例表示只接受其他設備的同名會話連接
 36 int OnSessionOpenedEventTest(int sessionId)
 37 {
 38     char sessionNameBuffer[NAME_LENGTH+1];
 39     if(GetPeerSessionName(sessionId,sessionNameBuffer,NAME_LENGTH) == TRANS_FAILED) {
 40         printf("GetPeerSessionName faild, which sessionId = %d\n",sessionId);
 41         return -1;
 42     }
 43     if (strcmp(sessionNameBuffer, g_sessionName) != 0) {
 44         printf("Reject the session which name is different from mine, sessionId=%d\n", sessionId);
 45         return -1;
 46     }
 47     printf("Open session successfully, sessionId=%d\n", sessionId);
 48     return 0;
 49 }
 50 
 51 // 向SoftBus注冊業務會話服務及其回調
 52 int StartSessionServer()
 53 {
 54     if (g_sessionCallback == NULL) {
 55         g_sessionCallback = (struct ISessionListener*)malloc(sizeof(struct ISessionListener));
 56     }
 57     if (g_sessionCallback == NULL) {
 58         printf("Failed to malloc g_sessionCallback!\n");
 59         return -1;
 60     }
 61     g_sessionCallback->onBytesReceived = OnBytesReceivedTest;
 62     g_sessionCallback->onSessionOpened = OnSessionOpenedEventTest;
 63     g_sessionCallback->onSessionClosed = OnSessionClosedEventTest;
 64     int ret = CreateSessionServer(g_pkgName, g_sessionName, g_sessionCallback);
 65     if (ret < 0) {
 66         printf("Failed to create session server!\n");
 67         free(g_sessionCallback);
 68         g_sessionCallback = NULL;
 69     }
 70     return ret;
 71 }
 72 
 73 // 從SoftBus中刪除業務會話服務及其回調
 74 void StopSessionServer()
 75 {
 76     int ret = RemoveSessionServer(g_pkgName, g_sessionName);
 77     if (ret < 0) {
 78         printf("Failed to remove session server!\n");
 79         return;
 80     }
 81     if (g_sessionCallback != NULL) {
 82         free(g_sessionCallback);
 83         g_sessionCallback = NULL;
 84     }
 85 }
 86 
 87 // 回調函數聲明:
 88 void onSuccess(int publishId)
 89 {
 90     printf("publish succeeded, publishId = %d\r\n", publishId);
 91     char ipbuff[NSTACKX_MAX_IP_STRING_LEN] = {"0.0.0.0"};
 92     CoapGetIp(ipbuff,NSTACKX_MAX_IP_STRING_LEN,0);
 93     printf("CoapGetIp = %s\n",ipbuff);
 94     if(StartSessionServer()!=-1)
 95         printf("StartSessionServer successed!\n");
 96 }
 97 void onFail(int publishId, PublishFailReason reason)
 98 {
 99     printf("publish failed, publishId = %d, reason = %d\r\n", publishId, reason);
100 }
101 
102 int main(){
103     // 服務發布接口使用
104     PublishInfo info = {0};
105     IPublishCallback cb = {0};
106     cb.onPublishSuccess = onSuccess;
107     cb.onPublishFail = onFail;
108     char a[] = "456";
109     info.capabilityData = a;
110     info.capability = "ddmpCapability";
111     info.dataLen = strlen(a);
112     info.medium = 2;
113     info.publishId = 1;
114     PublishService("cxx", &info, &cb);
115 }

4.2 編譯運行

  將libsoft_bus_lite.so放入根目錄的/lib,然后執行

gcc -o publshiServer harmonyTest.c -lsoftbus_lite -lrt -lpthread -I /usr/local/softbus/include

  得到可執行文件publshiServer,用sudo權限執行

4.3 分析

(1) 發現端設備,發起discover請求后,使用coap協議在局域網內發送廣播。報文如下:

(2)被發現端設備使用PublishService接口發布服務,接收端收到廣播后,發送coap協議單播給發現端。報文格式如下:

(3)發現端設備收到報文會更新設備信息。

五、源碼分析

 5.1 軟總線代碼結構

  在源代碼目錄,我們可以通過ls看到主要分為四個目錄,分別是authmanager、discovery、trans_service、 和為兼容系統差別而生的os_adapter。

  1. discover:提供基於 COAP 協議的設備發現機制;
  2. authmanager:提供設備認證機制和知識庫管理功能;
  3. trans_service:提供身份驗證和數據傳輸通道;
  4. os_adapter:檢測運行設備性能,決定部分功能是否執行。 

(1)authmanager

  authmanager提供設備認證機制和設備知識庫管理,當發現有請求時,調用ProcessDataEvent函數,收包,檢驗包頭,根據數據包的類型確定不同的處理方式。類型主要包括以下三種:

  1. MODULE_AUTH_SDK     加密數據類型
  2. MODULE_TRUST_ENGINE 可信類型,直接進行數據傳輸
  3. MODULE_CONNECTION  進行ip及設備認證

  authmanager源碼結構及功能概述如下:

  • auth_conn.c  提供發送、接收、認證、獲取秘鑰功能;
  • auth_interface.c

  管理各個會話節點、各個鏈接節點、各個秘鑰節點,提供包括增刪改查等功能;

  • msg_get_deviceid.c

  提供以cJSON格式獲取各個設備的信息,包括設備id、鏈接信息、設備名、設備類型等;

  • bus_manager.c

  主要通過deviceIp創建兩個不同的listen,主要用來監聽系統上有哪些device及新的device節點的創建;其中有兩個回調函數OnConnectEvent和OnDataEvent,分別是用來處理設備節點的基本操作及節點數據的處理;

  • wifi_auth_manager.c

  主要實現了連接管理和數據接收功能。連接管理包括連接的建立、斷開及連接的查找。數據接收包括數據獲取、包頭及包長等的校驗,並且為了簡化包頭數據讀取,單獨實現了對一個int型和一個long型數據的接收函數;

(2) discover

  discover是一種基於coap協議的設備發現機制,選擇coap協議是因為物聯網設備的ram,rom都通常非常小,運行TCP和HTTP是不可以接受的。而coap(Constrained Application Protocol、受限應用協議)是一種在物聯網世界的類web協議,顧名思義,可以使用在資源受限的物聯網設備上。它支持可靠傳輸的輕量化協議。discover的設備發現功能就是用的這個特性。

 

  discovery 代碼包含coap和discovery_service兩部分,coap部分是coap協議封裝實現,discovery_service 是基於coap協議設備的發現流程實現。

 

(3) trans_service 

  trans_service目錄中的代碼提供身份驗證和傳輸通道。它主要封裝了socket、cJSON、線程鎖接口,實現了用戶的創建、監聽、會話管理,以及設備、指令、數據等信息的獲取,最終提供加密和解密傳輸兩種傳輸通道。 trans_service模塊依賴於系統OS提供的網絡socket服務,向認證模塊提供認證通道管理和認證數據的收發;向業務模塊提供session管理和基於session的數據收發功能,並且通過GCM模塊的加密功能提供收發報文的加解密保護。

  trans_service源碼結構及功能概述如下:

  • auth_conn_manager.c

  用戶創建,監聽,連接等服務管理;

  • tcp_session_manager.c

  會話管理;

  • trans_lock.c

  互斥鎖初始化以及互斥鎖資源獲取與釋放;

  • aes_gcm.c

  提供加密傳輸和解密傳輸接口;

  • messages.c

  用於獲取以cJSON格式管理的設備(包括設備名、設備類型、設備ID等)、指令、數據、會話(包括用戶端口、會話端口等)等信息;

  • tcp_socket.c

  端口號管理以及數據傳輸管理。

5.2 trans_service詳解

5.2.1 初始化

  在分布式軟總線的設計中,trans_service模塊是在authmanager模塊中被初始化的,而authmanager模塊又被discovery模塊初始化,因此設備在向外發布本設備信息的過程中,即完成了這三個相互關聯模塊的初始化動作。

  authmanager模塊中存在StartBus()函數,其中,StartListener()函數負責為認證模塊提供通道完成初始化,StartSession()函數負責初始化業務的session管理:

 

  認證通信與業務session的實現原理類似,本文中我們以簡單一些的認證通信代碼為例子,說明典型的處理流程。

  初始化的入口在StartListener,StartListener()函數的底層存在對應不同版本平台的適配函數,這印證了鴻蒙OS各部分解耦的模塊化設計思想,針對不同的硬件設備,組合成最適合該設備的OS。比如創建線程時采用了統一的static void WaitProcess(void)函數,而其內部封裝了不同底層API的適配代碼。

  StartListener()調用InitListenFd()函數完成監聽TCP socket的創建和監聽,其中IP地址和端口號由上層調用者指定。

 

  如上文所述,AuthCreate()在不同平台上會有不同的實現,在LITEOS_A和Linux平台上, AuthCreate()會調用兼容POSIX的pthread_create()完成線程的創建,線程的入口函數為static void WaitProcess(void)。

 5.2.2 監聽新連接和數據

  WaitProcess()使用忙等方式,調用select()來監聽listenFd和數據g_dataFd的信息,如果監聽到有數據可讀,則進入ProcessAuthData來處理。如果發現g_dataFd有異常信息,則將其關閉。其中g_dataFd是由listenFd監聽到連接時創建的socket。

5.2.3 處理新連接和數據

  無論是新連接請求,還是已有連接中有數據到來,均會進入本函數。

  函數通過FD_ISSET()判斷是否是listenFd上存在消息,如果是,則說明當前存在新的連接,這時調用accept()完成鏈接創建,新創建的socket的fd被存儲在g_dataFd中,同時調用g_callback->onConnectEvent通知認證模塊有新的連接事件發生,並將新創建的fd和client的IP地址告知認證模塊。

  與此同時,創建g_dataFd時候需要刷新g_maxFd,以保證在WaitProcess()中的下一次select()操作時中,會監聽到g_dataFd上的事件。

  如果FD_ISSET()判斷出g_dataFd上存在消息,則說明已完成握手的連接向本節點發送了數據,這時函數回調g_callback->onDataEvent(),把控制權返回給調用者,以處理接收到的數據。

5.2.4 回調函數的處理

  trans_service模塊的使用者設置的回調函數將在存在新連接、和新數據時被調用,比如認證模塊通過以下函數完成認證動作:OnConnectEvent()函數中完成對新連接的處理, OnDataEvent()函數中完成對新數據的處理。

5.2.5 業務的session處理

  該部分代碼負責業務的數據通信,節點通過名稱進行通信,對外隱藏了端口信息,代碼的實現原理與上面的認證通信類似,針對業務需求增加了如多路通信等方面的實現,這里不再詳細分析。

5.3 discovery_service詳解

  discovery目錄下的頭文件common_info_manager.h定義了目前鴻蒙OS支持的設備類型和設備級別,從定義可以看出,目前鴻蒙OS只支持L0和L1的設備。 

  用戶使用發現功能時,需要保證發現端設備與被發現端設備在同一個局域網內,並且互相能收到對方以下流程的報文。

(1)發現端設備,發起discover請求后,使用coap協議在局域網內發送廣播。

(2)被發現端設備使用PublishService接口發布服務,接收端收到廣播后,發送coap協議單播給發現端。

(3)發現端設備收到報文會更新設備信息。

  下面是設備的定義,分為本地設備(被發現端設備)和外部設備,從中可以看出鴻蒙OS的分布式特性,本地設備可以通過PublishService函數將自身的服務發布出去,供外部設備發現並使用。鴻蒙OS的設備發現機制是被發現設備在COAP端口監聽來自發現設備的廣播包,這也符合按需使用的原則。

PublishService的啟動過程

  被發現端設備通過調用PublishService向外部發布設備的能力,接口使用示例如下:

  PublishService函數如下所示:

  PublishService首先將檢查是否有發布權限以及檢查了一些發布參數的合法性,其次確認發布協議是不是COAP。然后,考慮到被發現端上可能有多個設備對外部發布服務,因此創建了一個信號量g_serviceSemId來防止沖突。進入信號量之后,主要調用InitService函數來啟動服務,並將模塊注冊到全局變量g_publishModule中,服務注冊到g_capabilityData中。

 

  下面看一下服務啟動函數InitService,因為InitService可能被多個設備所調用,因此通過全局變量g_isServiceInit來確認服務是否啟動,如果其他設備以及啟動則直接略過。如果沒有,則需要做很多初始化工作。

 

  服務啟動函數InitService首先調用InitCommonManager,InitCommonManager再調用InitLocalDeviceInfo初始化本地設備信息。本地設備的信息被保存在g_deviceInfo這個全局變量中。接下來,服務啟動函數InitService初始化了兩個全局變量,g_publishModule和g_capabilityData,分別用來存放發布的模塊和能力描述數據。之后,調用CoapInit函數,CoapInit則會調用NSTACKX_Init,NSTACKX_Init調用CoapInitDiscovery來啟動COAP端口監聽。 

  Discovery 是一種基於coap 通信協議的設備發現機制;Coap(Constrained Application Protocol)是一種可以使用在資源受限的物聯網設備上,並支持可靠傳輸的輕量化類web協議。它詳細規范定義在 RFC 7252, coap 協議支持IP多播, 即可以同時向多個設備發送請求,鴻蒙OS的設備發現功能也是基於這個特性;用戶使用discovery功能時,需要保證發現端設備與被發現端設備在同一個局域網內,並且都能收到對方coap協議報文;目前discovery服務僅支持基於Wi-Fi通信方式的設備發現機制。

  Discovery的目錄結構如下所示:

   Discovery 對外提供PublishService() 接口來實現設備的發現功能,其函數實現解讀如下:

 

  PublishService主要的代碼流程圖如下

  被發現端主要是通過PublishService()這個函數發布服務。PublishService()函數的實現在discovery_service.c文件中,我們來看看這個函數的主流程代碼; 

  函數參數有三個:

    moduleName:調用者的模塊名稱

    info:PublishInfo結構體,發布的信息

    cb:發布成功或者失敗的回調函數

  在函數實現中,我們可以看到權限檢查,參數檢驗,信號量創建之類等代碼;我們從初始化服務 InitService()函數看,

  InitCommonManager() 函數主要是調用InitLocalDeviceInfo()給g_deviceInfo結構體初始化;

  RegisterWifiCallback(WifiEventTrigger)函數將WifiEventTrigger(unsigned int para)函數賦值給全局變量g_wifiCallback,最主要看CoapInit()函數

  這里面我們優先分析下CoapInitSocket() 和 CreateCoapListenThread()

  CoapInitSocket()函數實現如下:

  可以看到CoapInitSocket()函數里面其實就是調用了socket()函數創建了socket

  CreateCoapListenThread() 創建線程接收消息,函數實現如下:

 

  CoapReadHandle 接收並處理收到的消息

 

  HandleReadEvent函數實現如下,我們分別看看CoapSocketRecv()、COAP_SoftBusDecode()、PostServiceDiscover()函數; 

  CoapSocketRecv()實現就是調用recvfrom()接收消息。

  收到消息放到recvBuffer里面  然后調用COAP_SoftBusDecode()解碼收到的消息。解碼之后放到decodePacket里面然后調用PostServiceDiscover()函數對接收到的消息進行回應。

  PostServiceDiscover()函數代碼如下:

 

  其中GetServiceDiscoverInfo(),這個函數可以獲取到對端的ip 和remoteUrl。

 

 

  這里可以獲取到設備信息,也就是deviceInfo 結構體成員如下:

  獲取到這些信息之后我們就可以調用CoapResponseService()函數回復消息了。這里看一下主要的回復消息流程。

 

  調用socket()創建socket 並將socket描述符返回跟全局變量g_clientFd,以便后面函數GetCoapClientSocket()獲取socket描述符。

 

  調用sendto發送消息。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM