要在 local 網路註冊 service
首先得創建一個 NsdServiceInfo 物件
這物件提供給 local 網路內的其他裝置一些資訊
讓它們決定是否與你的裝置連接
NsdManager mNsdManager; public void registerService(int port) { // 創建 NsdServiceInfo 物件並填充它 NsdServiceInfo serviceInfo = new NsdServiceInfo(); // 若在同一網路中其他裝置也有相同服務名稱,Android會自動把其中一台的 ServiceName 改變避免衝突 // 例如 "NsdChat" 可能會被改名成 "NsdChat(1)" 這樣的名稱 serviceInfo.setServiceName("NsdChat"); // ServiceType 的語法為 "_<protocol>._<transportlayer>" // 所以 uses HTTP protocol running over TCP 寫成 "_http._tcp." serviceInfo.setServiceType("_http._tcp."); serviceInfo.setPort(port); mNsdManager = Context.getSystemService(Context.NSD_SERVICE); //註冊 service mNsdManager.registerService( serviceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener); }
為了避免你的 service 使用的 port 發生衝突,我們要取得一個可用 port
如下例是設定 port 為 0 讓 socket 可以使用任意可用的 port
public void initializeServerSocket() { // 初始化 server socket 於下個可用 port mServerSocket = new ServerSocket(0); // 儲存被選到的 port mLocalPort = mServerSocket.getLocalPort(); ... }
在 registerService 前我們必須實作 RegistrationListener 以接收註冊 service 成功與否的訊息
因為 registerService 是非同步的,所以 service 建立後才要做的事情必須放在 onServiceRegistered 裡
public void initializeRegistrationListener() { mRegistrationListener = new NsdManager.RegistrationListener() { @Override public void onServiceRegistered(NsdServiceInfo NsdServiceInfo) { // 儲存 service name. 因為名稱有可能與原本設定的不同所以這裡要更新為實際名稱 mServiceName = NsdServiceInfo.getServiceName(); } @Override public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { // 註冊失敗! 依錯誤碼進行處理 } @Override public void onServiceUnregistered(NsdServiceInfo arg0) { // Service 反註冊。在我們呼叫 NsdManager.unregisterService() 後才可能被觸發 } @Override public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { // Service 反註冊失敗。依錯誤碼進行處理 } }; }
* Discover Services on the Network
Service discovery 跟 service registration 一樣有兩個步驟:
建立 discovery 的 listener 跟 callback 然後呼叫非同步執行的 discoverServices()
public void initializeDiscoveryListener() { // new 一個 DiscoveryListener 實體 mDiscoveryListener = new NsdManager.DiscoveryListener() { // Discovery 開始時就會被呼叫 @Override public void onDiscoveryStarted(String regType) { Log.d(TAG, "Service discovery started"); } @Override public void onServiceFound(NsdServiceInfo service) { // 發現 Service Log.d(TAG, "Service discovery success" + service); if (!service.getServiceType().equals(SERVICE_TYPE)) { // 還記得嗎?Service type 是包含 protocol 跟 transport layer 的字串 Log.d(TAG, "Unknown Service Type: " + service.getServiceType()); } else if (service.getServiceName().equals(mServiceName)) { // service 名稱 Log.d(TAG, "Same machine: " + mServiceName); } else if (service.getServiceName().contains("NsdChat")){ mNsdManager.resolveService(service, mResolveListener); } } @Override public void onServiceLost(NsdServiceInfo service) { // 當網路失效 Log.e(TAG, "service lost" + service); } @Override public void onDiscoveryStopped(String serviceType) { Log.i(TAG, "Discovery stopped: " + serviceType); } @Override public void onStartDiscoveryFailed(String serviceType, int errorCode) { Log.e(TAG, "Discovery failed: Error code:" + errorCode); mNsdManager.stopServiceDiscovery(this); } @Override public void onStopDiscoveryFailed(String serviceType, int errorCode) { Log.e(TAG, "Discovery failed: Error code:" + errorCode); mNsdManager.stopServiceDiscovery(this); } }; }NSD API 透過 DiscoveryListener 去通知你 discovery 的狀態
注意上例在找到 service 時進行了一些檢查:
1. 找到的 service name 必須與 local 的 service name 比對以確定裝置是否接收到自己的 broadcast
2. service type 會被檢查以驗證你的應用程式可以連接
3. service name 會被檢查以確定連接到正確的應用程式
然而檢查 service name 並不是必要的,例如應用程式可能只想要連接到其他裝置上相同應用程式的實體
(像網路印表機就只需要檢查service type 是 "_ipp._tcp")
設定好 DiscoveryListener 後就可以呼叫 discoverServices()
mNsdManager.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener);
* Connect to Services on the Network
當找到可以連接的網路服務,首先要用 resolveService() 判讀連線資訊
我們必須實作 NsdManager.ResolveListener 接收結果並取得包含連線資訊的 NsdServiceInfo
public void initializeResolveListener() { mResolveListener = new NsdManager.ResolveListener() { @Override public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) { // 解析失敗。使用錯誤碼進行除錯 Log.e(TAG, "Resolve failed" + errorCode); } @Override public void onServiceResolved(NsdServiceInfo serviceInfo) { Log.e(TAG, "Resolve Succeeded. " + serviceInfo); if (serviceInfo.getServiceName().equals(mServiceName)) { Log.d(TAG, "Same IP."); return; } // mService = serviceInfo; int port = mService.getPort(); InetAddress host = mService.getHost(); } }; }
* Unregister Your Service on Application Close
在應用程式的生命週期裡適當的啟用和停用 NSD 功能是很重要的
當應用程式關閉時將 NSD 反註冊有助於防止其他應用程式認為它仍在活躍中並嘗試連接它
而 service discovery 是很耗效能的操作,所以應該在 activity pause 的時候停止、在 resume 時重新啟用
//In your application's Activity @Override protected void onPause() { if (mNsdHelper != null) { mNsdHelper.tearDown(); } super.onPause(); } @Override protected void onResume() { super.onResume(); if (mNsdHelper != null) { mNsdHelper.registerService(mConnection.getLocalPort()); mNsdHelper.discoverServices(); } } @Override protected void onDestroy() { mNsdHelper.tearDown(); mConnection.tearDown(); super.onDestroy(); } // NsdHelper's tearDown method public void tearDown() { mNsdManager.unregisterService(mRegistrationListener); mNsdManager.stopServiceDiscovery(mDiscoveryListener); }
相關資料:
Connecting Devices Wirelessly
YouTube-DevBytes: Network Service Discovery
Using Network Service Discovery
沒有留言:
張貼留言