android NSD服务详解
一.NSD的基础知识:
NSD全称为: Network Service Discovery.翻译过来的意思就是发现服务器网络的意思。理解的说就是:发现服务器对应的网络信息。
作用为: The addition of Network Service Discovery (NSD) takes this further by allowing an application to seek out a nearby device running services with which it can communicate.
(也就是说使用这个服务就可以搜索附近哪个网络设备设备可以进行通信,一般指的是局域网内连接同一个wifi的信号源).
NSD的作用和意义
NSD只是发现网络,并非直接连接网络,发现网络就是发现某个局域网内可连网络,并且获取到对应设备的IP地址和端口号。
NSD的作用就是为下一步连接网络做准备,比如使用Socket网络连接同一局域网两个手机,并且进行数据通信,这就要知道服务器的ip地址和端口号。
Socket不限于蓝牙交互,通过IP和端口号也是可以进行网络交互的,并且效率也比较快,就是会麻烦一些。
二.关于NSD的开发的过程.
NSD(NsdManager)是Android SDK中自带的类库,可以直接调用编程。
这里我吐槽一下,网上很多示例代码都是比较坑爹的,基本步骤都是说什么注册、监听、处理、注销。。。
他们最大的失误是没有区分服务端和客户端。。。而是将代码混合在一起,刚开始接触的开发者就不知道怎么搞了!
其实NSD和Socket有点类似,都是有Server服务器端和Client客户端
对于Socket:
SocketServer服务器端需要定义端口号,然后进行监听连接
SocketClient客户端需要知道服务器端定义的端口号和服务器的ip地址,然后请求连接
然后在客户端进行发送请求连接,服务器端同意连接,这时Socket连接就建立起来的,就可以相互通信了。
对于NSD:
NSD Server服务器端定义主机名字、端口号,然后进行NSD注册
NSD Client客户端进行扫描,但是只能扫描到包含NSD Server服务器定义的主机名字的NsdServiceInfo对象
然后NSD Client客户端能根据这个NsdServiceInfo对象解析到服务器端的IP地址和它的端口号
到这里我们就应该知道了:NSD是为Socket连接做准备的其中一种手段!
1.NSD Server 服务器端的开发
服务器端很简单的,就一个注册就可以了
具体的步骤有:
(1)进行注册监听
private NsdManager.RegistrationListener mRegistrationListener;
//实例化注册监听器
private void initializeRegistrationListener() {
mRegistrationListener = new NsdManager.RegistrationListener() {
@Override
public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
Log.e(TAG, "NsdServiceInfo onRegistrationFailed");
}
@Override
public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
Log.i(TAG, "onUnregistrationFailed serviceInfo: " + serviceInfo + " ,errorCode:" + errorCode);
}
@Override
public void onServiceRegistered(NsdServiceInfo serviceInfo) {
Log.i(TAG, "onServiceRegistered: " + serviceInfo);
}
@Override
public void onServiceUnregistered(NsdServiceInfo serviceInfo) {
Log.i(TAG, "onServiceUnregistered serviceInfo: " + serviceInfo);
}
};
}
1
2
3
注册监听是为了监听后面的注册服务器是否成功
(2)进行注册
private NsdManager mNsdManager;
//注册服务器端
private void registerService(Context context, String serviceName, int port) {
mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE);
NsdServiceInfo serviceInfo = new NsdServiceInfo();
serviceInfo.setServiceName(serviceName);
serviceInfo.setPort(port);
serviceInfo.setServiceType("_http._tcp.");//客户端发现服务器是需要对应的这个Type字符串
mNsdManager.registerService(serviceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener);
}
11
12
到这里如果客户端马上进行扫描就能看到注册了NSD的服务器网络数据
接着就可以进行Socket连接,并且可以取消掉注册NSD服务器,就是让别人看不到你的NSD服务器
(3)取消注册
public void stopNSDServer() {
mNsdManager.unregisterService(mRegistrationListener);
}
1
2
3
4
如果要一直暴露自己的ip和端口数据,这个NSDServer就不要关闭,开发中根据实际情况来。
反正我是一直没有关闭这个暴露的端口信息,即使Socket连接上了,也没有关闭(隐藏),让它一直暴露着也没什么大问题!
NSD服务器端就是这么简单,网上说的还要扫描,解析的那些都是在客户端来完成的。虽然也是NSD的一些操作方法,但是在服务器端是完全没有用到的。
2.NSD Client 客户端的开发
客户端分为两个步骤:扫描,解析
但是过程分为四步:
(1)注册扫描监听器
//NSD管理器
private NsdManager mNsdManager = null;
private void initData() {
mNsdManager = (NsdManager) getSystemService(Context.NSD_SERVICE);
}
private NsdManager.DiscoveryListener mNSDDiscoveryListener = null; // 搜寻监听器
//注册NSD服务网络的监听,发现NSD网络后会在对应的方法回调
private void initNSDDiscoveryListener() {
mNSDDiscoveryListener = new NsdManager.DiscoveryListener() {
@Override
public void onStartDiscoveryFailed(String serviceType, int errorCode) {
Log.i(TAG, "onStartDiscoveryFailed--> " + serviceType + ":" + errorCode);
}
@Override
public void onStopDiscoveryFailed(String serviceType, int errorCode) {
Log.i(TAG, "onStopDiscoveryFailed--> " + serviceType + ":" + errorCode);
}
@Override
public void onDiscoveryStarted(String serviceType) {
Log.i(TAG, "onDiscoveryStarted--> " + serviceType );
}
@Override
public void onDiscoveryStopped(String serviceType) {
Log.i(TAG, "onDiscoveryStopped--> " + serviceType );
}
@Override
public void onServiceFound(NsdServiceInfo serviceInfo) {//关键的回调方法
//这里的 serviceInfo里面只有NSD服务器的主机名,要解析后才能得到该主机名的其他数据信息
Log.i(TAG, onServiceFound Info: --> " + serviceInfo);
//开始解析数据
}
@Override
public void onServiceLost(NsdServiceInfo serviceInfo) {
Log.i(TAG, "onServiceLost--> " + serviceInfo);
}
};
}
47
(2)注册解析监听器
private NsdManager.ResolveListener mNSDResolveListener = null;// 解析监听器
//注册解析NSD网络的监听 ,解析NSD数据后回调
private void initNSDResolveListener() {
mNSDResolveListener = new NsdManager.ResolveListener() {
@Override
public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
}
@Override
public void onServiceResolved(NsdServiceInfo serviceInfo) {//到这里就是我们要的最终数据信息
Log.i(TAG, "resolution : " + serviceInfo.getServiceName() + " \n host_from_server: " + serviceInfo.getHost() +
"\n port from server: " + serviceInfo.getPort());
String hostAddress = serviceInfo.getHost().getHostAddress();
Log.i(TAG, "hostAddress ip--> " + hostAddress );
}
};
}
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
(3)扫描网络
private final String mServerType = "_http._tcp.";
//发现周边的NSD相关网络
public void discoveryNSDServer(View view) {
//三个参数
//第一个参数要和NSD服务器端定的ServerType一样,
//第二个参数是固定的
//第三个参数是扫描监听器
mNsdManager.discoverServices(mServerType, NsdManager.PROTOCOL_DNS_SD, mNSDDiscoveryListener);
}
1
2
3
4
5
6
7
8
9
10
11
12
进行发现服务操作后,会在扫描监听器对应的方法得到数据。
(4)解析网络
//对得到的NDSServiceInfo进行解析
public void resoleServer(){
//第一个参数是扫描得到的对象,第二个参数是解析监听对象
mNsdManager.resolveService(nsdServiceInfo, mNSDResolveListener);
}
1
2
3
4
5
6
7
8
9
对数据解析后就能在解析监听对象的回调方法中,获取到我们最终要的数据了。
有些人看网上那些例子,说客户端也要注册,才不会导致什么什么问题。但是我是没有注册的并且程序很正常。
按道理来说也是,不需要注册的,因为我们是要连接服务器端,自己并不需要作为服务器端,Socket连接也是这样的。
如果要自己是客户端,又是别人的服务器端,这种情况那就另说了。
对NSD的其他方面
NSD的Server服务器端的注册和监听最好是放在子线程中或者使用Android服务类Service来注册监听
同样Client客户端也是用子线程或Service来实现比较稳妥。
直接服务器端直接在Activity监听,回调,拿到里面的数据显示在Activity上面是有点问题的,我试过!
NSD还有一些其他的坑,比如客户端扫描NSD网络中,这个扫描监听器不能在执行一次扫描,要等它扫描完成,否则程序会崩溃。
关于NSD这里有个简单的示例,代码:
https://download.csdn.net/download/wenzhi20102321/10289492
程序基本功能还是可以的。就是现在不方便展示!
这个程序的主页是选择“Server页面”或者“Client”页面
点击选择“Server页面”进入服务器端页面,输入主机名、端口号,点击注册,稍等一下就显示是否注册成功
如果有另一个手机选择“Client页面”进入客户端页面,只有一个按钮选择dicoveryNSD,就会进行NSD网络发现,并且解析,最后显示扫描到的NSD网络的完整数据。
这里还有一个服务器端的封装代码,可以直接拿来用的
package com.nsd.lwx.nsddemo.nsd;
import android.content.Context;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.util.Log;
import com.nsd.lwx.nsddemo.utils.Constant;
/**
* Description: NSD Server 的封装类
*/
public class NSDServer {
public static final String TAG = "NSDServer";
private NsdManager mNsdManager;
private NsdManager.RegistrationListener mRegistrationListener;//注册监听器对象
private String mServerName;
private Context mContext;
private int mPort;
private String mServiceName;
private final String mServerType = "_http._tcp.";
//监听注册,并开始NSDServer注册
public void startNSDServer(Context context, String serviceName, int port) {
initializeRegistrationListener();
registerService(context, serviceName, port);
}
//实例化注册监听器
private void initializeRegistrationListener() {
mRegistrationListener = new NsdManager.RegistrationListener() {
@Override
public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
Log.e(TAG, "NsdServiceInfo onRegistrationFailed");
if (registerState != null) {
registerState.onRegistrationFailed(serviceInfo, errorCode);
}
}
@Override
public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
Log.i(TAG, "onUnregistrationFailed serviceInfo: " + serviceInfo + " ,errorCode:" + errorCode);
if (registerState != null) {
registerState.onUnregistrationFailed(serviceInfo, errorCode);
}
}
@Override
public void onServiceRegistered(NsdServiceInfo serviceInfo) {
mServerName = serviceInfo.getServiceName();
Log.i(TAG, "onServiceRegistered: " + serviceInfo);
Log.i(TAG, "mServerName onServiceRegistered: " + mServerName);
if (registerState != null) {
registerState.onServiceRegistered(serviceInfo);
}
}
@Override
public void onServiceUnregistered(NsdServiceInfo serviceInfo) {
Log.i(TAG, "onServiceUnregistered serviceInfo: " + serviceInfo);
if (registerState != null) {
registerState.onServiceUnregistered(serviceInfo);
}
}
};
}
//注册NSD服务器端
private void registerService(Context context, String serviceName, int port) {
mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE);
NsdServiceInfo serviceInfo = new NsdServiceInfo();
serviceInfo.setServiceName(serviceName);
serviceInfo.setPort(port);
serviceInfo.setServiceType(mServerType);
mNsdManager.registerService(serviceInfo, NsdManager.PROTOCOL_DNS_SD, mRegistrationListener);
}
//取消注册NSD服务器端
public void stopNSDServer() {
mNsdManager.unregisterService(mRegistrationListener);
}
//NSD服务注册监听接口
public interface IRegisterState {
void onServiceRegistered(NsdServiceInfo serviceInfo); //注册NSD成功
void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode); //注册NSD失败
void onServiceUnregistered(NsdServiceInfo serviceInfo); //取消NSD注册成功
void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode); //取消NSD注册失败
}
//NSD服务接口对象
private IRegisterState registerState;
//设置NSD服务接口对象
public void setRegisterState(IRegisterState registerState) {
this.registerState = registerState;
}
}
1