为什么(UDP多播)数据包没有被接收?

时间:2022-10-11 14:00:14

So, I've been trying to figure out exactly why this isn't working but I haven't a clue. I've managed to send packets from the iPhone and receive them on my Mac. And according to tcpdump my mac is correctly sending the packets. Additionally, if I run this in the simulator it works fine. This leads me to believe that it's a network problem, but I have no idea what that could be, so I was hoping(!) that it's something below.

所以,我一直在试图弄清楚为什么这不起作用,但我一点头绪都没有。我成功地从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);

EDIT:

编辑:

The code above is for the receiving end on the iPhone.

上面的代码用于iPhone的接收端。

I'm using the java code below to talk to the iPhone (this is condensed). The packet that gets sent is not received by the iPhone but the mac receives the packet that the iPhone sends.

我正在使用下面的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));

This is the code I'm using to send from the iPhone

这是我用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));

So, to clarify the question, I want to know why the iPhone isn't receiving the packet. Also, Robert is completely right that the reason it was working on the simulator is due to the loopback.

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

3 个解决方案

#1


4  

I'm assuming that this is the recv side... On the face the multicast socket setup looks fine. You say it works on the simulator but not a real network, correct? There is an issue that your network equipment, specifically any routers, possibly other equipment as well may need to be explicitly set up to allow forwarding of broadcast and / or multicast packets. These kinds of packet are usually dropped at the edge of networks by default. Here's another long shot - if you run both the sender and receiver on the same machine and turn off IP_MULTICAST_LOOP then you won't get any packets as it disables the multicast loopback interface. That's all I can think of without more info on your setup and / or seeing a bit more code.

我假设这是矩形。在表面上,多播套接字安装看起来很好。你说它可以在模拟器上运行但不是真正的网络,对吧?有一个问题,您的网络设备,特别是任何路由器,可能也需要显式地设置其他设备,以允许广播和/或多播数据包的转发。这些类型的数据包通常在默认情况下被丢弃在网络的边缘。这是另一个很长的尝试——如果您在同一台机器上同时运行发送方和接收方并关闭ip_multi - ast_loop,那么您将不会得到任何数据包,因为它会禁用多播环回接口。这就是我所能想到的,没有更多关于您的设置和/或看到更多代码。

#2


1  

I needed to change the INADDR_ANY to the broadcastIP...

我需要把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;

#3


0  

I had a similar problem trying to test on my PC, by tcpreplay, some data exchange recorded by Wireshark between other 2 PC's A and B. So I adapt the old record this way:

我在我的电脑上也遇到了类似的问题,通过tcpreplay, Wireshark在另外两台电脑的a和b之间记录了一些数据交换,所以我将旧记录调整为:

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

and then

然后

tcpreplay -T nano --verbose -i lo newrecordfile

But my application recv() fail.

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

The problem could be related to loopback limit of tcpreplay, so I decide to resend the data from PC to PC, after regenerating suitable recordfile with new IP's by tcprewrite. At this point tcpdump showed things as excepted on my receiving-end, but the program recv() always fail.

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

Finally I found out the reason of that were the old MAC addresses and the presence of a router between the two PC's:

最后我发现原因是旧的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

#1


4  

I'm assuming that this is the recv side... On the face the multicast socket setup looks fine. You say it works on the simulator but not a real network, correct? There is an issue that your network equipment, specifically any routers, possibly other equipment as well may need to be explicitly set up to allow forwarding of broadcast and / or multicast packets. These kinds of packet are usually dropped at the edge of networks by default. Here's another long shot - if you run both the sender and receiver on the same machine and turn off IP_MULTICAST_LOOP then you won't get any packets as it disables the multicast loopback interface. That's all I can think of without more info on your setup and / or seeing a bit more code.

我假设这是矩形。在表面上,多播套接字安装看起来很好。你说它可以在模拟器上运行但不是真正的网络,对吧?有一个问题,您的网络设备,特别是任何路由器,可能也需要显式地设置其他设备,以允许广播和/或多播数据包的转发。这些类型的数据包通常在默认情况下被丢弃在网络的边缘。这是另一个很长的尝试——如果您在同一台机器上同时运行发送方和接收方并关闭ip_multi - ast_loop,那么您将不会得到任何数据包,因为它会禁用多播环回接口。这就是我所能想到的,没有更多关于您的设置和/或看到更多代码。

#2


1  

I needed to change the INADDR_ANY to the broadcastIP...

我需要把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;

#3


0  

I had a similar problem trying to test on my PC, by tcpreplay, some data exchange recorded by Wireshark between other 2 PC's A and B. So I adapt the old record this way:

我在我的电脑上也遇到了类似的问题,通过tcpreplay, Wireshark在另外两台电脑的a和b之间记录了一些数据交换,所以我将旧记录调整为:

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

and then

然后

tcpreplay -T nano --verbose -i lo newrecordfile

But my application recv() fail.

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

The problem could be related to loopback limit of tcpreplay, so I decide to resend the data from PC to PC, after regenerating suitable recordfile with new IP's by tcprewrite. At this point tcpdump showed things as excepted on my receiving-end, but the program recv() always fail.

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

Finally I found out the reason of that were the old MAC addresses and the presence of a router between the two PC's:

最后我发现原因是旧的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