Android Says Bonjour

转载自: https://blog.csdn.net/innost/article/details/8629139

  • Android Says Bonjour

很高兴能在农历蛇年刚开始的这期《程序员》杂志上继续为读者奉上Android的故事。初来咋到,首先要向大家说声”你好“。有意思的是,Android也很通人情,从4.1开始,它会说”Bonjour“了。不过它说得是不是原汁原味的法语腔呢?来看下文。

一背景知识介绍

Bonjour是法语中的Hello之意。它是Apple公司为基于组播域名服务(multicast DNS)的开放性零配置网络标准所起的名字。使用Bonjour的设备在网络中自动组播它们自己的服务信息并监听其它设备的服务信息。设备之间就像在打招呼,这也是该技术命名为Bonjour的原因。Bonjour使得局域网中的系统和服务即使在没有网络管理员的情况下也很容易被找到。

举一个简单的例子:在局域网中,如果要进行打印服务,必须先知道打印服务器的IP地址。此IP地址一般由IT部门的人负责分配,然后他还得全员发邮件以公示此地址。有了Bonjour以后,打印服务器自己会依据零配置网络标准在局域网内部找到一个可用的IP并注册一个打印服务,名为“print service”之类的。当客户端需要打印服务时,会先搜索网络内部的打印服务器。由于不知道打印服务器的IP地址,客户端只能根据诸如"print service"的名字去查找打印机。在Bonjour的帮助下,客户端最终能找到这台注册了“print service”名字的打印机,并获得它的IP地址以及端口号。

Bonjour角度来看,该技术主要解决了三个问题:

  • Addressing:即为主机分配IPBonjourAddressing处理比较简单,即每个主机在网络内部的地址可选范围内找一个IP,然后查看网络内部是否有其他主机再用。如果该IP没有被分配的话,它将使用此IP
  • NamingNaming解决的是host名和IP地址的对应关系。Bonjour采用的是Multiple DNS技术,即DNS查询消息将通过UDP组播方式发送。一旦网络内部某个机器发现查询的机器名和自己设置的一样,就回复这条请求。此外,Bonjour还拓展了MDNS的用途,即除了能查找host外,还支持对service的查找。不过,BonjourNaming有一个限制,即网络内部不能有重名的hostservice
  • Service DiscoverySD基于上面的Naming工作,它使得应用程序能查找到网络内部的服务,并解析该服务对应的IP地址和端口号。应用程序一旦得到服务的IP地址和端口号,就可以直接和该服务建立交互关系。

Bonjour技术在Mac OS以及ItunesIphone上都得到了广泛应用。为了进一步推广,Apple通过开源工程mdnsresponder将其开源出来。在Windows平台上,它将生成一个后台程序mdnsresponder。在Android平台上(或者说支持POSIXLinux平台)它是一个名为mdnsd的程序。不过,不论是mdnsresponder还是mdnsd,应用开发者要做的仅仅是利用BonjourAPI向它们发起服务注册、服务查询和服务解析等请求并接收来自它们的处理结果。

下面我们将介绍Bonjour API中使用最多的三个函数,它们分别是服务注册、服务查询和服务解析。理解这三个函数的功能也是理解MDnsSdListener的基础。

使用Bonjour API必须包含如下的头文件和动态库,并连接到:

#include <dns_sd.h>  //必须包含此头文件

libmdnssd.so  //链接到此so

Bonjour中,服务注册的APIDNSServiceRegister,原型如图1所示:

1  DNSServiceRegister原型

该函数的解释如下:

  • sdRef:代表一个未初始化的DNSService实体。其类型DNSServiceRef是指针。该参数最终由DNSServiceRegister函数分配内存并初始化。
  • flags:表示当网络内部有重名服务时的冲突处理。默认是按顺序修改服务名。例如要注册的服务名为“printer”,当检测到重名冲突时,就可改名为“printer(1)”。
  • interfaceIndex:表示该服务输出到主机的哪些网络接口上。值-1表示仅对本机支持,也就是该服务的用在loop接口上。
  • name:表示服务名,为空的话就取机器名。
  • regtype:服务类型,用字符串表达。Bonjour要求格式为"_服务名._传输协议",例如"_ftp._tcp"。目前传输协议仅支持TCPUDP
  • domianhost一般都为空。
  • port表示该服务的端口。如果为0的话,Bonjour会自动分配一个。
  • txtLen以及txtRecord字符串用来描述该服务。一般都设置为空。
  • callBack:设置回调函数。该服注册的请求结果都会通过它回调给客户端。
  • context:上下文指针,由应用程序设置。

