设备发现[Airplay投屏应用]-mdns协议简介

背景

在电视的实际应用中,投屏功能占了很大的一个比重,熟悉和掌握投屏的原理和应用,对于
我们开发和测试人员是必不可少的;在投屏时,首先第一步,手机和IPAD等客户端需要发
现局域网中的目标投屏设备(也即电视),才能将音视频或图片或镜像投放到电视上去。

在局域网中,设备和设备之前相互通信需要知道对方的ip地址的,大多数情况,设备的ip
不是静态ip地址,而是通过dhcp 协议动态分配的ip 地址,如何设备发现呢,就是要mdns
大显身手,例如:现在物联网设备和app之间的通信,要么app通过广播,要么通过组播,
发一些特定信息,感兴趣设备应答,实现局域网设备的发现,当然mdns 比这强大的多。


简介

Mdns 即多播dns(Multicast DNS),mDNS主要实现了在没有传统DNS服务器的情况下使
局域网内的主机实现相互发现和通信,使用的端口为5353,遵从dns协议,使用现有的DNS
信息结构、名语法和资源记录类型。并且没有指定新的操作代码或响应代码。

本文简要的介绍一下Mdns的工作原理,设备注册和设备发现。


工作原理

Mdns 使用组播地址为: 224.0.0.251 (ipv6:FF02::FB) 端口为5353,mdns 是用于局域
网内部的,并且主机的域名为.local 结尾,每个进入局域网的主机,如果开启了mDNS服务的
话,都会向局域网内的所有主机组播一个消息,我是谁(域名),和我的IP地址是多少。然
后其他有Mdns服务的主机就会响应,也会告诉你,它是谁(域名),它的IP地址是多少。
当然设备需要服务时,就是使用Mdns查询域名对应的ip地址,对应的设备收到该报文后
同样通过组播方式应答,此时其他主机设备也是可以收到该应答报文,其他主机也会记录域
名和ip 以及ttl 等,更新缓存。
简单的来说,同在一个局域网的投屏设备,源设备和目标设备会加入同一个组播组【IP:
224.0.0.251 PORT: 5353 】;
目标设备,主要是指电视或电脑等大屏设备,在服务启动时,会根据设备名和MAC注册两
个服务(_raop._tcp.local和_airplay._tcp.local),并向组播地址(224.0.0.251)发布广播,宣称自
己提供的服务;
源设备,也即苹果手机或Ipad等,在打开视频APP 投屏时(一般都会有个类似TV图标提示 ),
或在浏览相册需要进行投屏时,向组播地址询问是否有_raop._tcp.local和_airplay._tcp.local
服务的设备,组内其他的主机在接收到请求后,如果本机注册了该两项服务,则会就行应对,
应答时,包含了本机的IP 地址和所提供的服务。
大概的原理就是这样子,mDNS提供的服务要远远多于这个,当然服务多但并不复杂。

Airplay协议用Bonjour做设备发现,它是Apple公司为基于组播域名服务(multicast DNS)的开
放性零配置网络标准所起的名字。

以下我们以开源项目Jmdns 为例讲解mDNS 服务的注册和设备发现


Sample Code for Service Registration
import java.io.IOException;
import java.net.InetAddress;

import javax.jmdns.JmDNS;
import javax.jmdns.ServiceInfo;

public class ExampleServiceRegistration {

    public static void main(String[] args) throws InterruptedException {

        try {
            // Create a JmDNS instance
            JmDNS jmdns = JmDNS.create(InetAddress.getLocalHost());

            // Register a service
            ServiceInfo serviceInfo = ServiceInfo.create("_http._tcp.local.", "example", 1234, "path=index.html");
            jmdns.registerService(serviceInfo);

            // Wait a bit
            Thread.sleep(25000);

            // Unregister all services
            jmdns.unregisterAllServices();

        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }
}

Sample code for Service Discovery
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;

import javax.jmdns.JmDNS;
import javax.jmdns.ServiceEvent;
import javax.jmdns.ServiceListener;

public class ExampleServiceDiscovery {

    private static class SampleListener implements ServiceListener {
        @Override
        public void serviceAdded(ServiceEvent event) {
            System.out.println("Service added: " + event.getInfo());
        }

        @Override
        public void serviceRemoved(ServiceEvent event) {
            System.out.println("Service removed: " + event.getInfo());
        }

        @Override
        public void serviceResolved(ServiceEvent event) {
            System.out.println("Service resolved: " + event.getInfo());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        try {
            // Create a JmDNS instance
            JmDNS jmdns = JmDNS.create(InetAddress.getLocalHost());

            // Add a service listener
            jmdns.addServiceListener("_http._tcp.local.", new SampleListener());

            // Wait a bit
            Thread.sleep(30000);
        } catch (UnknownHostException e) {
            System.out.println(e.getMessage());
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }
}

上面这段代码在是一个简单的设备注册示例;主要分为两部分

  1. 创建一个ServiceInfo 实例
  2. 将刚刚创建的ServerInfo进行注册,并进行广播

PART1: ServiceInfo 详细

ServiceInfo create(final String type, final String name, final int port, final String text)
@param type服务类型,Bonjour要求格式为"_服务名._传输协议",例如"_ftp._tcp";目前传输协议仅支持TCP和UDP
@param name [唯一的]服务名称
@param port 服务端口号, 如果为0的话,Bonjour会自动分配一个
@param text 对服务的描述

以Bonjour 为例,Airplay 投屏需要创建两个服务,分别是_raop._tcp.local和_airplay._tcp.local

private void startRaop(String devicename) {
        ………
        String raopName = preMac + "@" + devicename;
        raopService = ServiceInfo.create(raopName + raopType, raopName,
                    RAOP_PORT, 0, 0, values);
        jmdnsRaop = JmDNS.create(inetAddress);
        jmdnsRaop.registerService(raopService);
    }
private void startAirplay(String devicename) {
        ………
        airplayService = ServiceInfo.create(devicename + airplayType, devicename,
        AIRPLAY_PORT, 0, 0, values);
    	jmdnsAirplay = JmDNS.create(inetAddress);
		jmdnsAirplay.registerService(airplayService);
 }
Devicename 表示本机的设备名
raopType = "._raop._tcp.local";
airplayType = "._airplay._tcp.local"
raopService 的raopName 中需要加入设备的MAC 地址后面紧跟一个@,否则服务将不被识别。

PART2: 服务注册和广播
javax.jmdns.impl.JmDNSImpl.java

public void registerService(ServiceInfo infoAbstract) throws IOException {
        if (this.isClosing() || this.isClosed()) {
            throw new IllegalStateException("This DNS is closed.");
        }
        final ServiceInfoImpl info = (ServiceInfoImpl) infoAbstract;

        if (info.getDns() != null) {
            if (info.getDns() != this) {
                throw new IllegalStateException("A service information can only be registered with a single instamce of JmDNS.");
            } else if (_services.get(info.getKey()) != null) {
                throw new IllegalStateException("A service information can only be registered once.");
            }
        }
        info.setDns(this);

        this.registerServiceType(info.getTypeWithSubtype());

        // bind the service to this address
        info.recoverState();
        info.setServer(_localHost.getName());
        info.addAddress(_localHost.getInet4Address());
        info.addAddress(_localHost.getInet6Address());

        this.makeServiceNameUnique(info);
        while (_services.putIfAbsent(info.getKey(), info) != null) {
            this.makeServiceNameUnique(info);
        }

        this.startProber();

        logger.debug("registerService() JmDNS registered service as {}", info);
    }

有兴趣的朋友可以点击进入项目,下载源码
项目地址

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值