这些日子碰到这样一个需求,需要在局域网中发现我们创建的某个特定设备,并连上互相通信。本以为要去遍历整个局域网,后来查阅相关资料后发现 Android 为我们提供了网络服务发现(Network Service Discovery)简称 NSD,就是本文要讲的东西了。

NSD 有什么用

用来扫描局域网中特定的服务,并与之通信。省去了自己去写发现协议。

NSD 怎么用

创建服务

首先要创建一个 ServerSocket,用来等待别人的连接

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
/**
 *创建一个 server 来获取端口
 */
private void createServerSocket() {
  if (mServerSocket != null) return;
  try {
    mServerSocket = new ServerSocket(0);//设为 0,会自动获取没有占用的端口
    mPort = mServerSocket.getLocalPort();
  } catch (IOException e) {
    e.printStackTrace();
  }
}

接下来就可以创建 NsdServiceInfo 了,NsdServiceInfo 是一个包含 Nsd 服务信息的类,看名字就知道了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/**
 *创建 NsdServiceInfo
 */
private void createNsdServiceInfo() {
  if (mNsdServiceInfo != null) return;
  mNsdServiceInfo = new NsdServiceInfo();
  mNsdServiceInfo.setServiceName(SERVICE_NAME);//设置服务的名字,被别的机器发现时显示的名字。
  mNsdServiceInfo.setServiceType(SERVICE_TYPE);
  mNsdServiceInfo.setPort(mPort);
}

注册 NSD 服务

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private void register() {
    mNsdManager = (NsdManager) getSystemService(NSD_SERVICE);
    mNsdManager.registerService(mNsdServiceInfo, NsdManager.PROTOCOL_DNS_SD,mRegistrationListener);
}
private void createRegistration() {
    mRegistrationListener = new NsdManager.RegistrationListener() {
        @Override
        public void onRegistrationFailed(NsdServiceInfo nsdServiceInfo, int i){
            Toast.makeText(MainActivity.this, "onRegistrationFailed", Toast.LENGTH_SHORT).show();
        }
        @Override
        public void onUnregistrationFailed(NsdServiceInfo nsdServiceInfo, int i) {
            Toast.makeText(MainActivity.this, "onUnregistrationFailed", Toast.LENGTH_SHORT).show();
        }
        @Override
        public void onServiceRegistered(NsdServiceInfo nsdServiceInfo) {
            Toast.makeText(MainActivity.this, "onServiceRegistered", Toast.LENGTH_SHORT).show();
            new Thread(MainActivity.this).start();
        }
        @Override
        public void onServiceUnregistered(NsdServiceInfo nsdServiceInfo) {
            Toast.makeText(MainActivity.this, "onServiceUnregistered", Toast.LENGTH_SHORT).show();
        }
    };
}

在客户端发现 NSD 服务

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
nsdManager.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener);
private void createDiscoverListener() {
    mDiscoveryListener = new NsdManager.DiscoveryListener() {
        @Override
        public void onStartDiscoveryFailed(String s, int i) {
            Toast.makeText(MainActivity.this, "onStartDiscoveryFailed", Toast.LENGTH_SHORT).show();
        }
        @Override
        public void onStopDiscoveryFailed(String s, int i) {
            Toast.makeText(MainActivity.this, "onStopDiscoveryFailed", Toast.LENGTH_SHORT).show();
        }
        @Override
        public void onDiscoveryStarted(String s) {
            Toast.makeText(MainActivity.this, "onDiscoveryStarted", Toast.LENGTH_SHORT).show();
        }
        @Override
        public void onDiscoveryStopped(String s) {
            Toast.makeText(MainActivity.this, "onDiscoveryStopped", Toast.LENGTH_SHORT).show();
        }
        @Override
        public void onServiceFound(NsdServiceInfo nsdServiceInfo) {
            //这里的 nsdServiceInfo 只能获取到名字,ip 和端口都不能获取到,要想获取到需要调用 NsdManager.resolveService 方法
            datas.add(nsdServiceInfo);
            mHandler.sendEmptyMessage(0);
        }
        @Override
        public void onServiceLost(NsdServiceInfo nsdServiceInfo) {
            Toast.makeText(MainActivity.this, "onServiceLost", Toast.LENGTH_SHORT).show();
        }
    };
}

获取服务的详细信息

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
nsdManager.resolveService(nsdServiceInfo, mResolverListener);
private void createResolverListener() {
  mResolverListener = new NsdManager.ResolveListener() {
      @Override
      public void onResolveFailed(NsdServiceInfo nsdServiceInfo, int i) {
        Toast.makeText(MainActivity.this, "onResolveFailed", Toast.LENGTH_SHORT).show();
      }
      @Override
      public void onServiceResolved(NsdServiceInfo nsdServiceInfo) {
        mNsdServiceInfo = nsdServiceInfo;
        new Thread(MainActivity.this).start();
      }
    };
}

连接

1
2
3
4
mSocket = new Socket(mNsdServiceInfo.getHost().getHostAddress(), mNsdServiceInfo.getPort());
bufferedWriter = new BufferedWriter(new OutputStreamWriter(mSocket.getOutputStream()));
bufferedWriter.write("我连上你了!" + "\n");
bufferedWriter.flush();

效果图

NSD

相关资料链接