6 responses

  1. Tim DeBenedictis
    March 9, 2010

    I have not tested your code yet, but it appears to solve at least *part* of a problem that I’ve got.

    The other part is, I need to actually send a UDP broadcast message to the WiFi broadcast address, from my iPhone, and listen for a response. I’ve found a bunch of examples, mostly linux-based, but all of them fail on the iPhone. Here’s my code – what fails is listed below it:

    {
    struct sockaddr_in saddr = { 0 };
    struct sockaddr_in raddr = { 0 };

    int sock;
    int on = 1;

    // open socket
    if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
    {
    return ( TEL_SOCKET_OPEN_ERROR );
    }

    // enable udp broadcast
    if ( setsockopt ( sock, SOL_SOCKET, SO_BROADCAST, &on, sizeof on ) < 0 )
    {
    close ( sock );
    return ( TEL_SOCKET_OPEN_ERROR );
    }

    // setup source port / destination address

    saddr.sin_len = sizeof ( saddr );
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(4031);
    saddr.sin_addr.s_addr = inet_addr ( "10.255.255.255" ); // htonl(INADDR_BROADCAST);

    // bind outgoing port

    if (bind(sock, (sockaddr *) &saddr, sizeof(saddr)) < 0)
    {
    perror ( "bind" );
    // return ( TEL_SOCKET_OPEN_ERROR );
    }

    raddr.sin_len = sizeof ( raddr );
    raddr.sin_family = AF_INET;
    raddr.sin_port = htons(4031);
    raddr.sin_addr.s_addr = inet_addr ( "10.255.255.255" ); // htonl(INADDR_BROADCAST);

    const char *buf = "skyfi?";
    if (sendto(sock, buf, strlen(buf), 0, (sockaddr *) &raddr, sizeof(raddr)) < 0)
    {
    perror ( "sendto" );
    return ( TEL_SOCKET_WRITE_ERROR );
    }

    sleep ( 1 );

    char reply[256] = { 0 };
    int replen = 0;

    replen = recvfrom ( sock, reply, sizeof ( reply ), 0, NULL, 0 );
    if ( replen < 1 )
    {
    return ( TEL_SOCKET_READ_ERROR );
    }

    return ( TEL_NO_ERROR );
    }

    The above code always fails at the sendto() call, and errno is set to 49 (EADDRNOTAVAIL). perror() gives me sendto: Can't assign requested address. I happen to know that the WiFi network broadcast address is 10.255.255.255 – that's why that value is hard coded. But I still can't send to it. Arrrgh!

    Incidentally, your code (if it works) would solve the other part of my problem, which is finding the broadcast address in the first place.

    Thanks in advance,

    -Tim

    • Nick
      March 10, 2010

      I can’t stress enough staying in ObjC-land for iPhone development. I had to drop to C to get the netmask, etc only because the NSHost API isn’t present in the iPhone SDK.

      And luckily, someone else has written a nice little UDP library for it as well: http://code.google.com/p/cocoaasyncsocket

      Using that, in some of my code (which this post was also written for), sending a UDP broadcast is as simple as:


      // initialization
      socket = [[AsyncUdpSocket alloc] initWithDelegate:self];
      NSError *error = nil;
      if (![socket enableBroadcast:YES error:&error]) {
      NSLog(@"Failed to enable broadcast: '%@'", error);
      }

      and


      // use
      unsigned char bytes[] = {0};
      NSData *data = [NSData dataWithBytes:bytes length:sizeof(bytes)];
      if (![socket sendData:data toHost:broadcastHost port:33848 withTimeout:2.0 tag:DISCOVERY_TAG]) {
      NSLog(@"Failed to send data to broadcast host '%@'", broadcastHost);
      return;
      }

      // casually do other stuff, the UDP is asynchronous
      [socket receiveWithTimeout:10.0 tag:DISCOVERY_TAG];

      Finally, a callback like:


      - (BOOL)onUdpSocket:(AsyncUdpSocket *)sock didReceiveData:(NSData *)data withTag:(long)tag fromHost:(NSString *)host port:(UInt16)port {
      if (DISCOVERY_TAG != tag) {
      return NO;
      }

      // queue up work unit for data received
      // ....
      [[NSNotificationCenter defaultCenter] postNotificationName:@"udp.work.recv" object:self];
      [socket receiveWithTimeout:10.0 tag:DISCOVERY_TAG];
      return YES;
      }

  2. Tim DeBenedictis
    March 23, 2010

    Thanks, Nick. It appears to be working now. After much experimentation, the solution to my original problem was to bind the UDP socket to the WiFi interface address, not to the broadcast address for that interface – and your code for finding the WiFI interface address solved it. Actually, I managed to do it with just the getifaddrs() call you had inside ipAddressForInterface[].

    I’m not sure if I agree with your assessment that it’s always best to work on objective-C. This particular bit of code that I’m working on has to be portable to OS X, Linux, and WIn32, as well as iPhone OS, so keeping it all in C is really the only option. And if it works OK on all 4 platforms … why not?

    Anyhow, thanks again for the help. Greatly appreciated.

  3. Nick
    March 23, 2010

    The higher level the better, as long as the frameworks are sound and everything. But yeah, your choice is kind of made for you if you have multi-platform requirements like that!

    Glad I could help.

  4. Oliver
    June 29, 2010

    Hi,
    I have a similar problem.

    I cant connect to the address…why?
    Im using the AsyncUdpSocket class.

    int port = 8080;
    NSString *add_tmp = @”192.168.123.100″;
    NSData *add = [add_tmp dataUsingEncoding:NSUTF8StringEncoding];

    if(![listenSocket bindToAddress:addr error:&err]){
    printf(“ERROR”)
    };

    What I make wrong?
    Can anyone help me please?

  5. Nick
    June 29, 2010

    Instead of:

    printf(“ERROR”);

    Trying using the error that should have been filled with useful information:

    NSLog(@”Error occurred: ‘%@'”, err);

Leave a Reply

 

 

 

*

Back to top
mobile desktop