Android服務之網路服務探索服務

來源:互聯網
上載者:User

      

        自android 4.1 開始實現了一個網路服務的探索服務NsdService,其基於蘋果的Bonjour服務發現協議,支援遠程服務的發現和零配置。

        Bonjour協議包括IP地址的自動分配、服務名稱與地址的轉換以及服務的發現三部分內容,ANDROID4.1藉助第三方開源工程mDNSResponder實現了Bonjour協議的服務名稱與地址的轉換以及服務的發現等 Bonjour部分協議的支援。Bonjour協議的服務名稱與地址的轉換以及服務的發現採用的流程和DNS流程近似包括:登記過程、服務發現過程、服務位址解析過程以及建立串連等過程,服務發現採用的協議也和DNS協議相似,不過與DNS協議採用的單播方式不同的是採用了組播方式,因此被稱為mDNS。

                           

                            

                                                                                                  

                                                          

       ANDROID4.2 對網路服務發現的實現架構包括四層:NSD應用用戶端、服務探索服務架構層(對應NsdService)、MDns後台監視層(對應運行在netd本地服務進程的MDnsSdListener類 )以及MDns後台服務(對應mdnsd本地服務進程)。架構的每層作為其上一層的服務端對上一層提供服務,上層通過connect與下層服務建立串連。其中NsdService 和NSD應用用戶端採用JAVA語言實現 ,MDns後台監視採用C++實現,而MDns後台服務為採用C語言的開原始碼。四層分別運行在不同的進程,採用相應的跨進程通訊方式進行互動。

       NsdService處於整個層次的承上啟下層,其通過NsdManager對上層應用用戶端提供調用和回調服務,NsdManager客戶和NsdService服務之間採用AsyncChannel非同步通道進行訊息互動。NsdService服務對下在其NativeDaemonConnector線程對象中使用UNIX SOCKET介面與MDns後台監視層建立跨進程串連,傳輸命令和接收響應,MDns後台監視層的MDnsSdListener對象運行在netd本地服務中。

在MDnsSdListener類中調用mDNSResponder開源工程提供的用戶端樁介面與MDns後台服務建立本地SOCKET通訊,並採用Monitor對象來啟動MDns後台服務,實現MDns後台服務的事件監聽和事件回調處理等工作。MDnsSdListener及Monitor對象與MDns後台服務的互動也是採用UNIX SOCKET機制進行跨進程互動。

MDns後台服務的整個實現代碼及用戶端的樁實現由第三方工程mDNSResponder提供,代碼位於 external目錄下 的mdnsresponder中,包括mDNSCore(包括MDNS核心協議引擎代碼)、mDNSShared多個平台共用的非核心引擎代碼、mDNSPosix  Posix平台相關代碼、Clients包括如何使用後台服務提供的API的用戶端例子代碼等四個目錄,整個工程編譯產生一個mdnsd後台服務和一個MDns監視層使用的庫libmdnssd,而Clients中的代碼產生一個dnssd執行檔案用於測試。

      一個應用為了讓網路上的其它應用發現它需要通過網路聲明自己,即服務登記,這通過調用NsdManager的registerService介面實現。

      下面分步驟描述服務登記流程。

 1、應用通過調用Context.getSystemService(Context.NSD_SERVICE)獲得NsdManager的執行個體。

 在NsdManager的執行個體化過程中對使用到的資源進行執行個體化,包括調用NsdService的getMessenger函數獲得服務的Messenger對象用作用戶端訊息的發送目標,執行個體化和啟動事件處理線程HandlerThread及執行個體化事件接收處理對象ServiceHandler,AsyncChannel對象的執行個體化並且調用AsyncChannel對象的connect函數與目標建立串連。

在NsdService服務接收到串連訊息後,執行個體化一個服務端的AsyncChannel對象,並根據訊息的源和服務端的AsyncChannel對象執行個體化一個ClientInfo對象放入mClients HashMap數組中。

      2、應用調用NsdManager執行個體的registerService介面,registerService介面參數中包含一個NsdServiceInfo參數(指示要登記的服務資訊)、一個protocolType參數(指定協議類型)以及一個監聽對象listener,用來接收響應事件回調。

   在registerService介面中調用putListener函數分別把NsdServiceInfo參數和監聽對象listener儲存到mServiceMap和mListenerMap的映射數組中,並返回數組的索引值key;然後registerService通過NsdManager的AsyncChannel對象向目標發送REGISTER_SERVICE訊息,發送的訊息參數包括putListener函數返回的key以及NsdServiceInfo資訊。

      3、NsdService服務收到REGISTER_SERVICE訊息後,首先根據訊息源從mClients數組中獲得clientInfo對象,然後調用getUniqueId獲得一個UniqueId作為登記請求ID;接著調用服務端的registerService函數,registerService的參數為UniqueId和訊息傳進來的NsdServiceInfo資訊。在registerService函數中調用NativeDaemonConnector對象的execute函數,execute函數的命令參數為”mdnssd”,其它參數包括登記命令名稱標示"register"、登記ID、從NsdServiceInfo中獲得的ServiceName、ServiceType和port等參數。