当客户端需要搜索网络内部特定服务时,需要使用DNSServiceBrowser API,其原型如图2所示:

2  DNSServiceBrowser原型

其中:

  • sdrefinterfaceIndexregtypedomain以及context含义与DNSServiceRegister一样。
  • flags:在本函数中没有作用。
  • callBack:为DNSServiceBrowser处理结果的回调通知接口。

当客户端想获得指定服务的IP和端口号时,需要使用DNSServiceResolve API,其原型如图3所示:

3  DNSServiceResolve原型

其中:

  • nameregtypedomain都从DNSServiceBrowse函数的处理结果中获得。
  • callBack用于通知DNSServiceResolve的处理结果。该回调函数将返回服务的IP地址和端口号。

以上介绍的三个APIBonjure的核心API。不过Android中的Bonjour会是怎么个说法呢?

Android Says Bonjour

几乎能肯定的是,Bonjour想跑在Android平台上,还需要一番定制。不过这套定制不是针对mdnsd本身,而是针对Bonjour API的使用。Android平台的Bonjour架构可由图4表达:

4  Android Bonjour架构

由图4可知,Android拓展了原有的Bonjour架构,改变如下:

  • Netd中增加了MDnsSdListener对象,它一方面通过socket和上层对象通信,另一方面通过Bonjour APImdnsd通信(也是基于Socket的跨进程通信)。从mdnsd角度来看,它是最懂Bonjour API的”人“了。
  • System_process进程新增NsdServiceNsdNetwork Service Discovery的缩写。NsdService通过socket和位于Netd中的MDnsSdListener通信。
  • App借用NsdManager API通过Binder技术和System_processNsdService通信。

总之,在Android平台中,应用程序要借助其他三个进程(System_processNetdmdnsd)才能享受到Bonjour好处。不过,这么繁杂的进程间通信会不会影响效率呢?

答案是肯定的。但就如Bonjour的本意一样,它仅是通过打一声招呼以了解网络内服务是否存在以及一些简单信息。一旦客户端通过Bonjour获取到服务的IP地址和端口后,后续客户端和服务的交互就属于私密范畴(即客户端通过服务的IP地址直接和其建立连接)了。从这个角度来看,Android上的这点效率损失实属无伤大雅。

另外,AndroidBonjour架构的设计对读者们来说还有一个启示:如果手机厂商想定制一些功能,最好先对现有Android的架构有充分了解。这样才能结合自己的需求,将功能模块合理得集成到Android架构中以更有效得发挥其功用。

下面来看看AndroidBonjour架构中的几位重要成员。

2.1  MDnsSdListener介绍

MDnsSdListenerAndroid Bonjour架构中扮演着转换器的角色:

  • 一方面它处理来自NsdService的请求,并通过Bonjour API将其转换成mdnsd能懂的“语言”以驱动其工作。
  • 另一方面它接收来自mdnsd的信息,并把它们通报给NsdService

5所示为MDnsSdListener的家族成员示意图。

5  MDnsSdListener家族成员

由图5可知:

  • MDnsSdListener的内部类Monitor用于和mdnsd进程通信,它将调用前面提到的Bonjour API
  • Monitor内部针对每个DNSService都会建立一个Element对象,该对象通过MonitormHead指针保存在一个list中。
  • HandlerMDnsSdListener注册的Command

下面将简单介绍MDnsSdListener的运行过程,其主要工作可分成三步:

  • Netd创建MDnsSdListener对象,其内部会创建Monitor对象,而Monitor对象将启动一个线程用于和mdnsd通信,并接收来自Handler的请求。
  • NsdService启动完毕后将向MDnsSdListener发送"start-service"命令。
  • NsdService响应应用程序的请求,向MDnsSdListener发送其他命令,例如"discovery"等。Monitor将最终处理这些请求。

先来看第一步,当MDnsSdListener构造时,会创建一个Monitor对象,代码如图6所示:

6  Monitor的构造

由图6可知:

  • MonitorthreadStart线程将调用其run函数,该函数通过poll方式侦听包括mCtrlSocketPair在内的socket信息。这部分代码属于基本的Linux socket编程。对大部分读者来说,难度应该不大。
  • NsdService发送"start-service"命令后,HandlerrunCommand将执行MonitorstartService函数。

starService将启动mdnsd,其所使用的方法颇具Android特色,如图7所示:

