网络发现
网络发现中有主控设备(一般是移动端)与被控设备(一般是TV或者PC端)两种角色。
方案一(局域网发现)
采用mDns(组播DNS)相对更独立灵活一些,Android原生有API(NSD)。
在Windows上需要应用自己集成mDns开发。
为防止主控设备与错误的被控设备绑定,采用随机码辅助选择。被控设备会一直显示一个连接码。
在异构网络拓扑中,因为组播转发受限,该方案不可行,需要下面两个辅助方案。
Android NSD 使用:
public class ServiceDiscovery {
public interface IServiceListener {
void onServiceFound(NsdServiceInfo serviceInfo);
void onServiceLost(NsdServiceInfo serviceInfo);
}
private static final String TAG = "ServiceDiscovery";
private final String mServerType = "_board._tcp.";
private NsdManager mNsdManager = null;
private NsdManager.DiscoveryListener mDiscoveryListener = null;
private NsdManager.ResolveListener mResolveListener = null;
private IServiceListener mServiceListener;
ServiceDiscovery(Context context, IServiceListener mServiceListener) {
this.mServiceListener = mServiceListener;
mNsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE);
mDiscoveryListener = new MyDiscoveryListener();
mResolveListener = new MyResolveListener();
}
public void startDiscover() {
mNsdManager.discoverServices(mServerType,
NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener);
}
public void resoleServer(NsdServiceInfo nsdServiceInfo){
mNsdManager.resolveService(nsdServiceInfo, mResolveListener);
}
private class MyResolveListener implements 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 );
mServiceListener.onServiceFound(serviceInfo);
}
}
private class MyDiscoveryListener implements 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) {//关键的回调方法
Log.i(TAG, "onServiceFound Info: " + serviceInfo);
resoleServer(serviceInfo);
}
@Override
public void onServiceLost(NsdServiceInfo serviceInfo) {
Log.i(TAG, "onServiceLost--> " + serviceInfo);
mServiceListener.onServiceLost(serviceInfo);
}
}
}
方案二(后台辅助)
用后台辅助设备绑定,被控设备向后台注册,后台负责维护设备编号与网络地址的对应关系,主控设备通过某种途径获取到设备编号,然后到后台查询其最新的地址。
被控设备的网络地址端口可能每次启动不一样,最新的地址会同步到后台。
为防止IP地址变化引起的冲突,被控设备需要增加校验机制(比如校验编号),拒绝信息不匹配的请求。
方案三(二维码辅助)
可选的,主控设备主动把设备编号推给被控设备,被控设备立即并且以后每次启动都用新的编号更新地址。
使用zxing-android-embedded,启动扫码:
IntentIntegrator intentIntegrator = new IntentIntegrator(MainActivity.this);
intentIntegrator.setCaptureActivity(CaptureActivity.class);
intentIntegrator.setBeepEnabled(false);
intentIntegrator.initiateScan();
获取结果:
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
if (result != null) {
Log.d(TAG, "", result.getContents());
}
}
服务请求
采用定制推送协议,基于HTTP。
主控设备能够通过被控设备的地址主动连接被控设备。主控设备可能处于NAT内网,被控设备如果反向连接,需要在路由器上配置端口映射,基于UPNP/IGD可以自动配置端口映射,Android平台Upnp实现一般基于Cling库。
为解决 NAT 网络穿透问题,多端互动传输协议的设计要尽量避免反向连接,减少对网络拓扑的依赖。比如:投屏功能实现中视频数据的传输需要支持推流模式。