NativeDaemonConnector對象在NsdService服務執行個體化時執行個體化, NativeDaemonConnector對象執行個體化mSocket參數為"mdns",mCallbacks參數指向NsdService服務內部NativeCallbackReceiver對象。NativeDaemonConnector對象本身是一個派生自Runnable的線程對象,因此其線程函數run也在執行個體化後啟動。

在run函數中首先執行個體化和啟動了一個事件處理線程HandlerThread及其事件處理Handler,接著進入while迴圈調用listenToSocket函數。

listenToSocket首先執行個體化一個本地socket對象,LocalSocket對象的LocalSocketAddress地址的 Socket名稱為已初始化的mSocket,並使用該地址調用connect函數,從init.rc 可以看到名稱為"mdns"的Socket對應的本地服務為netd,因此NativeCallbackReceiver對象與netd服務建立了串連;然後listenToSocket函數調用socket的getInputStream和getOutputStream函數獲得輸入和輸出資料流對象;最後listenToSocket函數進入while迴圈不斷從輸入資料流讀取事件進行分析。解析後的事件發給HandlerThread線程的Handler函數進行處理,在Handler函數中調用mCallbacks的onEvent回呼函數,即NsdService服務內部NativeCallbackReceiver對象的onEvent回呼函數。

      4、在NativeDaemonConnector對象的execute函數中首先根據傳進的參數調用makeCommand函數產生一個字串類型的命令,然後調用本地socket的輸出資料流對象 mOutputStream的write函數來發送命令。

      5、在本地服務netd的進程中調用其MDnsSdListener對象的startListener函數啟動命令的監聽。

MDnsSdListener對象通過FrameworkListener間接派生自SocketListener,在MDnsSdListener對象執行個體化時其成員mSocketName初始化 為"mdns",因此對應的socket通道和NativeCallbackReceiver對象中的socket通道相同。MDnsSdListener執行個體化時還初始化一個Monitor對象和一個FrameworkCommand類型的Handler對象。

Handler對象初始化時其mCommand屬性賦值為"mdnssd",用來和發送來的命令匹配,Handler對象也儲存到FrameworkCommand命令列表對象中mCommands。

Monitor對象執行個體化時調用socketpair函數建立一個Socket組mCtrlSocketPair,還建立一個監聽線程,線程中調用Monitor對象的run函數。

      6、startListener函數首先調用android_get_control_socket函數根據mSocketName名稱獲得其SOCKET fd;然後調用listen函數監聽socket通道;

然後建立一個線程,線上程中執行runListener函數,在runListener函數迴圈調用accept接收用戶端串連。當有用戶端串連後,根據accept返回的socket fd執行個體化一個SocketClient對象儲存到SocketClient對象列表中mClients,並調用onDataAvailable函數。

onDataAvailable函數調用read函數讀取用戶端發送的命令,並調用dispatchCommand函數提交命令。

在dispatchCommand函數中解析命令參數,並與mCommands命令對象列表進行命令匹配,並調用匹配後命令對象的runCommand函數,這裡即調用MDnsSdListener對象中的Handler對象的runCommand函數。

     7、在Handler對象的runCommand函數中進行命令參數的匹配,這裡匹配的是"register",因此在獲得命令參數後調用serviceRegister函數,serviceRegister函數參數包括匹配的SocketClient對象以及命令參數資訊。

    8、在serviceRegister函數中,首先調用mMonitor的allocateServiceRef函數根據請求ID執行個體化一個Element對象放入鏈表中,並返回Element對象的DNSServiceRef指標,DNSServiceRef指向_DNSServiceRef_t結構,其成員包括DNS操作或應答類型,接收訊息回調介面、用戶端回調和上下文、用戶端與服務端串連socket等參數。