7  startService代码示意

7中,MDS_SERVICE_NAME宏代表字符串"mdnsd"。了解Android的读者,看完图7,您能很快知道Android启动mdnsd的方法吗?

NsdService发送注册服务请求时,HandlerserviceRegister函数将被调用,代码如图8所示:

8  serviceRegister示意图

DNSServiceRegister内部将把请求发送给mdnsd去处理,处理的结果通过MDnsSdListenerRegisterCallback返回,该函数最终会通过socket把信息传递给NsdService去处理。

MDnsSdListener介绍暂且到此,感兴趣的读者不妨亲自看看代码以加深对Bonjour API用法的理解。

2.2  NsdService介绍

对所有Android App来说,NsdService才是背后的Boss,其用法(当然,是NsdService客户端API的封装类NsdManager的用法)也是在SDK文档中白纸黑字列出来的。NsdService的内部结构可由图9表示:

9  NsdService内部结构示意图

9列出了NsdService中的几个重要成员,其中:

  • NsdServiceINsdManager.stub派生。这个类也是Android的特色产品,由INsdManager.aidl文件生成。
  • NsdService内部工作将通过NsdStateMachine及内部的三个状态对象(DefaultStateEnableStateDisableState)驱动。让笔者颇为惊讶的是,整个NsdService的代码只有800来行。而且从理论上说,NSD不存在什么状态转换。状态机的出现使得代码理解会相对困难。还好NsdStateMachine只有三个状态。读者不妨以它为契机,了解一下AndroidStateMachine的用法。因为它在系统很多地方都被用到。在那些代码中,状态就不止三个了。
  • NsdService通过NativeDaemonConnectorNetd中的MDnsSdListener建立socket通信。
  • NativeCallbackReceiver用来通知NsdService来自Netd的消息。
  • 当然,NsdService费劲心力得到的最重要的产出物就是NsdServiceInfo了。它就是Network ServiceAndroid Bonjour中的代表。其包含的内容有服务名、服务类型、IP地址和端口号等。

由于篇幅原因,本文不拟对NsdService展开详细讨论了。接下来,本文将介绍Android SDK中一个关于Nsd API使用的小例子NsdChat

2.3  NsdChat案例介绍

Android SDK新增了一个NsdChat例子用于向开发者介绍Android平台中Nsd的使用方法。相关文档位于http://developer.android.com/training/connect-devices-wirelessly/nsd.html。案例的源码位于Android4.1源码根目录/development/samples/training/NsdChat下。

该例描述了一个简单的聊天程序,故其命名为NsdChatNsd在此例中的作用就是注册并搜索网络内的聊天服务。所以,在本例中有一个NsdChat进程将通过NsdServiceregisterService函数注册一个聊天服务。相关代码如图10所示:

10  NsdChat注册聊天服务

由图10可知:

  • 应用程序要注册的Nsd服务将通过NsdServiceInfo类来表达。结合前文背景知识,在此NsdServiceInfo中,最重要就是服务的端口号、服务名(根据Bonjour的要求,网络内部不能有同名服务)以及服务的类型。
  • 接着,应用程序通过NsdManagerregisterService函数注册此服务。注册的结果通过NsdManager的内部接口类RegistrationListener来通知。

两人聊天才有意义,所以另外一个运行着NsdChat的客户端进程将搜索网络内部的”NsdChat“服务,相关代码如图11所示:

11  寻找“NsdChat”服务

由图11可知:

  • 应用进程只需调用NsdManagerdiscoveryServices函数并传递要找的服务类型即可。搜索的结果通过NsdManager的内部接口类DiscoveryListener返回。

注意,Nsd只能根据服务类型进行搜索。当网络中有多个同属于一种服务类型(本例中,服务类型是"_http._tcp.")的服务时,应用程序还需根据DiscoveryListener返回的信息进行筛选。这部分代码如图12所示:

12  NsdChatDiscoveryListener处理

由图12可知,discoveryServices的结果通过DiscoveryListener接口类提供的回调函数返回。注意其中onServiceFound函数对同类型服务的筛选处理(值得特别指出的是,Android SDK中并未对此处极易疏忽的地方做任何说明)。

当客户端成功找到NsdChat服务后,下一步工作就是解析该服务的IP地址和端口号。这是通过NsdManagerresolveService函数(注意图12中的红框)来完成的。这个函数的处理结果将通过NsdManager定义的另外一个接口类ResolveListener返回。

