很高興能在農曆蛇年剛開始的這期《程序員》雜誌上繼續爲讀者奉上Android的故事。初來咋到,首先要向你們說聲」你好「。有意思的是,Android也很通人情,從4.1開始,它會說」Bonjour「了。不過它說得是否是原汁原味的法語腔呢?來看下文。 html
Bonjour是法語中的Hello之意。它是Apple公司爲基於組播域名服務(multicast DNS)的開放性零配置網絡標準所起的名字。使用Bonjour的設備在網絡中自動組播它們本身的服務信息並監聽其它設備的服務信息。設備之間就像在打招呼,這也是該技術命名爲Bonjour的緣由。Bonjour使得局域網中的系統和服務即便在沒有網絡管理員的狀況下也很容易被找到。 android
舉一個簡單的例子:在局域網中,若是要進行打印服務,必須先知道打印服務器的IP地址。此IP地址通常由IT部門的人負責分配,而後他還得全員發郵件以公示此地址。有了Bonjour之後,打印服務器本身會依據零配置網絡標準在局域網內部找到一個可用的IP並註冊一個打印服務,名爲「print service」之類的。當客戶端須要打印服務時,會先搜索網絡內部的打印服務器。因爲不知道打印服務器的IP地址,客戶端只能根據諸如"print service"的名字去查找打印機。在Bonjour的幫助下,客戶端最終能找到這臺註冊了「print service」名字的打印機,並得到它的IP地址以及端口號。 程序員
從Bonjour角度來看,該技術主要解決了三個問題: 編程
Bonjour技術在Mac OS以及Itunes、Iphone上都獲得了普遍應用。爲了進一步推廣,Apple經過開源工程mdnsresponder將其開源出來。在Windows平臺上,它將生成一個後臺程序mdnsresponder。在Android平臺上(或者說支持POSIX的Linux平臺)它是一個名爲mdnsd的程序。不過,不管是mdnsresponder仍是mdnsd,應用開發者要作的僅僅是利用Bonjour的API向它們發起服務註冊、服務查詢和服務解析等請求並接收來自它們的處理結果。 服務器
下面咱們將介紹Bonjour API中使用最多的三個函數,它們分別是服務註冊、服務查詢和服務解析。理解這三個函數的功能也是理解MDnsSdListener的基礎。 網絡
使用Bonjour API必須包含以下的頭文件和動態庫,並鏈接到: 架構
#include <dns_sd.h> //必須包含此頭文件 app
libmdnssd.so //連接到此so less
Bonjour中,服務註冊的API爲DNSServiceRegister,原型如圖1所示: dom
圖1 DNSServiceRegister原型
該函數的解釋以下:
當客戶端須要搜索網絡內部特定服務時,須要使用DNSServiceBrowser API,其原型如圖2所示:
圖2 DNSServiceBrowser原型
其中:
當客戶端想得到指定服務的IP和端口號時,須要使用DNSServiceResolve API,其原型如圖3所示:
圖3 DNSServiceResolve原型
其中:
以上介紹的三個API是Bonjure的核心API。不過Android中的Bonjour會是怎麼個說法呢?
幾乎能確定的是,Bonjour想跑在Android平臺上,還須要一番定製。不過這套定製不是針對mdnsd自己,而是針對Bonjour API的使用。Android平臺的Bonjour架構可由圖4表達:
圖4 Android Bonjour架構
由圖4可知,Android拓展了原有的Bonjour架構,改變以下:
總之,在Android平臺中,應用程序要藉助其餘三個進程(System_process、Netd、mdnsd)才能享受到Bonjour好處。不過,這麼繁雜的進程間通訊會不會影響效率呢?
答案是確定的。但就如Bonjour的本意同樣,它僅是經過打一聲招呼以瞭解網絡內服務是否存在以及一些簡單信息。一旦客戶端經過Bonjour獲取到服務的IP地址和端口後,後續客戶端和服務的交互就屬於私密範疇(即客戶端經過服務的IP地址直接和其創建鏈接)了。從這個角度來看,Android上的這點效率損失實屬無傷大雅。
另外,Android上Bonjour架構的設計對讀者們來講還有一個啓示:若是手機廠商想定製一些功能,最好先對現有Android的架構有充分了解。這樣才能結合本身的需求,將功能模塊合理得集成到Android架構中以更有效得發揮其功用。
下面來看看Android中Bonjour架構中的幾位重要成員。
MDnsSdListener在Android Bonjour架構中扮演着轉換器的角色:
圖5所示爲MDnsSdListener的家族成員示意圖。
圖5 MDnsSdListener家族成員
由圖5可知:
下面將簡單介紹MDnsSdListener的運行過程,其主要工做可分紅三步:
先來看第一步,當MDnsSdListener構造時,會建立一個Monitor對象,代碼如圖6所示:
圖6 Monitor的構造
由圖6可知:
starService將啓動mdnsd,其所使用的方法頗具Android特點,如圖7所示:
圖7 startService代碼示意
圖7中,MDS_SERVICE_NAME宏表明字符串"mdnsd"。瞭解Android的讀者,看完圖7,您能很快知道Android啓動mdnsd的方法嗎?
當NsdService發送註冊服務請求時,Handler的serviceRegister函數將被調用,代碼如圖8所示:
圖8 serviceRegister示意圖
DNSServiceRegister內部將把請求發送給mdnsd去處理,處理的結果經過MDnsSdListenerRegisterCallback返回,該函數最終會經過socket把信息傳遞給NsdService去處理。
MDnsSdListener介紹暫且到此,感興趣的讀者不妨親自看看代碼以加深對Bonjour API用法的理解。
對全部Android App來講,NsdService纔是背後的Boss,其用法(固然,是NsdService客戶端API的封裝類NsdManager的用法)也是在SDK文檔中白紙黑字列出來的。NsdService的內部結構可由圖9表示:
圖9 NsdService內部結構示意圖
圖9列出了NsdService中的幾個重要成員,其中:
因爲篇幅緣由,本文不擬對NsdService展開詳細討論了。接下來,本文將介紹Android SDK中一個關於Nsd API使用的小例子NsdChat。
Android SDK新增了一個NsdChat例子用於向開發者介紹Android平臺中Nsd的使用方法。相關文檔位於http://developer.android.com/training/connect-devices-wirelessly/nsd.html。案例的源碼位於Android4.1源碼根目錄/development/samples/training/NsdChat下。
該例描述了一個簡單的聊天程序,故其命名爲NsdChat。Nsd在此例中的做用就是註冊並搜索網絡內的聊天服務。因此,在本例中有一個NsdChat進程將經過NsdService的registerService函數註冊一個聊天服務。相關代碼如圖10所示:
圖10 NsdChat註冊聊天服務
由圖10可知:
兩人聊天才有意義,因此另一個運行着NsdChat的客戶端進程將搜索網絡內部的」NsdChat「服務,相關代碼如圖11所示:
圖11 尋找「NsdChat」服務
由圖11可知:
注意,Nsd只能根據服務類型進行搜索。當網絡中有多個同屬於一種服務類型(本例中,服務類型是"_http._tcp.")的服務時,應用程序還需根據DiscoveryListener返回的信息進行篩選。這部分代碼如圖12所示:
圖12 NsdChat的DiscoveryListener處理
由圖12可知,discoveryServices的結果經過DiscoveryListener接口類提供的回調函數返回。注意其中onServiceFound函數對同類型服務的篩選處理(值得特別指出的是,Android SDK中並未對此處極易疏忽的地方作任何說明)。
當客戶端成功找到NsdChat服務後,下一步工做就是解析該服務的IP地址和端口號。這是經過NsdManager的resolveService函數(注意圖12中的紅框)來完成的。這個函數的處理結果將經過NsdManager定義的另一個接口類ResolveListener返回。
經過對NsdChat的研究,讀者會發現:
本文對Android中Bonjour的實現進行了一番介紹。Bonjour的原理知識還請讀者閱讀https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/NetServices/Introduction.html#//apple_ref/doc/uid/TP40002445-SW1。該網站是關於Bonjure基礎知識的入口,包含《About Bonjour》、《Bonjour API Architecture》等文檔。
另外,Android中的Bonjour主要是爲了支持Network Service Discovery功能。與其相似的還有UPnP技術中使用的Simple Service Discovery Protocol(SSDP)。相比Bonjour而言,UPnP不只實現了NSD,還在後續客戶端和服務端交互方面支持標準SOAP協議,極大方便了客戶端和服務端的代碼邏輯實現。因此,筆者在此提醒開發者,若是想使用Bonjour技術,要特別注意Nsd只能簡化服務註冊及尋找這一步驟,後續還需重點考慮客戶端和服務端交互的協議及實現。
關於DLNA,讀者可參考筆者的博客 http://blog.csdn.net/innost/article/details/7078539 。