Android局域网通信机制初探
在网络上,两台主机要进行通信,就必须互相知晓对方的IP地址,在Internet上,一台主机的IP地址可能是经常发生变化的,并且IP地址非常难以记忆,因此产生了将主机名(域名)解析为IP地址的DNS服务。在局域网内,往往不存在DNS服务器,而在使用了DHCP的局域网内,各主机的IP往往也是不固定的,因此在主机之间进行IP解析是有一定困难的,为解决这个问题,mDNS应运而生。
在计算机网络中,多播域名系统(multicast Domain Name System, mDNS)被用于在一个不包含DNS服务器的局域网络中做域名解析服务。
RFC 6762定义了mDNS的规范,它使用UDP协议承载,在IPv4网络上,使用组播地址224.0.0.251,在IPv6网络上,使用组播地址ff02::fb, 5353端口来广播报文。Apple的Bonjour就是一个基于mDNS的服务。
使用mDNS服务的主机主要执行两个动作:Register和Discover
Register顾名思义,Register即为主机在网络上发布一个服务,一个最基本的服务包含以下信息:
服务类型:表明该主机提供服务的类型,例如,一台提供HTTP访问服务的主机发布的服务类型为_http._tcp 服务名称:主机名,类似web网站的域名 端口:表明该服务所在的端口 DiscoverDiscover在网络内查找提供相应服务的主机,查找操作只需提供服务名称即可。
提供mDNS服务的主机在加入网络时,会向发送一个广播消息,表明自己的身份,在收到查询请求时,会回复自己的IP地址和端口信息;一个主机发起discover请求时,同样会发送一个广播消息,网络内所有能提供服务的主机都会回复此广播消息。
使用JmDNS库JmDNS是一个使用Java实现的用于在局域网内进行服务注册和发现的mDNS服务,它完全兼容Apple Bonjour,可工作在JDK 1.6+。
JmDNS在maven中的groupId为javax.jmdns, artifactId为jmdns
JmDNS jmdns = JmDNS.create();
注册服务是可选的,如果只需要使用网络发现,而自己不提供服务的话可以跳过这一步。注册服务需要提供一个ServiceInfo对象,用于描述所提供服务的信息。
//创建ServiceInfo String serviceType = "_test._tcp.local."; String serverName = "JmDNS Test"; int port = 1234; ServiceInfo serviceInfo = ServiceInfo.create(serviceType, serverName, port); //注册服务 jmdns.registerService(serviceInfo);
发现服务需要创建一个ServiceListener对象,用来处理网络发现过程中各阶段的回调。
jmdns.addServiceListener("_http._tcp.local", listener = new ServiceListener() { @Override public void serviceResolved(ServiceEvent ev) { String addr = ""; if (ev.getInfo().getInetAddresses() != null ev.getInfo().getInetAddresses().length 0) { addr = ev.getInfo().getInetAddresses()[0].getHostAddress(); System.out.println("Service resolved: " + ev.getInfo().getName() +" == "+ addr + ":" + ev.getInfo().getPort() ); @Override public void serviceRemoved(ServiceEvent ev) { System.out.println("Service removed: " + ev.getName()); @Override public void serviceAdded(ServiceEvent event) { // Required to force serviceResolved to be called again (after the first search) jmdns.requestServiceInfo(event.getType(), event.getName(), 1); }});
在解析过程中,当一个新服务被发现时,serviceAdded()方法会被回调,在回调中我们可以使用requestServiceInfo()方法发起一个解析请求,当服务被成功解析后,serviceResolved()方法会被回调。当一个服务在网络中被移除时,serviceRemved()会被回调。
JmDNS库在Android平台上效率欠佳
使用NsdManager自Android 4.1开始实现了一个网络服务的发现服务NsdService,其基于苹果的Bonjour服务发现协议,支持远程服务的发现和零配置。Bonjour协议包括IP地址的自动分配、服务名称与地址的转换以及服务的发现三部分内容,Android 4.1借助第三方开源工程mDNSResponder实现了Bonjour协议的服务名称与地址的转换以及服务的发现等Bonjour部分协议的支持。
NsdManager的使用方法和JmDNS十分相似。
NsdManager nsdManager = (NsdManager) context.getSystemService(Context.NSD_SERVICE)
在这里,注册服务同样是可选的
mNsdManager.registerService(serviceInfo,protocolType,listener);
发现服务是需要指定服务类型、协议类型和一个Listener
mNsdManager.discoverServices(SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, listener);
当发现一个新服务时,onServiceFound()方法会被回调,当一个服务丢失或停止提供时,onServiceLost()会被回调。
服务信息解析当一个服务被发现时,服务的IP地址和端口号仍然是未知的,需要进一步做解析
nsdManager.resolveService(serviceInfo,listener)
如果解析成功,listener的onServiceResolved()会被回调,否则onResolvedFailed()会被回调,失败的原因主要有以下几种:
FAILURE_ALREADY_ACTIVE
出现这个错误表示指定的ServiceInfo已经处于解析状态,一般出现在尝试对一个ServiceInfo进行解析而对应的服务又已经关闭或丢失的情况下,出现此错误后除非重启APP,否则没有其他的恢复手段。 FAILURE_MAX_LIMIT
出现这个错误表示NsdManager能执行的解析任务已经到达上限,可能代表着你多次调用了nsdManager.discoverServices()方法添加了多个lisnenet,经过实验,在Android L上并行解析的上限是10个,要解决这一问题,需要在适当的时候调用nsdManager.stopServiceDiscovery()方法来释放listener。 FAILURE_INTERNAL_ERROR
表示NsdService出现了内部错误
经过服务的发现和解析过程,就可拿到服务对应的IP和Port.
我有一段时间特别想实现一个短距离的手机交互软件,比如多人联机小游戏app或者是多人文件共享app等,思来想去,得出了两个方案,一个是使用手机自带的蓝牙设备,通过蓝牙技术实现两个手机的数据交换,而另一个就是使用socket了。蓝牙技术对于android的小伙伴来说并不陌生,传文件什么的常用到,而且网上资料也十分丰富,所以这里不详细说明。而socket则是网络通信的大神级工具,无论c语言也好j
Android组件化开发(七)--从零开始教你分析项目需求并实现 前面几篇文章我们封装了几个组件化功能组件:包括:**网络请求组件,图片加载请求组件,应用保活组件,音乐播放组件封装。** 每个组件都可以直接拿到自己项目中使用,当然还需根据自己项目要求进行优化。
Android组件化开发(六)-- 短视频播放组件封装 前面几篇文章我们封装了几个组件化功能组件: 包括:`网络请求组件`,`图片加载请求组件`,`应用保活组件`,`音乐播放组件封装`。 每个组件都可以直接拿到自己项目中使用,当然还需根据自己项目要求进行优化。
Android组件化开发(五)--完整版音乐播放组件的封装 前面几篇系列文章我们讲解了`组件化开发`中几个常用功能组件的开发,包括:`网络请求组件`,`图片加载请求组件`,`应用保活组件`。今天我们来封装一个`音乐播放组件`。
Android组件化开发(四)--进程保活组件的封装 前面文章我们封装了网络请求组件`lib_nework`和图片加载组件`lib_image_loader`,今天我们来封装一个进程保活的组件`lib_pull_alive`
相关文章
- Unity3d与android通信
- Android中如何使用自定义对话框
- 【COCOS2DX通信(HTTP&SOCKET)相关编译到ANDROID细节总结】编译加入CURL关联LIB与头文件 && 解决PTHREAD的CANCEL函数NDK不支持,找不到SOCKADDR_IN、HTONS等问题;
- EasyDarwin相关Android安卓客户端EasyPusher/EasyPlayer/EasyCamera/EasyClient在无开发环境进行log抓取
- 基于XMPP协议的Android即时通信系
- Android NDK开发 (进阶一)
- 《精通android网络开发》--使用Socket实现数据通信
- 【Android源码解析】一篇就够“路由、网络层、UI层、通信层....百大框架”源码解析,阿里P8神级之作
- Android 两个App间进行IPC通信
- 《Android Framework-跨进程通信高级实战课》笔记
- Android IPC(三)AIDL实现跨进程通信
- Android SQLite封装sql语句、查看数据库
- Android 11.0 无源码apk授予QUERY_ALL_PACKAGES权限
- android 11.0 屏蔽所有电话来电功能
- Android USB设备HID通信
- Flutter之Pigeon插件与Android通信使用指南
- 【Android Gradle 插件】Android Studio 工程 Gradle 构建流程 ② ( settings.gradle 构建脚本分析 | 根目录下 build.gradle 分析 )
- 【Android 逆向】函数拦截 ( ARM 架构下的插桩拦截 | 完整代码示例 )
- Android笔记之 网络http通信
- MonkeyRunner源代码分析Android通信设备
- android开发中fragment的引用以及fragment与activity之间通信的概述
- Android 左右滑动小圆点(ViewPager)布局有两种方法
- Android USB Host 通信程序
- Mac下Android Studio快捷键