通过对NsdChat的研究,读者会发现:

  • 总体而言,NsdManager的使用并不复杂,相关类也比较简单。
  • 唯一特别之处是其主要API都被设计成异步调用的方式,这将增大应用程序开发的难度。请读取务必注意这点。

三总结

本文对AndroidBonjour的实现进行了一番介绍。Bonjour的原理知识还请读者阅读https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/NetServices/Introduction.html#//apple_ref/doc/uid/TP40002445-SW1。该网站是关于Bonjure基础知识的入口,包含《About Bonjour》、《Bonjour API Architecture》等文档。

另外,Android中的Bonjour主要是为了支持Network Service Discovery功能。与其类似的还有UPnP技术中使用的Simple Service Discovery ProtocolSSDP)。相比Bonjour而言,UPnP不仅实现了NSD,还在后续客户端和服务端交互方面支持标准SOAP协议,极大方便了客户端和服务端的代码逻辑实现。所以,笔者在此提醒开发者,如果想使用Bonjour技术,要特别注意Nsd只能简化服务注册及寻找这一步骤,后续还需重点考虑客户端和服务端交互的协议及实现。

关于DLNA,读者可参考笔者的博客 http://blog.csdn.net/innost/article/details/7078539

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/352785.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

iOS 之 Bonjour 协议简单抓包分析

引子 因在学习iOS编程之bonjour编程的过程中&#xff0c;对bonjour产生了一些些的好奇&#xff0c;因此就查 阅了各种资料以及自己抓包分析该协议。 注&#xff1a;文章作为个人学习记录&#xff0c;不一定准确&#xff0c;如有错误请多多指教&#xff0c;共同进步&#xff01;…

1.7 基于XML配置方式使用Spring MVC

一、基于XML配置与注解的方式使用Spring MVC 1、创建Maven项目 Maven项目 - SpringMvcDemo01 单击【Finish】按钮 2、添加相关依赖 在pom.xml文件里添加支持Spring MVC的相关依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmln…

硬件工程师-BOOST升压电源设计

一、Boost变换原理 开关闭合时&#xff0c;电感电压等于输入电压 开关断开时&#xff0c;电感电压输出电压-输入电压&#xff0c; 电感的感生电动势&#xff0c;N ΔΦ磁通的变化率&#xff0c;Δt时间 假设开关闭合与开关断开&#xff0c;开关断开时能量全部释放光 将第三个式…

【Python】深度理解Class类、Object类、Type元类的概念和关系

深度理解Class类、Object类、Type元类 1.Class类、Object类、Type元类的表面关系2.Class、Object、Type解释3.关系详解4.那么如何看待object、type在Python面对对象概念中的一席之地呢&#xff1f;5.那么object、type扮演了什么样的角色呢&#xff1f;他们对class又分别做了什么…

MongoDB(学习笔记1.0)

最近在学非关系型数据库MongoDB&#xff0c;猛地用起来的真的没关系型数据库方便啊。 首先还是数据库的安装&#xff1a; 安装直接去官网安装即可&#xff0c;官网地址&#xff1a;MongoDB: The Developer Data Platform | MongoDB 当前也有免安装版的&#xff0c;这里就不再…

京东数据分析软件工具(京东618销量查询)

这一期&#xff0c;我们主要分享今年618京东美妆的预售数据&#xff0c;包括面部护肤、香水彩妆、男士面部护肤品类。 -面部护肤- 今年618&#xff0c;面部护肤品类在京东累计预售量达到130万件&#xff0c;预售额达到13亿元。预售期间&#xff0c;护肤品类均价在1010元左右。期…

Android 使用第三方字体

先看下图 一.全局替换方式 1.新建assets引入资源 2. 自定义application&#xff0c;将第三方的字体&#xff0c;替换当前系统默认字体 class App : Application() {override fun onCreate() {super.onCreate()initTypeface()}private fun initTypeface() {val typefaceByson…

Android 字体大小(fontScale)不随系统设置变化

需求 App字体大小不变 如果用户将系统字体大小设置的非常大&#xff0c;可能导致APP的文字大小显示异常。 目标效果是&#xff0c;APP内字体大小不随系统设置的 字体大小 变化。 原始效果 系统的字体大小设置为 超大 时&#xff1a;&#xff08;字体大小 可变&#xff09; 目…

Android 自带的字体库、字体样式

