3

所以,我一直在试图弄清楚为什么这不起作用,但我不知道。我已经设法从 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 没有收到数据包。此外,罗伯特是完全正确的,它在模拟器上工作的原因是由于环回。

4

3 回答 3

4

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

于 2010-01-12T18:59:41.430 回答
1

我需要将 INADDR_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;
于 2010-02-07T07:05:41.113 回答
0

我在尝试通过 tcpreplay 在我的 PC 上测试时遇到了类似的问题,Wireshark 在其他两台 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
于 2014-08-20T08:56:38.707 回答