背景, Zeroconf

BonjourZeroconfApple 上的实现, Zeroconf 看名字就很屌, zero-configuration ,也就是零配置,主要解决的是如何动态发现一个网络中具有各种服务的设备,连接这种设备完成对应的功能。一般情况下,当网络中的设备连上网时会得到一个 ipip 要么是手动配置的固定 ip ,要么是自动获取的动态 ip 。在网络中设备需要互联时,需要知道对方的 ip ,一个方案是手动设置网络中各设备的 ip 地址并记录起来。但是在一个动态网络中, ip 会经常变化,另一个方案是采用 DNS 系统,设置一个解析器,并且将不同服务的设备起一个容易记住的别名,在访问别名时,通过 DNS 的解析获得真实的 ip

对于大型网络,这么做没问题,也工作得很好。如果只是一个小小的局域网,甚至是临时组起来的网络,并没有条件来建议一个 DNS 服务器来提供服务。比如大家到了一个地方,连上了那里的无线,想使用网络中的打印机,一个方案是询问打印机 ip 然后连接,=ip= 一直变的话体验应该会很酸爽。

Zeroconf 就是为了解决这个问题,在一个动态的网络中,动态的发现各种服务。简单说来, Zeroconf 实现了不需要配置在网络中注册,发现设备的一套方法,类似一个分布式动态的 DNS 系统。

Zeroconf 在各系统下有不同的实现,主流操作系统中, Apple 里面是 Bonjourunix-like 系统里面是 avahiwindows 系统中是 LLMNR

Bonjour 的应用和开发环境

Zeroconf 类的实现一般都是通过 DNSSD ,即基于 DNS 的服务发现,与中心化的 DNS 系统不一样,是一个优化的专门用于发现来服务分布式方案。

Apple 的设备上大量的使用 Bonjour 协议, Safari, iChat,Message 都可以用它来发现附近的设备或进行 p2p 的连接。 Android 中使用也比较多,例如 chromecast 推送等。 Apple 开源了实现 BonjourmDNSResponder ,并且提供 cjava 的绑定。 Android 呢,从 4.1 开始, SDK 中提供了 NsdService 的系统服务,可以用作设备发现。官方文档也比较齐全,根据示例代码能很快的写出一个发现设备的服务。

但是,但是,但是,重要的转折要说三遍。 Android 中的 NsdService 实现是不全的!缺的是其中最重要的一个东西,即 txtRecords 。通过 Android 的库写出来的应用,只能简单的发现设备,得到其名字等简单信息。而没有重要的附加信息 txtRecordstxtRecords 简单来说就是设备可以共享的一些附加信息,可能包含版本号,某设备当前运运行状态,如果是游戏中的共享,可能还有游戏对手的一些信息。 txtRecords 非常灵活,大小一般在 200 字节左右,能共享一些非常有用的东西。一般来讲,使用 Zeroconf/Bonjour 最重要的就是获取 txtRecords 来得知设备的一些状态, 所以一个不能解析 txtRecords 的实现就好比过年没有年夜饭一样。 很久之前这个 bug 就被发现并报告给 Android 官方了,但是时光流转,这么多年来 google 对此没有任何改进(详见这里 还有 这里bug2012 年提到了 2015 年,没有任何改进)。要在 Android 里面完整支持设备发现,只能使用一个老旧的 jmDNS 的库。然而这个库也有各种问题,首先是好久没更新了,然后虽然可以提取出 txtRecords ,但是对 txtRecords 的解析有些小问题,导致解析出来的大部分信息都是空白。需要经过一定的修改才能用。而且,这个库非常慢。。。

Apple 和 mDNSResponder

以上两种方法都不完美,最终完美的方法,是使用来自 ApplemDNSResponder , 上面提到苹果为它做了 java 的封装。这个封装的原理是通过 jni 调用 c 语言实现的 libdns_sd.so 。很讽刺,对吧, Android 里面自己也使用的一个标准协议,原生提供的编程接口竟然是一个阉割版,而完整功能还需要苹果来拯救。

集成方法是去 Android 源码里面查询 mDNSResponder 的版本,然后去 Apple 的开源网站上获取源码,然后编译 jni ,再将 java wrapper 放到项目里面使用。

其实折腾起来似乎也蛮复杂。。。不过好在有个小哥将这个事情都做好了,包括各个平台 jni 库,还写了一个示例 app 。这个 app 包含了一个模块,即 mDNSResponderjava 绑定,在 android studio 下面编译后会生成一个 aar ,图方便的话,可以直接使用这个 aar

其他不多说了,放上那小哥的 github 项目地址:BonjourBrowser。 以及,他还贴心的写了一个 blog 来介绍这个,见 Bonjour in Android Applications