我通过UDP连接到Bonjour公布的其他设备上的服务器。当运行此代码的iOS设备和服务器都在我们的wifi网络上时,它工作正常,因为bonjour服务解析为我们的dhcp服务器发出的192.168.0.xxx地址。然而,当它通过蓝牙做广告时,有时服务解析为169.254.xxx.xxx(IPv4),在这种情况下它可以正常工作。但有时它解析为fe80 :: xxxx:xxxx:xxxx:xxxx(IPv6),在这种情况下套接字连接(我收到udpSocket:didConnectToAddress
回调)但在我尝试发送数据时立即关闭(我收到{{1调用send后立即回调)。
udpSocketDidClose:withError
我的- (BOOL) setupConnection: (DNSSDService*) service
{
NSString *host = [service resolvedHost];
NSUInteger port = [service resolvedPort];
NSLog(@"in setupConnection: host %@ port %u",
host, port);
self.sock = [[GCDAsyncUdpSocket alloc]initWithDelegate:self
delegateQueue:dispatch_get_main_queue() ];
NSError *err = nil;
if (![self.sock connectToHost:host onPort:port error:&err]) {
NSLog(@"we goofed: %@", err);
return NO;
}
return YES;
}
方法调用send,此时我的其他回调基本上只是信息(NSLog)。这是传递给udpSocket:didConnectToAddress
的NSError:
udpSocketDidClose:withError
不太有用。
在解决这个问题时,我想让它与IPv6协同工作而不是强制IPv4 ...强迫IPv4对我来说似乎很脆弱。
答案 0 :(得分:1)
fe80是链路本地IPv6地址。您要连接的机器必须具有多个网络接口 - 大部分都是这样的,例如,以太网和WiFi。要完全特定IPv6地址,scope_id是必需的。这是来自:
的sin6_scope_id// IPv6 AF_INET6 sockets:
struct sockaddr_in6 {
u_int16_t sin6_family; // address family, AF_INET6
u_int16_t sin6_port; // port number, Network Byte Order
u_int32_t sin6_flowinfo; // IPv6 flow information
struct in6_addr sin6_addr; // IPv6 address
u_int32_t sin6_scope_id; // Scope ID
};
当与地址结合并转换为字符串时,如下所示:fe80::e2f8:47ff:fe23:5392%eth1
解析DNS后,包裹NSData
结构的sockaddr
包含此信息。但是,在您的代码中,您正在提取sin6_port
和sin6_addr
,然后将它们反馈给GCDAsyncUDPSocket
,而不是sin6_flowinfo
(您不需要)和sin6_scope_id
(在这种情况下,你这样做)。
使用直接从您的解析服务获得的-[GCDAsyncUDPSocket connectToAddress:error:]
直接使用NSData
,您应该很高兴。
答案 1 :(得分:0)
我所做的是在套接字上调用setPreferIPv4
和setIPv6Enabled:FALSE
,如果DNS查找仅返回IPv6地址,则会导致连接失败。然后,在udpSocket:didNotConnect:
我检查了该特定错误(IPv6 has been disabled and DNS lookup found no IPv4 address(es).
),如果连接因此失败,请返回我的setupConnection
方法并再次尝试。最终DNS查找返回一个IPv4地址,事情从那里顺利进行。
这不是最优雅的解决方案,但它确实有效。