发送到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:didConnectToAddresscallback),但立即closures当我尝试发送数据(我收到udpSocketDidClose:withErrorcallback立即在发送时)。

 - (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_portsin6_addr ,然后把它们送回到没有sin6_flowinfo (你不需要)和sin6_scope_id (在这种情况下你这样做)的sin6_scope_id中。

直接使用-[GCDAsyncUDPSocket connectToAddress:error:] ,使用你直接从你的parsing服务中获得的NSData ,你应该很好。

我所做的就是在套接字上调用setPreferIPv4setIPv6Enabled:FALSE ,如果DNS查找只返回一个IPv6地址,则会导致连接失败。 然后,在udpSocket:didNotConnect:我检查了具体的错误( IPv6 has been disabled and DNS lookup found no IPv4 address(es).如果连接失败,返回到我的setupConnection方法,并再次尝试。 最终,DNS查询返回一个IPv4地址,并从那里顺利进行。

这不是最优雅的解决scheme,但它的工作原理。