Penemuan layanan jaringan (NSD) memberi aplikasi Anda akses ke layanan yang yang disediakan perangkat di jaringan lokal. Perangkat yang mendukung NSD termasuk {i>printer<i}, webcam, server HTTPS, dan perangkat seluler lainnya.
NSD menerapkan mekanisme Penemuan Layanan berbasis DNS (DNS-SD), yang memungkinkan aplikasi Anda meminta layanan dengan menentukan jenis layanan dan namanya dari {i>instance<i} perangkat yang menyediakan jenis layanan yang diinginkan. DNS-SD adalah didukung di Android dan platform seluler lainnya.
Menambahkan NSD ke aplikasi memungkinkan pengguna mengidentifikasi perangkat lain di jaringan lokal yang mendukung layanan yang diminta aplikasi Anda. Hal ini berguna untuk berbagai aplikasi peer-to-peer seperti berbagi file atau multi-player bermain game. NSD API Android menyederhanakan upaya yang diperlukan untuk menerapkan fitur-fitur tersebut.
Pelajaran ini menunjukkan cara membangun aplikasi yang dapat menyiarkan nama dan informasi koneksi ke jaringan lokal dan memindai informasi dari aplikasi lain melakukan hal yang sama. Akhirnya, pelajaran ini menunjukkan cara menghubungkan ke aplikasi yang sama yang berjalan di perangkat lain.
Mendaftarkan layanan di jaringan
Catatan: Langkah ini bersifat opsional. Jika Anda tidak perlu menyiarkan layanan aplikasi Anda melalui jaringan lokal, Anda dapat langsung menuju ke bagian berikutnya, Menemukan Layanan di Jaringan.
Untuk mendaftarkan layanan Anda di jaringan lokal, terlebih dahulu buat objek NsdServiceInfo
. Objek ini memberikan informasi
yang digunakan oleh perangkat lain di jaringan tersebut
ketika mereka memutuskan apakah ingin terhubung ke
layanan.
Kotlin
fun registerService(port: Int) { // Create the NsdServiceInfo object, and populate it. val serviceInfo = NsdServiceInfo().apply { // The name is subject to change based on conflicts // with other services advertised on the same network. serviceName = "NsdChat" serviceType = "_nsdchat._tcp" setPort(port) ... } }
Java
public void registerService(int port) { // Create the NsdServiceInfo object, and populate it. NsdServiceInfo serviceInfo = new NsdServiceInfo(); // The name is subject to change based on conflicts // with other services advertised on the same network. serviceInfo.setServiceName("NsdChat"); serviceInfo.setServiceType("_nsdchat._tcp"); serviceInfo.setPort(port); ... }
Cuplikan kode tersebut menetapkan nama layanan ke "NsdChat". Nama layanan adalah nama instance: nama tersebut dapat dilihat oleh perangkat lain di jaringan. Nama terlihat oleh perangkat apa pun di jaringan yang menggunakan NSD untuk mencari dan layanan lokal. Perhatikan bahwa nama harus unik untuk setiap layanan pada jaringan, dan Android akan secara otomatis menangani resolusi konflik. Jika dua perangkat di jaringan keduanya telah menginstal aplikasi NsdChat, salah satu mereka mengubah nama layanan secara otomatis, menjadi sesuatu seperti "NsdChat (1)".
Parameter kedua menetapkan jenis layanan, menentukan protokol dan transport yang digunakan aplikasi. {i>Syntax<i}-nya adalah "_<protokol>._<transportlayer>". Di kolom cuplikan kode, layanan ini menggunakan protokol HTTP yang berjalan melalui TCP. Aplikasi menawarkan layanan {i>printer <i}(misalnya, {i>printer<i} jaringan) akan mengatur ke "_ipp._tcp".
Catatan: International Assigned Numbers Otoritas (IANA) mengelola daftar otoritatif jenis layanan yang digunakan oleh protokol penemuan layanan seperti NSD dan Bonjour. Anda dapat mendownload daftar tersebut dari Daftar nama layanan dan nomor port IANA. Jika Anda ingin menggunakan jenis layanan baru, Anda harus memesannya dengan mengisi Porta dan Layanan IANA formulir pendaftaran.
Ketika menyetel porta untuk layanan Anda, hindari melakukan hardcode konflik dengan aplikasi lain. Misalnya, dengan asumsi aplikasi Anda selalu menggunakan porta 1337 sehingga memiliki potensi konflik dengan aplikasi terinstal lainnya yang menggunakan porta yang sama. Sebagai gantinya, gunakan porta berikutnya yang tersedia. Karena informasi ini diberikan ke aplikasi lain oleh siaran layanan, tidak perlu porta yang digunakan aplikasi Anda untuk dikenal oleh aplikasi lain pada waktu kompilasi. Sebagai gantinya, aplikasi bisa mendapatkan informasi ini dari siaran layanan Anda, tepat sebelum menyambungkan ke layanan.
Jika Anda bekerja dengan soket, berikut ini cara Anda menginisialisasi soket ke porta yang tersedia hanya dengan menyetelnya ke 0.
Kotlin
fun initializeServerSocket() { // Initialize a server socket on the next available port. serverSocket = ServerSocket(0).also { socket -> // Store the chosen port. mLocalPort = socket.localPort ... } }
Java
public void initializeServerSocket() { // Initialize a server socket on the next available port. serverSocket = new ServerSocket(0); // Store the chosen port. localPort = serverSocket.getLocalPort(); ... }
Setelah menentukan objek NsdServiceInfo
, Anda harus mengimplementasikan antarmuka RegistrationListener
. Ini
berisi callback yang digunakan oleh Android untuk memperingatkan aplikasi Anda tentang
keberhasilan atau kegagalan pendaftaran
dan pembatalan pendaftaran layanan.
Kotlin
private val registrationListener = object : NsdManager.RegistrationListener { override fun onServiceRegistered(NsdServiceInfo: NsdServiceInfo) { // Save the service name. Android may have changed it in order to // resolve a conflict, so update the name you initially requested // with the name Android actually used. mServiceName = NsdServiceInfo.serviceName } override fun onRegistrationFailed(serviceInfo: NsdServiceInfo, errorCode: Int) { // Registration failed! Put debugging code here to determine why. } override fun onServiceUnregistered(arg0: NsdServiceInfo) { // Service has been unregistered. This only happens when you call // NsdManager.unregisterService() and pass in this listener. } override fun onUnregistrationFailed(serviceInfo: NsdServiceInfo, errorCode: Int) { // Unregistration failed. Put debugging code here to determine why. } }
Java
public void initializeRegistrationListener() { registrationListener = new NsdManager.RegistrationListener() { @Override public void onServiceRegistered(NsdServiceInfo NsdServiceInfo) { // Save the service name. Android may have changed it in order to // resolve a conflict, so update the name you initially requested // with the name Android actually used. serviceName = NsdServiceInfo.getServiceName(); } @Override public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { // Registration failed! Put debugging code here to determine why. } @Override public void onServiceUnregistered(NsdServiceInfo arg0) { // Service has been unregistered. This only happens when you call // NsdManager.unregisterService() and pass in this listener. } @Override public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) { // Unregistration failed. Put debugging code here to determine why. } }; }
Sekarang Anda memiliki semua komponen untuk mendaftarkan layanan Anda. Memanggil metode
registerService()
.
Perhatikan, metode ini asinkron, jadi setiap kode yang perlu dijalankan
setelah layanan didaftarkan harus menggunakan metode onServiceRegistered()
.
Kotlin
fun registerService(port: Int) { // Create the NsdServiceInfo object, and populate it. val serviceInfo = NsdServiceInfo().apply { // The name is subject to change based on conflicts // with other services advertised on the same network. serviceName = "NsdChat" serviceType = "_nsdchat._tcp" setPort(port) } nsdManager = (getSystemService(Context.NSD_SERVICE) as NsdManager).apply { registerService(serviceInfo, NsdManager.PROTOCOL_DNS_SD, registrationListener) } }
Java
public void registerService(int port) { NsdServiceInfo serviceInfo = new NsdServiceInfo(); serviceInfo.setServiceName("NsdChat"); serviceInfo.setServiceType("_http._tcp."); serviceInfo.setPort(port); nsdManager = Context.getSystemService(Context.NSD_SERVICE); nsdManager.registerService( serviceInfo, NsdManager.PROTOCOL_DNS_SD, registrationListener); }
Menemukan layanan pada jaringan
Jaringan ini penuh dengan kehidupan, dari {i> printer<i} jaringan yang mengerikan hingga webcam jaringan yang berbahaya, ke pertempuran brutal dan sengit dari aksi tic-tac-toe terdekat pemain. Kunci untuk membuat aplikasi Anda melihat ekosistem yang dinamis fungsionalitasnya adalah menemukan layanan. Aplikasi Anda perlu memproses layanan {i>broadcast <i}di jaringan untuk melihat layanan apa yang tersedia, dan menyaring apa pun yang tidak dapat dikerjakan aplikasi.
Penemuan layanan, seperti pendaftaran layanan, memiliki dua langkah:
menyiapkan pemroses penemuan dengan callback yang relevan, dan membuat
Panggilan API ke discoverServices()
.
Pertama, buat instance dari class anonim yang mengimplementasikan NsdManager.DiscoveryListener
. Cuplikan berikut menunjukkan
contoh sederhana:
Kotlin
// Instantiate a new DiscoveryListener private val discoveryListener = object : NsdManager.DiscoveryListener { // Called as soon as service discovery begins. override fun onDiscoveryStarted(regType: String) { Log.d(TAG, "Service discovery started") } override fun onServiceFound(service: NsdServiceInfo) { // A service was found! Do something with it. Log.d(TAG, "Service discovery success$service") when { service.serviceType != SERVICE_TYPE -> // Service type is the string containing the protocol and // transport layer for this service. Log.d(TAG, "Unknown Service Type: ${service.serviceType}") service.serviceName == mServiceName -> // The name of the service tells the user what they'd be // connecting to. It could be "Bob's Chat App". Log.d(TAG, "Same machine: $mServiceName") service.serviceName.contains("NsdChat") -> nsdManager.resolveService(service, resolveListener) } } override fun onServiceLost(service: NsdServiceInfo) { // When the network service is no longer available. // Internal bookkeeping code goes here. Log.e(TAG, "service lost: $service") } override fun onDiscoveryStopped(serviceType: String) { Log.i(TAG, "Discovery stopped: $serviceType") } override fun onStartDiscoveryFailed(serviceType: String, errorCode: Int) { Log.e(TAG, "Discovery failed: Error code:$errorCode") nsdManager.stopServiceDiscovery(this) } override fun onStopDiscoveryFailed(serviceType: String, errorCode: Int) { Log.e(TAG, "Discovery failed: Error code:$errorCode") nsdManager.stopServiceDiscovery(this) } }
Java
public void initializeDiscoveryListener() { // Instantiate a new DiscoveryListener discoveryListener = new NsdManager.DiscoveryListener() { // Called as soon as service discovery begins. @Override public void onDiscoveryStarted(String regType) { Log.d(TAG, "Service discovery started"); } @Override public void onServiceFound(NsdServiceInfo service) { // A service was found! Do something with it. Log.d(TAG, "Service discovery success" + service); if (!service.getServiceType().equals(SERVICE_TYPE)) { // Service type is the string containing the protocol and // transport layer for this service. Log.d(TAG, "Unknown Service Type: " + service.getServiceType()); } else if (service.getServiceName().equals(serviceName)) { // The name of the service tells the user what they'd be // connecting to. It could be "Bob's Chat App". Log.d(TAG, "Same machine: " + serviceName); } else if (service.getServiceName().contains("NsdChat")){ nsdManager.resolveService(service, resolveListener); } } @Override public void onServiceLost(NsdServiceInfo service) { // When the network service is no longer available. // Internal bookkeeping code goes here. 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); nsdManager.stopServiceDiscovery(this); } @Override public void onStopDiscoveryFailed(String serviceType, int errorCode) { Log.e(TAG, "Discovery failed: Error code:" + errorCode); nsdManager.stopServiceDiscovery(this); } }; }
NSD API menggunakan metode dalam antarmuka ini untuk memberi tahu aplikasi Anda saat penemuan dimulai, saat layanan gagal, dan saat layanan ditemukan dan hilang (hilang berarti " tidak lagi tersedia"). Perhatikan bahwa cuplikan ini melakukan beberapa pemeriksaan saat layanan ditemukan.
- Nama layanan dari layanan yang ditemukan dibandingkan dengan layanan tersebut nama layanan lokal untuk menentukan apakah perangkat baru saja mengambilnya sendiri {i>broadcast<i} (yang valid).
- Jenis layanan diperiksa, untuk memverifikasi bahwa itu adalah jenis layanan terhubung dengan aplikasi Anda.
- Nama layanan diperiksa untuk memverifikasi koneksi ke alamat aplikasi.
Memeriksa nama layanan tidak selalu diperlukan, dan hanya relevan jika Anda ingin terhubung ke aplikasi tertentu. Misalnya, aplikasi mungkin hanya ingin terhubung ke {i>instance<i} yang berjalan di perangkat lain. Namun, jika aplikasi ingin terhubung ke {i>printer<i} jaringan, cukup untuk melihat bahwa jenis layanan adalah "_ipp._tcp".
Setelah menyiapkan pemroses, panggil discoverServices()
dengan meneruskan jenis layanan
yang harus dicari aplikasi Anda, protokol penemuan
yang digunakan, dan
pemroses yang baru saja Anda buat.
Kotlin
nsdManager.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, discoveryListener)
Java
nsdManager.discoverServices( SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, discoveryListener);
Menghubungkan ke layanan pada jaringan
Bila aplikasi Anda menemukan layanan pada jaringan yang akan disambungkan,
harus terlebih dahulu menentukan informasi koneksi untuk layanan tersebut, dengan menggunakan
Metode resolveService()
.
Implementasikan NsdManager.ResolveListener
untuk diteruskan ke class ini
, dan gunakan untuk mendapatkan NsdServiceInfo
yang berisi
informasi koneksi.
Kotlin
private val resolveListener = object : NsdManager.ResolveListener { override fun onResolveFailed(serviceInfo: NsdServiceInfo, errorCode: Int) { // Called when the resolve fails. Use the error code to debug. Log.e(TAG, "Resolve failed: $errorCode") } override fun onServiceResolved(serviceInfo: NsdServiceInfo) { Log.e(TAG, "Resolve Succeeded. $serviceInfo") if (serviceInfo.serviceName == mServiceName) { Log.d(TAG, "Same IP.") return } mService = serviceInfo val port: Int = serviceInfo.port val host: InetAddress = serviceInfo.host } }
Java
public void initializeResolveListener() { resolveListener = new NsdManager.ResolveListener() { @Override public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) { // Called when the resolve fails. Use the error code to debug. Log.e(TAG, "Resolve failed: " + errorCode); } @Override public void onServiceResolved(NsdServiceInfo serviceInfo) { Log.e(TAG, "Resolve Succeeded. " + serviceInfo); if (serviceInfo.getServiceName().equals(serviceName)) { Log.d(TAG, "Same IP."); return; } mService = serviceInfo; int port = mService.getPort(); InetAddress host = mService.getHost(); } }; }
Setelah layanan diselesaikan, aplikasi Anda akan menerima informasi layanan Anda, termasuk alamat IP dan nomor porta. Ini semua Anda harus membuat koneksi jaringan Anda sendiri ke layanan.
Membatalkan pendaftaran layanan dengan penutupan aplikasi
NSD harus mengaktifkan dan menonaktifkan NSD fungsionalitasnya yang sesuai selama siklus proses. Membatalkan pendaftaran aplikasi Anda saat ditutup membantu mencegah aplikasi lain agar komputer masih aktif dan mencoba untuk terhubung ke anotasi. Selain itu, penemuan layanan merupakan operasi yang mahal, dan harus dihentikan saat Aktivitas induk dijeda, dan diaktifkan kembali saat Aktivitas dilanjutkan. Ganti metode siklus proses Aktivitas utama Anda dan sisipkan kode untuk memulai dan menghentikan siaran dan penemuan layanan yang sesuai.
Kotlin
// In your application's Activity override fun onPause() { nsdHelper?.tearDown() super.onPause() } override fun onResume() { super.onResume() nsdHelper?.apply { registerService(connection.localPort) discoverServices() } } override fun onDestroy() { nsdHelper?.tearDown() connection.tearDown() super.onDestroy() } // NsdHelper's tearDown method fun tearDown() { nsdManager.apply { unregisterService(registrationListener) stopServiceDiscovery(discoveryListener) } }
Java
// In your application's Activity @Override protected void onPause() { if (nsdHelper != null) { nsdHelper.tearDown(); } super.onPause(); } @Override protected void onResume() { super.onResume(); if (nsdHelper != null) { nsdHelper.registerService(connection.getLocalPort()); nsdHelper.discoverServices(); } } @Override protected void onDestroy() { nsdHelper.tearDown(); connection.tearDown(); super.onDestroy(); } // NsdHelper's tearDown method public void tearDown() { nsdManager.unregisterService(registrationListener); nsdManager.stopServiceDiscovery(discoveryListener); }