Network programming 您好:按名称搜索服务

Network programming 您好:按名称搜索服务,network-programming,bonjour,zeroconf,Network Programming,Bonjour,Zeroconf,我有一个应用程序,需要搜索和解决一个名为“早安”的广告服务。我发现的大多数与服务发现相关的Bonjour示例的结构大致如下: 调用browse检测给定类型的所有服务(例如,这可能是\u http.\u tcp) 对于找到的每个服务,将调用serviceFound。此处报告服务名称 对找到的每个服务调用resolve 对于每个已解析的服务,调用servicesolved 既然我事先知道要解析的服务的名称,那么使用Bonjour是否可以跳过“发现”阶段?我可以检测并解析一个名称已知的服务吗?1-回答

我有一个应用程序,需要搜索和解决一个名为“早安”的广告服务。我发现的大多数与服务发现相关的Bonjour示例的结构大致如下:

  • 调用
    browse
    检测给定类型的所有服务(例如,这可能是
    \u http.\u tcp
  • 对于找到的每个服务,将调用
    serviceFound
    。此处报告服务名称
  • 对找到的每个服务调用
    resolve
  • 对于每个已解析的服务,调用
    servicesolved

  • 既然我事先知道要解析的服务的名称,那么使用Bonjour是否可以跳过“发现”阶段?我可以检测并解析一个名称已知的服务吗?

    1-回答

    是的,如果您已经知道服务的名称,可以从第三步开始。这是因为此步骤是通过DNS查找发送到已知多播地址的服务名为的SRV记录来执行的。因此,进行此调用不需要以前的信息,mDNS响应程序必须是无状态的,因为基础DNS协议是无状态的(每个响应都绑定到一个唯一的请求-在多个请求之间不维护状态)

    2-示例

    这是我刚刚用Swift写的一个例子,它通过了在我的iPad上运行的测试,找到了在我的MacMini上运行的服务。 因此,我们假设域是
    local
    ,服务类型是
    \u http.\u tcp
    ,服务的名称是
    myservice
    ,运行在主机Mac-mini-de-Alexandre.local上,并侦听tcp端口8080

    为了跟踪有关服务的信息,例如其主机名和TCP端口,我们定义了一个实现NetServiceDelegate协议的类:

    class MyNetServiceDelegate : NSObject, NetServiceDelegate {
         public func netServiceDidResolveAddress(_ sender: NetService) {
            print(sender.hostName!, sender.port)
        }
    }
    
    这个新类将用于实例化NetService实例的委托

    因此,我们创建了一个NetService实例,该实例对应于我们已经知道的服务,我们使用某个主类的静态常量属性长期存储该实例:

    static let ns = NetService(domain: "local.", type: "_http._tcp.", name: "myservice")
    
    它是长期存储的,因为在我们找到服务之前,它不能被释放

    请注意,NetService类中的委托属性声明为无主(不安全)。因此,我们还需要创建对委托实例的引用:

    static let ns_deleg = MyNetServiceDelegate()
    
    当我们想要解决服务时,我们可以写:

    ns.delegate = ns_deleg
    ns.resolve(withTimeout: TimeInterval(10))
    
    如果找到服务,稍后将调用委托实例(
    resolve()
    是一种非阻塞方法),在这种情况下,它将打印主机名和端口

    以下是我在Xcode输出窗口中获得的输出:

    Mac-mini-de-Alexandre.local. 8080
    
    最后,请注意,由于无主引用,编写以下代码是错误的(委托实例将很快被释放):

    3-帮助调试的技巧

    下面是调试这种mDNS解决方案的一个小技巧:在Unix shell(例如macOS)上,只需运行以下行:

    dig -p 5353 @224.0.0.251 myservice._http._tcp.local. SRV +short
    
    如果名为myservice的http服务正在运行,您将获得主机名和端口。通过我的示例,您将得到以下结果:

    0 0 8080 Mac-mini-de-Alexandre.local.
    
    因此,在尝试使用我在这里编写的Swift代码之前,只需检查您的服务是否已使用此shell命令正确宣布


    最后,请注意,此基于dig的命令仅在每个IPv4网络接口上进行一次IPv4 MDN查询,但使用Apple Bonjour API,会自动执行两组MDN请求:一组使用IPv4发送到支持IPv4的每个网络接口上的多播目标224.0.0.251,在支持IPv6的每个接口上,另一个连接IPv6到多播目标ff02::fb。

    1-回答

    是的,如果您已经知道服务的名称,可以从第三步开始。这是因为此步骤是通过DNS查找发送到已知多播地址的服务名为的SRV记录来执行的。因此,进行此调用不需要以前的信息,mDNS响应程序必须是无状态的,因为基础DNS协议是无状态的(每个响应都绑定到一个唯一的请求-在多个请求之间不维护状态)

    2-示例

    这是我刚刚用Swift写的一个例子,它通过了在我的iPad上运行的测试,找到了在我的MacMini上运行的服务。 因此,我们假设域是
    local
    ,服务类型是
    \u http.\u tcp
    ,服务的名称是
    myservice
    ,运行在主机Mac-mini-de-Alexandre.local上,并侦听tcp端口8080

    为了跟踪有关服务的信息,例如其主机名和TCP端口,我们定义了一个实现NetServiceDelegate协议的类:

    class MyNetServiceDelegate : NSObject, NetServiceDelegate {
         public func netServiceDidResolveAddress(_ sender: NetService) {
            print(sender.hostName!, sender.port)
        }
    }
    
    这个新类将用于实例化NetService实例的委托

    因此,我们创建了一个NetService实例,该实例对应于我们已经知道的服务,我们使用某个主类的静态常量属性长期存储该实例:

    static let ns = NetService(domain: "local.", type: "_http._tcp.", name: "myservice")
    
    它是长期存储的,因为在我们找到服务之前,它不能被释放

    请注意,NetService类中的委托属性声明为无主(不安全)。因此,我们还需要创建对委托实例的引用:

    static let ns_deleg = MyNetServiceDelegate()
    
    当我们想要解决服务时,我们可以写:

    ns.delegate = ns_deleg
    ns.resolve(withTimeout: TimeInterval(10))
    
    如果找到服务,稍后将调用委托实例(
    resolve()
    是一种非阻塞方法),在这种情况下,它将打印主机名和端口

    以下是我在Xcode输出窗口中获得的输出:

    Mac-mini-de-Alexandre.local. 8080
    
    最后,请注意,由于无主引用,编写以下代码是错误的(委托实例将很快被释放):

    3-帮助调试的技巧

    这里有一个调试这种mDNS解决方案的小技巧:在Unix shell(例如macOS)上,只需运行以下lin