7

I need to be able to send out a UDP message and also receive one in order to discover SSDP devices on the network from the iPhone.

I know that I need to send the packet to the multicast address and my HTTP request needs to look something like this:

M-SEARCH * HTTP/1.1
Host: 239.255.255.250:1900
Man: ssdp:discover
Mx: 3
ST: "urn:schemas-upnp-org:device:InternetGatewayDevice:1"

From reading the docs it appears that I can do all this with CFNetwork and despite reading (and re-reading the docs) I am struggling to get started. Can anyone recommend and tutorials or code snippets to get me over the initial learning hump?

I've got the CFNetwork programming guide:

http://developer.apple.com/mac/library/documentation/Networking/Conceptual/CFNetwork/CFNetwork.pdf

and Beej's Guide to Network programming Using Internet Sockets:

http://beej.us/guide/bgnet/

Thanks

Dave

P.S.

I am unable to use any of the 3rd party libraries and frameworks in this instance.

Magic Bullet Dave
  • 9,006
  • 10
  • 51
  • 81

3 Answers3

4

I have the following code for SSDP search in my app:

-(void)discoverDevices {
ssdpSock = [[AsyncUdpSocket alloc] initWithDelegate:self];
[ssdpSock enableBroadcast:TRUE error:nil];
NSString *str = @"M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nMan: \"ssdp:discover\"\r\nST: mydev\r\n\r\n";    
[ssdpSock bindToPort:0 error:nil];
[ssdpSock joinMulticastGroup:@"239.255.255.250" error:nil];
[ssdpSock sendData:[str dataUsingEncoding:NSUTF8StringEncoding] 
         toHost: @"239.255.255.250" port: 1900 withTimeout:-1 tag:1];
[ssdpSock receiveWithTimeout: -1 tag:1];
[NSTimer scheduledTimerWithTimeInterval: 5 target: self 
           selector:@selector(completeSearch:) userInfo: self repeats: NO]; }


-(void) completeSearch: (NSTimer *)t {
NSLog(@"%s",__FUNCTION__);
[ssdpSock close];
ssdpSock = nil;}

- (BOOL)onUdpSocket:(AsyncUdpSocket *)sock didReceiveData:(NSData *)data withTag:(long)tag fromHost:(NSString *)host port:(UInt16)port{
NSLog(@"%s %d %@ %d",__FUNCTION__,tag,host,port);
NSString *aStr = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
NSLog(@"%@",aStr);}

It uses the AsyncUdpSocket from CocoaAsyncSocket.

Avi Shukron
  • 6,088
  • 8
  • 50
  • 84
savvybud
  • 66
  • 1
  • 4
  • 4
    Hi Savvybud, looks ok, but from the top of my head (it was a while ago I did this) I think your problem lies with the bindToPort. I am pretty sure this is the port that return messages will be sent and should not be 1900 as this is reserved for multicasts. If you set this to zero then the system will allocate one and it should work. sendData looks fine. – Magic Bullet Dave Jan 28 '11 at 07:42
4

I have used AsyncUdpSocket successfully to run SSDP Discovery and find controllers. Here are my code snippets:

Initialize and setup the socket:

//  AsyncUdpSocket *ssdpSock = [[AsyncUdpSocket alloc] initWithDelegate:self];
    AsyncUdpSocket *ssdpSock = [[AsyncUdpSocket alloc] initIPv4];
    [ssdpSock setDelegate:self];

Note the first line commented out. I found on the AsyncUdpSocket forums some issues with duplicates. I don't think I was facing them but I did it anyhow.

I added error checking, and it was useful because during my debugging I wasn't closing sockets and I started getting socket setup failures:

NSError *socketError = nil;

    if (![ssdpSock bindToPort:1900 error:&socketError]) {
        NSLog(@"Failed binding socket: %@", [socketError localizedDescription]);
        return statusController;
    }

    if(![ssdpSock joinMulticastGroup:@"239.255.255.250" error:&socketError]){
        NSLog(@"Failed joining multicast group: %@", [socketError localizedDescription]);
        return statusController;
    }

    if (![ssdpSock enableBroadcast:TRUE error:&socketError]){
        NSLog(@"Failed enabling broadcast: %@", [socketError localizedDescription]);
        return statusController;
    }

    [ssdpSock sendData:[self.discoverControllerString dataUsingEncoding:NSUTF8StringEncoding]
                toHost:@"239.255.255.250"
                  port:1900
           withTimeout:2
                   tag:1];

Notice the changes I have made to the time out. And then finally did the receive setup, and closed the socket. Note the socket close. Since I am in my own class when I am running this - the code above did not work for me.

[ssdpSock receiveWithTimeout: 2 tag:1];
    [NSTimer scheduledTimerWithTimeInterval: 5 target: self 
                                   selector:@selector(completeSearch:) userInfo: self repeats: NO]; 





    [ssdpSock closeAfterSendingAndReceiving];

The most important change probably was returning "NO" if I did not find my controller. The first receive was incidentally the discovery message itself coming back. And when I read through the AsyncUdpSocket.h file carefully - returning "NO" when it is not a packet you are looking for helped.

Also note that I am using ARC in my code but I compiled the AsyncUdpSocket without ARC support.

-(void) completeSearch: (NSTimer *)t 
{

    NSLog(@"%s",__FUNCTION__);

    //[ssdpSock close];
    //ssdpSock = nil;

}


- (BOOL)onUdpSocket:(AsyncUdpSocket *)sock 
     didReceiveData:(NSData *)data 
            withTag:(long)tag 
           fromHost:(NSString *)host 
               port:(UInt16)port
{
    NSLog(@"%s %ld %@ %d",__FUNCTION__,tag,host,port);
    NSString *aStr = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];

    NSLog(@"%@",aStr);



    NSString *compareString = [aStr stringByPaddingToLength:[self.responseString length] withString:@"." startingAtIndex:0];
    //NSLog(@"%@", compareString);
    //NSLog(@"%@", self.responseString);

    if ([compareString isEqualToString:self.responseString])
    {
        NSLog(@"String Compare, Controller Found!");
        [self.controllerList addObject:aStr];
        //NSData *controllerIP = [aStr dataUsingEncoding:NSUTF8StringEncoding];
        [[NSNotificationCenter defaultCenter] postNotificationName:@"DiscoveredController" object:nil];


        return YES;
    }

    return NO;

}
Ashu Joshi
  • 540
  • 3
  • 11
  • I am using the above described approach, to discover a sony action cam. On the line [ssdpSock bindToPort:1900 error:&socketError] appears on the debugger CFSocketSetAddress listen failure: 102, however everything is ok, discovery happens successfully. What the heck means this failure: 102 I dont know. Anyone any idea ? PS: If I dig a little bit deeper, the line where it happens is CFSocketError error = CFSocketSetAddress(theSocket4, (__bridge CFDataRef)address4); error value is kCFSocketSuccess – i-developer Aug 20 '15 at 15:35
2

OK, finally done it. Found a class in the public domain (thanks Chris) called AsyncUdpSocket that lets you create a UDP socket which you can then turn on broadcasting and join the multicast address.

There is a nice sendData method, complete with adding to a run loop to prevent blocking.

Hope that helps.

Dave

Magic Bullet Dave
  • 9,006
  • 10
  • 51
  • 81