1- 答案
是的,如果您已经知道服务的名称,则可以从第 3 步开始。这是因为此步骤是通过 DNS 查找具有发送到众所周知的多播地址的服务名称的 SRV 记录来执行的。因此,进行此调用不需要先前的信息,并且 mDNS 响应者必须是无状态的,因为底层 DNS 协议是无状态的(每个响应都绑定到一个唯一的请求 - 在多个请求之间不维护任何状态)。
2-示例
这是我刚刚用 Swift 编写的一个示例,它通过了在我的 iPad 上运行的测试,可以找到在我的 Mac Mini 上运行的服务。
所以,我们假设域名为local,服务类型为_http._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 类中的委托属性声明为 unowned(unsafe)。因此,我们还需要创建对委托实例的引用:
static let ns_deleg = MyNetServiceDelegate()
当我们要解析服务时,可以这样写:
ns.delegate = ns_deleg
ns.resolve(withTimeout: TimeInterval(10))
如果找到服务,稍后将调用委托实例(resolve() 是非阻塞方法),在这种情况下,它将打印主机名和端口。
这是我在 Xcode 输出窗口中得到的输出:
Mac-mini-de-Alexandre.local. 8080
最后,请注意,由于无主引用,编写以下代码将是错误的(委托实例将很快被释放):
// bad code -- do not write that -- only here to show a common mistake
ns.delegate = MyNetServiceDelegate()
ns.resolve(withTimeout: TimeInterval(10))
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 mDNS 查询,但使用 Apple Bonjour API,自动完成两组 mDNS 请求:一组使用 IPv4 到多播目标 224.0.0.251每个支持 IPv4 的网络接口,另一个支持 IPv6 的网络接口到多播目标 ff02::fb 的每个支持 IPv6 的接口。