我已使用 Bonjour API 注册了一条记录。现在我想知道我刚刚发布的记录的内容。我通过指定 NULL 主机名创建它,意思是“使用守护进程的默认值”,但我找不到简单的方法来查询它是什么!
使用 avahi,这很简单:我调用
avahi_client_get_host_name()
来获取机器主机名的起始值。
对于 avahi 和 Bonjour,SRV 记录的值可以在注册生命周期内更改 - 如果注册是使用 NULL 主机名完成的,则记录的主机名会在必要时自动更新。我在这里想要的只是一种在执行注册时获取主机名的initial值的方法。
请注意,在我的 Snow Leopard 测试机器上,默认多播主机名与 gethostname(2)
中的机器名称
不相同。
我能想到的四种解决方案:
strings(3)
搜索,并在我的地址空间中发现了多播主机名的四个实例,但这可能是巧合,因为该名称用于其他用途。即使我想要的字符串在我的进程中的某个地方,我也找不到 API 来正常地检索它。重新实现uDNS.c中的逻辑。它从以下组合中获取机器的主机名:
gethostname(2)
或等效物重新实现该逻辑似乎不可行。
目前,我倾向于进行查找以获取初始 SRV 注册的值,但这似乎并不理想。正确的解决办法是什么?
我需要这样做。您想要使用 ConvertDomainNameToCString 宏(包含在 mDNSEmbeddedAPI.h 中),并且需要访问核心 mDNS 结构。
以下是获取已注册的确切 Bonjour/Zeroconf 主机名的方法:
char szHostname[512];
extern mDNS m;
ConvertDomainNameToCString(&m.MulticastHostname, szHostname);
希望这对您有帮助。
为了记录,我选择了 (4),获取机器的配置以将守护进程正在使用的主机名组合在一起,而无需查询它。
static char* getBonjourDefaultHost()
{
char* rv = 0;
#ifdef __APPLE__
CFStringRef name = SCDynamicStoreCopyLocalHostName(NULL);
if (name) {
int len = CFStringGetLength(name);
rv = new char[len*4+1];
CFStringGetCString(name, rv, len*4+1, kCFStringEncodingUTF8);
CFRelease(name);
}
// This fallback is completely incorrect, but why should we care...
// Mac does something crazy like <sysctl hw.model>-<MAC address>.
if (!rv)
rv = GetHostname(); // using gethostname(2)
#elif defined(WIN32)
CHAR tmp[256+1];
ULONG namelength = sizeof(tmp);
DynamicFn<BOOL (WINAPI*)(COMPUTER_NAME_FORMAT,LPSTR,LPDWORD)>
GetComputerNameExA_("Kernel32", "GetComputerNameExA");
if (!GetComputerNameExA_.isValid() ||
!(*GetComputerNameExA_)(ComputerNamePhysicalDnsHostname, tmp, &namelength))
tmp[0] = 0;
// Roughly correct; there's some obscure string cleaning mDNSResponder does
// to tidy up funny international strings.
rv = tmp[0] ? strdup(tmp) : strdup("My Computer");
#elif defined(__sun)
// This is exactly correct! What a relief.
rv = GetHostName();
#else
#error Must add platform mDNS daemon scheme
#endif
return rv;
}
scutil
命令获取本地 (Bonjour) 主机名:
scutil --get LocalHostName
kSCPropNetLocalHostName
框架使用 SystemConfiguration
键获得此功能。
我知道这个问题已经得到了回答,但我根据皮尔兹在答案后半部分所说的内容,去了解如何使用 SystemConfiguration 框架来做到这一点。
这就是我所做的工作,我想这可能会节省其他人在谷歌上搜索的时间: 在斯威夫特:
import SystemConfiguration
let store = SCDynamicStoreCreate(nil, "ipmenu" as CFString, nil, nil )
if let hostNames = SCDynamicStoreCopyValue(store, "Setup:/Network/HostNames" as CFString){
if let hostName:String = (hostNames[kSCPropNetLocalHostName]) as? String {
print("Host name:\(hostName)")
}
}
我尝试了这里列出的一些方法,特别是 SCDynamicStoreCreate( CFSTR("ipmenu"),...); SCDynamicStoreCopyValue( ..., CFSTR("设置:/网络/主机名")); CFDictionaryGetValue( ..., kSPCropNetLocalHostName),但发现 C 语言中的 CF API 确实很混乱。(我避免将整个内容拼写为代码片段,以免诱使某人将其粘贴到他们的应用程序中。它不我觉得不可靠。)我不能排除返回的一些 CFPropertyList 实际上是数组而不是 CFDictionary/CFString。最后,经过一番摸索之后,我返回的字符串仍然是错误的,因为它需要在其与 DNSService 在本地发布的内容匹配之前附加“.local”。所以,我觉得这个问题还没有得到彻底解决。
虽然原发帖者特别询问如何在不执行服务请求的情况下执行此操作,但我认为最好的方法实际上是执行服务请求并从 DNSResponder 本身获取字符串 - 您可以使用 DNSServiceBrowse() 浏览您的自定义计算机主要网络接口上的注册表名称。浏览回调应调用 DNSServiceResolve(),然后您将正确的值作为解析回调的第 6 个参数。
剩下的唯一问题是找到要使用的网络接口。虽然我希望条条大路通罗马,但您可以使用 Apple 平台上的 nw_path_monitor 找出主要使用哪个外部网络接口。您需要将接口类型 (nw_path_uses_interface_type) 与 nw_path_enumerate_interfaces 给出的接口类型进行匹配,以选择正确的接口类型。该接口上的 nw_interface_get_index 提供与 DNSServiceBrowse 的第三个参数一起使用的索引。