然後調用DNSServiceRegister函數,DNSServiceRegister函數用來向本地MDns後台服務發起串連和訊息請求,DNSServiceRegister函數的參數包括allocateServiceRef函數返回的DNSServiceRef指標變數以及serviceRegister傳進來的命令請求參數,以及事件接收回呼函數MDnsSdListenerRegisterCallback。

在DNSServiceRegister函數調用後接著調用mMonitor的startMonitoring函數,參數為請求ID,startMonitoring函數用來準備與伺服器已建立串連的SOCKET監視通道和啟動監視通道的監聽。最後調用SocketClient對象的sendMsg函數向用戶端返回CommandOkay應答訊息。

      9、DNSServiceRegister函數為mDNSResponder開源工程提供的用戶端調用API介面,用來與MDns後台服務建立串連,並向其提交請求。

     在DNSServiceRegister函數中首先通過ConnectToServer函數與MDns後台服務建立串連。

    在ConnectToServer函數首先執行個體和初始化一個_DNSServiceRef_t類型DNSServiceOp變數,然後建立一個本地socket,且建立socket的檔案控制代碼賦值給DNSServiceOp對象的sockfd。   

然後調用connect與MDns後台服務建立串連,最後把執行個體化後的DNSServiceOp對象通過DNSServiceRef參數帶回。

    ConnectToServer函數返回後接著調用create_hdr函數為執行個體化一個ipc_msg_hdr類型的請求訊息,並對請求訊息賦值後連同ConnectToServer函數帶回的DNSServiceRef參數一同傳給deliver_request函數,通過deliver_request函數提交請求。

      10 、在Monitor對象的run函數中迴圈對mPollFds進行poll操作。

在startMonitoring函數通過向mCtrlSocketPair[1]寫入RESCAN命令後,由於mPollFds[0].fd指向mCtrlSocketPair[0],因此mMonitor的run函數在mPollFds[0]通道讀取到RESCAN命令並調用RESCAN函數,在RESCAN函數中根據已建立的與伺服器的串連為mPollFds的其它通道賦值,這些mPollFds通道的檔案控制代碼位賦值為伺服器已建立串連的socket 的控制代碼。

 在服務端的響應事件到來時在這些通道poll到事件,然後調用DNSServiceProcessResult函數,參數為DNSServiceRef。

   11、 在DNSServiceProcessResult函數中讀取響應事件和資料,並調用DNSServiceRef參數的事件回調ProcessReply函數,即對於服務登記請求對應的是MDnsSdListenerRegisterCallback函數。

   在MDnsSdListenerRegisterCallback中向Handler對象的監聽對象的sendBroadcast函數發送ResponseCode::ServiceRegistrationSucceeded應答訊息,Handler對象的監聽對象為MDnsSdListener對象本身,因此這裡調用SocketListener的sendBroadcast函數。

在sendBroadcast函數中遍曆mClients對象的成員對象,並調用其調用sendMsg函數,即調用SocketClient的sendMsg函數。

在sendMsg函數中通過與用戶端(即NsdService服務的NativeDaemonConnector對象)建立的SOCKET向用戶端發送應答訊息。

      12、在NsdService的NativeDaemonConnector對象的listenToSocket函數 收到服務端的應答訊息後,調用NsdService服務內部NativeCallbackReceiver對象的onEvent回呼函數。

在onEvent回呼函數中向NsdService服務的狀態機器發送NsdManager.NATIVE_DAEMON_EVENT事件,假如這時NsdService服務處於EnabledState狀態,狀態機器收到NsdManager.NATIVE_DAEMON_EVENT事件後調用handleNativeEvent函數。 

 handleNativeEvent函數首先根據響應訊息的請求ID從mIdToClientInfoMap中獲得先前用戶端建立串連時儲存的clientInfo對象及從clientInfo對象獲得clientId,然後執行響應事件代碼為NativeResponseCode.SERVICE_REGISTERED的事件處理,事件處理先根據返回的響應事件執行個體化一個NsdServiceInfo對象,然後通過clientInfo中的AsyncChannel對象成員向NsdService服務的用戶端發送NsdManager.REGISTER_SERVICE_SUCCEEDED響應事件。

     13、NsdManager的事件接收對象ServiceHandler接收到NsdManager.REGISTER_SERVICE_SUCCEEDED響應事件,在其handleMessage函數中調用其監聽對象(NSD應用用戶端)的onServiceRegistered回調。到此整個服務登記流程結束。

      服務發現和服務位址解析流程採用服務登記流程基本相同的流程。在服務發現和服務位址解析後就可以向服務收發資料了。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.