代码之家  ›  专栏  ›  技术社区  ›  Jonathon

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

  •  3
  • Jonathon  · 技术社区  · 16 年前

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

    编辑:

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

    3 回复  |  直到 12 年前
        1
  •  4
  •   Robert S. Barnes Antoni    16 年前

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

        2
  •  1
  •   Jonathon    16 年前

    我需要将INADDR\U ANY更改为广播IP。。。

    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
  •   Ldvp    12 年前

    我在我的电脑上测试时遇到了类似的问题,通过Tcppreplay,Wireshark在另外两台电脑的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地址和两台电脑之间的路由器:

    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