Android 附近设备Mdns

250 阅读2分钟

Mdns 扫描这么用?

NsdManager、NsdServiceInfo

NsdManager:The Network Service Discovery Manager

aospxref.com/android-11.…

NsdServiceInfo

aospxref.com/android-11.…

package moe.shizuku.manager.adb

//import androidx.lifecycle.MutableLiveData
import android.content.Context
import android.net.nsd.NsdManager
import android.net.nsd.NsdServiceInfo
import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi
import java.io.IOException
import java.net.InetAddress
import java.net.InetSocketAddress
import java.net.ServerSocket

@RequiresApi(Build.VERSION_CODES.R)
class AdbMdns(
    context: Context, private val serviceType: String,
    private val port: PortListener
) {
    private var registered = false
    var running = false
    private var serviceName: String? = null
    private val listener: DiscoveryListener

    private val nsdManager: NsdManager = context.getSystemService(NsdManager::class.java)

    var serviceType= "_http._tcp" //打印机_ipp._tcp
    
    fun start() {
        running = true
        if (!registered) {
            nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD, listener)
        }
    }

    fun stop() {
        running = false
        if (registered) {
            nsdManager.stopServiceDiscovery(listener)
        }
    }

    private fun onDiscoveryStart() {
        registered = true
    }

    private fun onDiscoveryStop() {
        registered = false
    }

    private fun onServiceFound(info: NsdServiceInfo) {
        nsdManager.resolveService(info, ResolveListener(this))
    }

    private fun onServiceLost(info: NsdServiceInfo) {
        if (info.serviceName == serviceName)
            port.postValue(info.host, -1, false)
    }

    private fun onStartDiscoveryFailed(str: String,i: Int){
        Log.e("my_tag", "onStartDiscoveryFailed :$str :$i")

    }    private fun onResolveFailed(resolvedService: NsdServiceInfo,i: Int){
        Log.e("my_tag", "onResolveFailed :$resolvedService :$i")

    }
    private fun onServiceResolved(resolvedService: NsdServiceInfo) {
        Log.e("my_tag", "resolvedService:$resolvedService")
        //todo 11.2 3:17
            port.postValue(
                resolvedService.host,
                resolvedService.port,
                isPortAvailable(resolvedService.port)
            )
    }


    private fun isPortAvailable(port: Int) = try {
        ServerSocket().use {
            it.bind(InetSocketAddress(getAddress(), port), 1)//10.19
            false
        }
    } catch (e: IOException) {
        true
    }

    internal class DiscoveryListener(private val adbMdns: AdbMdns) : NsdManager.DiscoveryListener {
        override fun onDiscoveryStarted(str: String) {
            adbMdns.onDiscoveryStart()
        }

        override fun onStartDiscoveryFailed(str: String, i: Int) {
            adbMdns.onStartDiscoveryFailed(str,i)

        }

        override fun onDiscoveryStopped(str: String) {
            adbMdns.onDiscoveryStop()
        }

        override fun onStopDiscoveryFailed(str: String, i: Int) {}

        override fun onServiceFound(nsdServiceInfo: NsdServiceInfo) {
            adbMdns.onServiceFound(nsdServiceInfo)
        }

        override fun onServiceLost(nsdServiceInfo: NsdServiceInfo) {
            adbMdns.onServiceLost(nsdServiceInfo)
        }
    }

    internal class ResolveListener(private val adbMdns: AdbMdns) : NsdManager.ResolveListener {
        override fun onResolveFailed(nsdServiceInfo: NsdServiceInfo, i: Int) {
            adbMdns.onResolveFailed(nsdServiceInfo,i)
        }

        override fun onServiceResolved(nsdServiceInfo: NsdServiceInfo) {
            adbMdns.onServiceResolved(nsdServiceInfo)
        }

    }

    init {
        listener = DiscoveryListener(this)
    }
}
NsdManager 构造方法
      /**
290       * Create a new Nsd instance. Applications use
291       * { @link android.content.Context#getSystemService Context.getSystemService()} to retrieve
292       * { @link android.content.Context#NSD_SERVICE Context.NSD_SERVICE}.
293       * @param service the Binder interface
294       * @hide - hide this because it takes in a parameter of type INsdManager, which
295       * is a system private class.
296       */
297      public NsdManager(Context context, INsdManager service) {
298          mService = service;
299          mContext = context;
300          init();
301      }
init()
  /**
507       * Initialize AsyncChannel
508       */
509      private void init() {
510          final Messenger messenger = getMessenger();
511          if (messenger == null) {
512              fatal("Failed to obtain service Messenger");
513          }
514          HandlerThread t = new HandlerThread("NsdManager");
515          t.start();
516          mHandler = new ServiceHandler(t.getLooper());
517          mAsyncChannel.connect(mContext, mHandler, messenger);
518          try {
519              mConnected.await();
520          } catch (InterruptedException e) {
521              fatal("Interrupted wait at init");
522          }
523          if (CompatChanges.isChangeEnabled(RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)) {
524              return;
525          }
526          // Only proactively start the daemon if the target SDK < S, otherwise the internal service
527          // would automatically start/stop the native daemon as needed.
528          mAsyncChannel.sendMessage(DAEMON_STARTUP);
529      }
nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD, listener)
onServiceFound回调里nsdManager.resolveService(info, ResolveListener(this))
private fun onServiceFound(info: NsdServiceInfo) { 
nsdManager.resolveService(info, ResolveListener(this)) 
}
再回调onServiceResolved(NsdServiceInfo nsdServiceInfo)

resolvedService.host, resolvedService.port,

建立tcp socket 连接是否能可用

private fun isPortAvailable(port: Int) = try { 
ServerSocket().use { it.bind(InetSocketAddress(getAddress(), port), 1)
//10.19 false }
} catch (e: IOException) { true }


// 解析成功,可以建立连接

InetAddress inetAddress = serviceInfo.getHost();

int port = serviceInfo.getPort();
try {

Socket socket = new Socket(inetAddress.getHostAddress(), port);

// 连接建立后的逻辑处理

} catch (IOException e) {

e.printStackTrace();

// 处理连接异常

}