user332000
user332000

Reputation: 187

Using UDP with a specific IP address

I'm trying to write an iOS app that communicates with a device using UDP. The documentation for the device says that it communicates on a specific IP address, 224.4.0.1, and a specific port number.

I'm having a hard time trying to figure out the right way to send and receive UDP packets on a specific IP address. Is this even possible?

I wrote some test code that can send a UDP packet from one iOS device and listen for it on another. The code I wrote works as long as I specify the sending IP address as INADDR_BROADCAST and the receiving address as INADDR_ANY. As soon as I change one or both of them to the 224.4.0.1 address, it no longer works (the sender doesn't report any error, but the receiver never gets the packet).

My understanding is that 224.4.0.1 is within the range of IP addresses reserved for multicast. I made sure to set the SO_REUSEADDR option on the listening socket before binding it.

The device I'm trying to ultimately use is on my network, but so far has not responded to anything I've tried to send. I'd like to get my test case working with two iPhones before I worry about the specifics of the device.

I'm using Berkeley sockets directly and/or wrapping them with CFSocket. I've successfully used these interfaces with TCP on earlier projects, so I'm pretty comfortable with how they work in general.

Here's the code that sets up the sending socket:

    struct sockaddr_in sin_send;
    memset(&sin_send, 0, sizeof(sin_send));
    sin_send.sin_len = sizeof(sin_send);
    sin_send.sin_family = AF_INET;
    sin_send.sin_port = htons(PORT_SEND);
    //sin_send.sin_addr.s_addr = htonl(INADDR_BROADCAST);
    inet_aton([@"224.4.0.1" UTF8String], &sin_send.sin_addr);

    int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

    int reuse = 1;
    NSLog(@"reuse %d", setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*) &reuse, sizeof(reuse)));
    NSLog(@"connect %d", connect(sock, (const struct sockaddr *) &sin_send, sizeof(sin_send)));

    _sendSocket = CFSocketCreateWithNative(kCFAllocatorDefault, sock, kCFSocketWriteCallBack, socketWriteCallBack, &context);

    if (!CFSocketIsValid(_sendSocket)) {
        NSLog(@"Send socket is not valid");
        return;
    }

    // Disable "nagling"
    CFSocketNativeHandle ssock = CFSocketGetNative(_sendSocket);
    int i = -1;
    setsockopt(ssock, IPPROTO_TCP, TCP_NODELAY, (char*) &i, sizeof(int));

    // Enable broadcast
    int broadcast = 1;
    setsockopt(ssock, SOL_SOCKET, SO_BROADCAST, (char*) &broadcast, sizeof(int));

This is the receiving socket:

    struct sockaddr_in sin_read;
    memset(&sin_read, 0, sizeof(sin_read));
    sin_read.sin_len = sizeof(sin_read);
    sin_read.sin_family = AF_INET;
    sin_read.sin_port = htons(PORT_READ);
    //sin_read.sin_addr.s_addr = INADDR_ANY;
    inet_aton([@"224.4.0.1" UTF8String], &sin_read.sin_addr);

    int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

    int reuse = 1;
    NSLog(@"reuse %d", setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*) &reuse, sizeof(reuse)));
    NSLog(@"bind %d", bind(sock, (const struct sockaddr *) &sin_read, sizeof(sin_read)));

    int broadcast = 1;
    setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*) &broadcast, sizeof(int));

    int flags;
    flags = fcntl(sock, F_GETFL);
    NSLog(@"fcntl %d", fcntl(sock, F_SETFL, flags | O_NONBLOCK));

    _readSocket = CFSocketCreateWithNative(kCFAllocatorDefault, sock, kCFSocketAcceptCallBack | kCFSocketDataCallBack | kCFSocketReadCallBack, socketReadCallBack, &context);

Thanks, Frank

Upvotes: 0

Views: 2787

Answers (1)

user332000
user332000

Reputation: 187

The solution was to bind my listening socket to INADDR_ANY, but then use setsockopt with the IP_ADD_MEMBERSHIP option to make sure it listens to the desired IP address.

    struct ip_mreq mreq;
    memset(&mreq, 0, sizeof(mreq));
    inet_aton([@"224.4.0.1" UTF8String], &mreq.imr_multiaddr);
    mreq.imr_interface.s_addr = INADDR_ANY;
    setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));

Upvotes: 2

Related Questions