iPhone 上的 SSDP

Posted

技术标签:

【中文标题】iPhone 上的 SSDP【英文标题】:SSDP on the iPhone 【发布时间】:2011-03-12 16:50:52 【问题描述】:

我需要能够发送和接收 UDP 消息,以便从 iPhone 发现网络上的 SSDP 设备。

我知道我需要将数据包发送到多播地址,并且我的 HTTP 请求需要如下所示:

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

从阅读文档看来,我可以使用 CFNetwork 完成所有这些工作,尽管阅读(并重新阅读文档)我仍然难以开始。任何人都可以推荐和教程或编码 sn-ps 来帮助我克服最初的学习障碍吗?

我有 CFNetwork 编程指南:

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

和 Beej 的使用 Internet 套接字进行网络编程的指南:

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

谢谢

戴夫

附言

在这种情况下,我无法使用任何第 3 方库和框架。

【问题讨论】:

【参考方案1】:

我已成功使用 AsyncUdpSocket 运行 SSDP Discovery 并找到控制器。这是我的代码 sn-ps:

初始化和设置套接字:

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

注意注释掉的第一行。我在AsyncUdpSocket forums 上发现了一些重复问题。我认为我没有面对他们,但无论如何我还是做到了。

我添加了错误检查,它很有用,因为在我的调试过程中我没有关闭套接字并且我开始遇到套接字设置失败:

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];

请注意我对超时所做的更改。然后最后进行了接收设置,并关闭了套接字。注意套接字关闭。因为当我运行它时我在自己的班级 - 上面的代码对我不起作用。

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





    [ssdpSock closeAfterSendingAndReceiving];

如果我没有找到我的控制器,最重要的变化可能是返回“NO”。第一次接收是偶然发现消息本身返回。当我仔细阅读 AsyncUdpSocket.h 文件时 - 当它不是您正在寻找帮助的数据包时返回“NO”。

另外请注意,我在代码中使用了 ARC,但我编译了没有 ARC 支持的 AsyncUdpSocket。

-(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;


【讨论】:

我正在使用上述方法,发现一个索尼动作凸轮。在调试器上出现 [ssdpSock bindToPort:1900 error:&socketError] 行监听失败:102,但是一切正常,发现成功。这个失败到底意味着什么:102 我不知道。有人知道吗? PS:如果我再深入一点,它发生的行是 CFSocketError error = CFSocketSetAddress(theSocket4, (__bridge CFDataRef)address4);错误值为 kCFSocketSuccess【参考方案2】:

我的应用中有以下用于 SSDP 搜索的代码:

-(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);

它使用来自CocoaAsyncSocket 的AsyncUdpSocket

【讨论】:

嗨 Savvybud,看起来不错,但从我的头顶上看(这是不久前我这样做的)我认为你的问题在于 bindToPort。我很确定这是将发送返回消息的端口,不应该是 1900,因为这是为多播保留的。如果您将其设置为零,那么系统将分配一个并且它应该可以工作。 sendData 看起来不错。【参考方案3】:

好的,终于搞定了。在公共域中找到了一个名为 AsyncUdpSocket 的类(感谢 Chris),它允许您创建一个 UDP 套接字,然后您可以打开广播并加入多播地址。

有一个不错的 sendData 方法,添加到运行循环以防止阻塞。

希望对您有所帮助。

戴夫

【讨论】:

以上是关于iPhone 上的 SSDP的主要内容,如果未能解决你的问题,请参考以下文章

iPhone上的聚胺

iPhone上的TCP打孔

在横向屏幕上旋转不是 iPhone 5 和 iPhone 6 上的视图

UINavigationController 作为 iPhone 上的弹出框?

Rails上的iPhone

iPhone 上的性能计数器