发送到IPv6地址时,GCDAsyncUdpSocket立即closures
我通过UDP连接到由Bonjour公布的不同设备上的服务器。 当运行这个代码的iOS设备和服务器都在我们的wifinetworking上时,它工作得很好,因为bonjour服务parsing为我们的dhcp服务器发出的192.168.0.xxx地址。 但是,当它通过蓝牙广告,有时服务parsing为169.254.xxx.xxx(IPv4),在这种情况下,它工作得很好。 但有时它会解决到fe80 :: xxxx:xxxx:xxxx:xxxx(IPv6)在这种情况下套接字连接(我收到udpSocket:didConnectToAddress
callback),但立即closures当我尝试发送数据(我收到udpSocketDidClose:withError
callback立即在发送时)。
- (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; }
我的udpSocket:didConnectToAddress
方法调用发送,而我的其他callback基本上只是信息(NSLog)在这一点上。 这是传递给udpSocketDidClose:withError
的NSError udpSocketDidClose:withError
:
Error Domain=GCDAsyncUdpSocketErrorDomain Code=4 "Socket closed" UserInfo=0x2630c0 {NSLocalizedDescription=Socket closed}
不够用。
在解决这个问题的时候,我希望使用IPv6而不是强制IPv4来强制IPv4,这对我来说似乎是脆弱的。
fe80是链路本地IPv6地址。 您连接的机器必须具有多个networking接口 – 大部分都可以,例如以太网和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 };
并与地址结合,并转换为一个string看起来像这样: fe80::e2f8:47ff:fe23:5392%eth1
当parsingDNS时,包装一个sockaddr
结构体的NSData
包含这个信息。 然而,在你的代码中,你正在提取sin6_port
和sin6_addr
,然后把它们送回到没有sin6_flowinfo
(你不需要)和sin6_scope_id
(在这种情况下你这样做)的sin6_scope_id
中。
直接使用-[GCDAsyncUDPSocket connectToAddress:error:]
,使用你直接从你的parsing服务中获得的NSData
,你应该很好。
我所做的就是在套接字上调用setPreferIPv4
和setIPv6Enabled:FALSE
,如果DNS查找只返回一个IPv6地址,则会导致连接失败。 然后,在udpSocket:didNotConnect:
我检查了具体的错误( IPv6 has been disabled and DNS lookup found no IPv4 address(es).
如果连接失败,返回到我的setupConnection
方法,并再次尝试。 最终,DNS查询返回一个IPv4地址,并从那里顺利进行。
这不是最优雅的解决scheme,但它的工作原理。