Android服務之網絡服務發現服務

jopen 11年前發布 | 46K 次閱讀 Android Android開發 移動開發

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

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

                           

             Android服務之網絡服務發現服務               

       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回調。到此整個服務登記流程結束。

      服務發現和服務地址解析流程采用服務登記流程基本相同的流程。在服務發現和服務地址解析后就可以向服務收發數據了。
來自:http://blog.csdn.net/goohong/article/details/8470426

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!