views:

946

answers:

2

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.

Any help is greatly appreciated, Thanks!

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.

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.

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

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.

+4  A: 

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.

Robert S. Barnes
Added the rest of the network code, and you were 100% correct about the loopback being why it worked on the simulator.
Argothian
+1  A: 

I needed to change the INADDR_ANY to the 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;
Argothian