为什么没有收到(UDP组播)数据包?

所以,我一直试图弄清楚为什么这不起作用,但我没有线索。 我已经设法从iPhone发送数据包并在我的Mac上接收它们。 根据tcpdump我的mac正确发送数据包。 另外,如果我在模拟器中运行它,它工作正常。 这让我相信这是一个网络问题,但我不知道那可能是什么,所以我希望(!)它是下面的东西。

CFSocketContext socketContext = {0, self, NULL, NULL, NULL}; advertiseSocket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_DGRAM, IPPROTO_UDP, kCFSocketDataCallBack, (CFSocketCallBack)&advertiseCallBack, &socketContext); int yes = 1; setsockopt(CFSocketGetNative(advertiseSocket), SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes)); u_char loop = 0; setsockopt(CFSocketGetNative(advertiseSocket), IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)); unsigned char ttl = 64; setsockopt(CFSocketGetNative(advertiseSocket), IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)); struct sockaddr_in addressData; memset(&addressData, 0, sizeof(addressData)); addressData.sin_len = sizeof(addressData); addressData.sin_family = AF_INET; addressData.sin_port = htons(broadcastPort); addressData.sin_addr.s_addr = htonl(INADDR_ANY); NSData *address = [NSData dataWithBytes:&addressData length:sizeof(addressData)]; CFSocketSetAddress(advertiseSocket, (CFDataRef)address); struct ip_mreq mreq; mreq.imr_multiaddr.s_addr = inet_addr(broadcastIP); mreq.imr_interface.s_addr = INADDR_ANY; setsockopt(CFSocketGetNative(advertiseSocket), IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); // set up the run loop sources for the sockets CFRunLoopRef cfrl = CFRunLoopGetCurrent(); CFRunLoopSourceRef source = CFSocketCreateRunLoopSource(kCFAllocatorDefault, advertiseSocket, 0); CFRunLoopAddSource(cfrl, source, kCFRunLoopCommonModes); CFRelease(source); 

编辑:

上面的代码是针对iPhone上的接收端。

我正在使用下面的java代码与iPhone交谈(这是浓缩的)。 iPhone不会收到发送的数据包,但mac会收到iPhone发送的数据包。

 String ident = broadcastKey; MulticastSocket socket = new MulticastSocket(broadcastPort); InetAddress group = InetAddress.getByName(broadcastIP); socket.joinGroup(group); socket.setTimeToLive(64); socket.setLoopbackMode(true); byte [] key = ident.getBytes("UTF-16BE"); byte [] request = Arrays.copyOf(key,key.length+2); System.out.println(Arrays.toString(request)); DatagramPacket packet = new DatagramPacket(request, request.length, group, broadcastPort); socket.send(packet); byte [] res = new byte[1024]; packet = new DatagramPacket(res, res.length); socket.receive(packet); System.out.println(Arrays.toString(res)); 

这是我用来从iPhone发送的代码

 NSData *toSend = [broadcastIdentifier dataUsingEncoding:NSUTF16BigEndianStringEncoding]; struct in_addr localInterface; struct sockaddr_in groupSock; int sd; int datalen; sd = socket(AF_INET, SOCK_DGRAM, 0); memset((char *) &groupSock, 0, sizeof(groupSock)); groupSock.sin_family = AF_INET; groupSock.sin_addr.s_addr = inet_addr(broadcastIP); groupSock.sin_port = htons(broadcastPort); localInterface.s_addr = INADDR_ANY; setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, (char *)&localInterface, sizeof(localInterface)); sendto(sd, [toSend bytes], [toSend length], 0, (struct sockaddr*)&groupSock, sizeof(groupSock)); 

所以,为了澄清这个问题,我想知道为什么iPhone没有收到数据包。 此外,罗伯特完全正确,它在模拟器上工作的原因是由于环回。

我假设这是recv方面…在脸上多播套接字设置看起来很好。 你说它适用于模拟器,但不是真正的网络,对吗? 存在一个问题,即您的网络设备,特别是任何路由器,可能还需要明确设置其他设备以允许转发广播和/或多播数据包。 默认情况下,这些类型的数据包通常会丢弃在网络边缘。 这是另一个长镜头 – 如果你在同一台机器上运行发送器和接收器并关闭IP_MULTICAST_LOOP,那么你将不会获得任何数据包,因为它禁用了组播环回接口。 如果没有关于您的设置的更多信息和/或看到更多代码,我就能想到这一切。

我需要将INADDR_ANY更改为broadcastIP …

 struct sockaddr_in addressData; memset(&addressData, 0, sizeof(addressData)); addressData.sin_len = sizeof(addressData); addressData.sin_family = AF_INET; addressData.sin_port = htons(broadcastPort); addressData.sin_addr.s_addr = inet_addr(broadcastIP); NSData *address = [NSData dataWithBytes:&addressData length:sizeof(addressData)]; if (kCFSocketSuccess != CFSocketSetAddress(advertiseSocket, (CFDataRef)address)) { [self stopBeforeStart]; [self connectionFailed]; return; } struct ip_mreq mreq; mreq.imr_multiaddr.s_addr = inet_addr(broadcastIP); mreq.imr_interface.s_addr = INADDR_ANY; 

我有一个类似的问题试图在我的电脑上测试,通过tcpreplay,Wireshark在其他2台PC的A和B之间记录的一些数据交换。所以我这样修改旧记录:

 tcprewrite --pnat=A-IP:loIP,B-IP:loIP -i oldrecordfile -o newrecordfile 

接着

 tcpreplay -T nano --verbose -i lo newrecordfile 

但是我的应用程序recv()失败了。

这个问题可能与tcpreplay的环回限制有关,所以我决定在用tcprewrite用新的IP重新生成合适的记录文件后,将数据从PC重新发送到PC。 此时tcpdump在我的接收端显示了例外情况,但程序recv()总是失败。

最后我发现原因是旧的MAC地址和两台PC之间存在路由器:

 tcprewrite --pnat=oldA-IP:newA-IP,oldB-IP:newB-IP --enet-smac=newA-MAC,newA-MAC --enet-dmac=newB-MAC,newB-MAC -i oldrecordfile -o newrecordfile