mDNS是multicast DNS,mDns实现了在局域网内的服务发现功能。mDNS协议应用非常广泛,其中苹果的Bonjour就是mDNS的一个实现,此外Android 4.1之后也加入了对mDNS协议的支持。那么不禁要问,mDNS是如何实现局域网内的服务发现功能的呢?其实答案就在问题中,multicast + DNS。
mDNS协议基于多播网络技术,它工作于UDP的5353端口。mDNS选择多播作为其实现方式,是有一定原因的。首先 ,单播网络不适合用于局域网络的服务 ,因为单播是点对点的,而服务的信息却应该面向多个客户的。其次,广播也是有局限的,为了避免广播风暴,广播是不能穿越子网的。所以mDNS选择多播技术的主要原因是多播面向多客户端、以及子网穿越的特点。多播节省带宽的特性在局域网内其实意义不大。
-
JmDNS的使用与实现
DNS的内容,我目前还掌握的不多,各种记录的含义并没有仔细了解。所以这里对DNS部分就不做过多的分析了,简单地认为它记录了一些服务的地址以及端口信息。mDNS其实是一个纯粹的应用层协议,也存在多种实现。其中,JmDNS就是mDNS在Java上的实现。下面的代码是一个简单的JmDNS示例:
public class DiscoverServices { static class SampleListener implements ServiceListener { public void serviceAdded(ServiceEvent event) { System.out.println("Service added : " + event.getName()+"."+event.getType()); } public void serviceRemoved(ServiceEvent event) { System.out.println("Service removed : " + event.getName()+"."+event.getType()); } public void serviceResolved(ServiceEvent event) { System.out.println("Service resolved: " + event.getInfo()); } } public static void main(String[] args) { try { JmDNS jmdns = JmDNS.create(); jmdns.addServiceListener("_http._tcp.local.", new SampleListener()); int b; while ((b = System.in.read()) != -1 && (char) b != 'q'); jmdns.close(); System.out.println("Done"); } catch (IOException e) { e.printStackTrace(); } } }
使用JmDNS的方式非常简单,基本上就是填充这几个回调函数。在回调中处理相应的事务逻辑即可,比如在serviceResolved中实现服务连接逻辑等等。
核心的事情都是JmDNS在做,其实JmDNS的代码量不大的,这里分析几处代码,算是揭一揭JmDNS的面纱吧!JmDNS的功能基本都是由JmDNSImpl实现的,JmDNSImpl类有几个关键的函数,分别是初始化函数init()和启动函数start()。其中init方法的代码如下:
private void init(InetAddress address, String name) throws IOException { localHost = new HostInfo(address, name); cache = new DNSCache(100); listeners = Collections.synchronizedList(new ArrayList()); serviceListeners = new HashMap(); typeListeners = new ArrayList(); services = new Hashtable(20); serviceTypes = new Hashtable(20); timer = new Timer(); new RecordReaper(this).start(timer); shutdown = new Thread(new Shutdown(), "JmDNS.Shutdown"); Runtime.getRuntime().addShutdownHook(shutdown); incomingListener = new Thread(new SocketListener(this), "JmDNS.SocketListener"); // Bind to multicast socket openMulticastSocket(getLocalHost()); start(getServices().values()); }
init方法主要实现了各种服务监听相关的数据结构,然后创建了一个线程incomingListener。顾名思义,这个线程就是用来接收和处理进来的网络数据。然后在函数的倒数两行,实现了多播地址的绑定以及mDNS服务的启动。这个start函数的功能相比之下显得更简单了,代码如下:
private void start(Collection serviceInfos) { setState(DNSState.PROBING_1); incomingListener.start(); new Prober(this).start(timer); for (final Iterator iterator = serviceInfos.iterator(); iterator.hasNext();) { try { registerService(new ServiceInfoImpl((ServiceInfoImpl) iterator.next())); } catch (final Exception exception) { logger.log(Level.WARNING, "start() Registration exception ", exception); } } }
没有骗人吧!真的很简单,基本就是各个线程的启动。比如说incomingListener服务进程,以及Prober这个探测进程,它们工作的方式基本都是事件驱动型的,一直等待着相应事件的发生。这样简单地分析一下可以发现,mDNS其实并不是一个特别复杂的系统。当然这里我并没有分析它的DNS部分的逻辑,因为现在还不熟悉。个人打算在随后分析一下DNS部分的代码,权当学习DNS系统了,我估计这个比BIND应该要简单不少。
此外,Android系统下的Nsd Service也是相当类似了,毕竟它其实就是mDNS在Android下的实现。所以也就不对它的使用方式再做解析了,实现方式倒是可以去分析一下,以后再说了!
发表评论