Bonjour:按名称搜索服务
Bonjour: Search for a service by name
我有一个应用程序需要搜索和解析 Bonjour 广告的服务,该服务的名称是预先知道的。我发现的大多数与服务发现相关的 Bonjour 示例的结构大致如下:
- 调用
browse
检测给定类型的所有服务(例如,这可能是 _http._tcp
)
- 对于找到的每个服务,调用
serviceFound
。此处报告服务名称
- 对找到的每个服务调用
resolve
- 对于每个解析的服务,
serviceResolved
被调用
Bonjour 是否可以跳过 "discovery" 阶段,因为我事先知道我要解析的服务的名称?我可以只检测并解析已知名称的服务吗?
1- 答案
是的,如果您已经知道服务的名称,则可以从第 3 步开始。这是因为此步骤是通过 DNS 查找 SRV 记录执行的,其中服务名称 发送到众所周知的多播地址 。因此,进行此调用不需要先前的信息,并且 mDNS 响应器必须是无状态的,因为底层 DNS 协议是无状态的(每个响应都绑定到一个唯一的请求 - 多个请求之间没有状态维护)。
2- 例子
这是我刚刚用 Swift 编写的示例,它已通过 运行ning 在我的 iPad 上的测试,可以在我的 运行ning 上找到服务Mac 迷你。
因此,我们假设域名为 local
,服务类型为 _http._tcp
,服务名称为 myservice
,运行ning 在主机 Mac-mini 上-de-Alexandre.local 并监听 TCP 端口 8080.
为了跟踪有关服务的信息,例如它的主机名和 TCP 端口,我们定义了一个 class 来实现 NetServiceDelegate 协议:
class MyNetServiceDelegate : NSObject, NetServiceDelegate {
public func netServiceDidResolveAddress(_ sender: NetService) {
print(sender.hostName!, sender.port)
}
}
这个新的 class 将用于实例化 NetService 实例的委托。
因此,我们创建了一个 NetService 实例,该实例对应于我们已知的服务,我们将其长期存储在一些主要 class:[=22 的静态常量 属性 中=]
static let ns = NetService(domain: "local.", type: "_http._tcp.", name: "myservice")
它是长期存储的,因为在我们找到我们的服务之前它不能被释放。
请注意 class NetService 中的委托 属性 声明为 unowned(不安全)。因此,我们还需要创建对委托实例的引用:
static let ns_deleg = MyNetServiceDelegate()
当我们要解析服务时,我们可以这样写:
ns.delegate = ns_deleg
ns.resolve(withTimeout: TimeInterval(10))
如果找到服务,稍后将调用委托实例(resolve()
是非阻塞方法),在这种情况下,它将打印主机名和端口。
这是我在 Xcode 输出中得到的输出 window:
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 服务是 运行ning,您将获得主机名和端口。使用我的示例,您将获得以下内容:
0 0 8080 Mac-mini-de-Alexandre.local.
因此,在尝试使用我在此处编写的 Swift 代码之前,只需检查您的服务是否已使用此 shell 命令正确宣布。
最后,请注意这个基于 dig 的命令只在每个 IPv4 网络接口上进行一次 IPv4 mDNS 查询,但是使用 Apple Bonjour API,会自动完成两组 mDNS 请求:一组使用 IPv4 到每个支持 IPv4 的网络接口上的多播目标 224.0.0.251,另一个带有 IPv6 的多播目标 ff02::fb 在每个支持 IPv6 的接口上。
我有一个应用程序需要搜索和解析 Bonjour 广告的服务,该服务的名称是预先知道的。我发现的大多数与服务发现相关的 Bonjour 示例的结构大致如下:
- 调用
browse
检测给定类型的所有服务(例如,这可能是_http._tcp
) - 对于找到的每个服务,调用
serviceFound
。此处报告服务名称 - 对找到的每个服务调用
resolve
- 对于每个解析的服务,
serviceResolved
被调用
Bonjour 是否可以跳过 "discovery" 阶段,因为我事先知道我要解析的服务的名称?我可以只检测并解析已知名称的服务吗?
1- 答案
是的,如果您已经知道服务的名称,则可以从第 3 步开始。这是因为此步骤是通过 DNS 查找 SRV 记录执行的,其中服务名称 发送到众所周知的多播地址 。因此,进行此调用不需要先前的信息,并且 mDNS 响应器必须是无状态的,因为底层 DNS 协议是无状态的(每个响应都绑定到一个唯一的请求 - 多个请求之间没有状态维护)。
2- 例子
这是我刚刚用 Swift 编写的示例,它已通过 运行ning 在我的 iPad 上的测试,可以在我的 运行ning 上找到服务Mac 迷你。
因此,我们假设域名为 local
,服务类型为 _http._tcp
,服务名称为 myservice
,运行ning 在主机 Mac-mini 上-de-Alexandre.local 并监听 TCP 端口 8080.
为了跟踪有关服务的信息,例如它的主机名和 TCP 端口,我们定义了一个 class 来实现 NetServiceDelegate 协议:
class MyNetServiceDelegate : NSObject, NetServiceDelegate {
public func netServiceDidResolveAddress(_ sender: NetService) {
print(sender.hostName!, sender.port)
}
}
这个新的 class 将用于实例化 NetService 实例的委托。
因此,我们创建了一个 NetService 实例,该实例对应于我们已知的服务,我们将其长期存储在一些主要 class:[=22 的静态常量 属性 中=]
static let ns = NetService(domain: "local.", type: "_http._tcp.", name: "myservice")
它是长期存储的,因为在我们找到我们的服务之前它不能被释放。
请注意 class NetService 中的委托 属性 声明为 unowned(不安全)。因此,我们还需要创建对委托实例的引用:
static let ns_deleg = MyNetServiceDelegate()
当我们要解析服务时,我们可以这样写:
ns.delegate = ns_deleg
ns.resolve(withTimeout: TimeInterval(10))
如果找到服务,稍后将调用委托实例(resolve()
是非阻塞方法),在这种情况下,它将打印主机名和端口。
这是我在 Xcode 输出中得到的输出 window:
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 服务是 运行ning,您将获得主机名和端口。使用我的示例,您将获得以下内容:
0 0 8080 Mac-mini-de-Alexandre.local.
因此,在尝试使用我在此处编写的 Swift 代码之前,只需检查您的服务是否已使用此 shell 命令正确宣布。
最后,请注意这个基于 dig 的命令只在每个 IPv4 网络接口上进行一次 IPv4 mDNS 查询,但是使用 Apple Bonjour API,会自动完成两组 mDNS 请求:一组使用 IPv4 到每个支持 IPv4 的网络接口上的多播目标 224.0.0.251,另一个带有 IPv6 的多播目标 ff02::fb 在每个支持 IPv6 的接口上。