1、设置字体 android:fontFamily“字体样式” 1、sans-serif-smallcaps &#xff08;左边只设置字体&#xff0c;右边设置加粗&#xff09; 2、sans-serif 3、cursive 4、sans-serif-black 5、sans-serif-condensed-light 6、sans-serif-thin 7、serif 8、serif-monospa…

android屏幕大小字体大小,Android字体大小自适应不同分辨率的解决办法

Android字体大小自适应不同分辨率的解决办法 今天有人问我&#xff0c;Android系统不同分辨率&#xff0c;不同大小的手机&#xff0c;字体大小怎么去适应呢&#xff1f;其实字体的适应和图片的适应是一个道理的。 一、原理如下&#xff1a; 假设需要适应320x240&#xff0c;48…

Android切换字体

有时候需要根据UI需求切换项目字体&#xff0c;步骤如下&#xff1a; 准备字体文件&#xff0c;示例使用ttf文件&#xff0c;新建font文件夹&#xff1a; 将ttf文件放到res/font目录下 设置全局字体&#xff0c;系统主题添加 <item name"android:fontFamily">f…

Android 字体颜色设置及颜色表

1、在android中经常看到设置的颜色为八位的十六进制的颜色值&#xff0c;例如&#xff1a; public static final class color { public static final int lightblue0x7f040000; } 或者在Java中tx.setTextColor(0xffff00f); 说明&#xff1a; 0xffff00ff是int类型的数据&#…

IOS字体与安卓字体渲染不一致

IOS字体与安卓字体渲染不一致 问题: 前端开发中经常会遇到各种各样的兼容问题,记录一次IOS字体与安卓字体渲染不一致,当字体包名字中包含’-Bold’时 错误代码如下: font-family: DINAlternate-Bold ; font-weight: bold;IOS: 不会渲染’font-weight: bold’只是加载了’…

android中文字体加粗,android TextView设置中文字体加粗实现方法

android TextView设置中文字体加粗实现方法 英文设置加粗可以在xml里面设置: 复制代码 代码如下: android:textStyle="bold" 英文还可以直接在String文件里面直接这样填写: 复制代码 代码如下: Plain, bold, italic, bold-italic b代码加粗,i代表倾斜 中文设置加粗就…

android常用字体代码,Android TextView设置字体风格多种组合

在开发应用过程中经常会遇到显示一些不同的字体风格的信息犹如默认的LockScreen上面的时间和充电信息。对于类似的情况,可能第一反应就是用不同的多个TextView来实现,对于每个TextView设置不同的字体风格以满足需求。 这里推荐的做法是使用Android.text.*;和android.text.sty…

Android 字体粗细的设置

转载:https://juejin.im/post/597d88f75188257fc2177c36 如何实现 “中间这几个字要加粗&#xff0c;但是不要太粗&#xff0c;比较纤细的那种粗” &#xff1f; 分享一个最近做业务遇到的简单又蛮有意思的的文本显示处理过程。具体就是有这么一段文字&#xff0c;类似“转盘…

Android字体引入

2个方案&#xff1a;①xml引入、②java代码引入。 效果图&#xff1a; 方法①&#xff1a;xml引入&#xff1a; 1.创建font目录&#xff1a; 2.把字体库复制到font目录下&#xff1a; 3.xml中&#xff0c;使用fontFamily引入字体库&#xff1a; <TextViewandroid:text"…

Android字体样式修改

效果图 准备字体 Download Alibaba Sans比如阿里巴巴普惠体&#xff0c;也可以其他网站找一些对应的资源。 字体使用 将字体文件放到res/font文件夹下&#xff0c;如果没有font文件&#xff0c;则新建一个。 添加到font路径下的字体文件&#xff0c;明明不能是汉字&#xff…

玩转安卓字体

起因 最近公司有个需求&#xff0c;需要做 Widget &#xff0c;内心其实是拒绝的&#xff0c;因为这个玩意儿特别难用&#xff0c;而且限制重重&#xff0c;但没办法&#xff0c;也不能不做&#xff0c;那就开始吧。 本来以为挺简单的东西&#xff0c;一个列表展示数据&#…

iPadPro看电影之MKV转MP4视频格式教程

拥有了苹果iPadPro如果不看高清电影那就有点可惜了&#xff0c;虽说在线看电影也不错&#xff0c;但就目前这种网络环境&#xff0c;还别说很多时候没有Wifi网络&#xff0c;即使能上网很多时候网速也无法支持流畅的在线播放普清电影&#xff0c;更不用说高清电影了。怎么办&am…