iOS-DLNA(UPnP-SSDP)

1,104 阅读5分钟

SSDP

SSDP:Simple Sever Discovery Protocol,简单服务发现协议,此协议为网络客户提供一种无需任何配置、管理和维护网络设备服务的机制。此协议采用基于通知和发现路由的多播发现方式实现。协议客户端在保留的多播地址:239.255.255.250:1900(IPV4)FF0x::C(IPv6)发现服务,同时每个设备服务也在此地址上监听服务发现请求。如果服务监听到的发现请求与此服务相匹配,此服务会使用单播方式响应。

通过之前UPnP协议栈介绍,知道SSDP请求是基于UDP协议的,因此我们需要使用框架CocoaAsyncSocket进行相关UDP连接数据处理。

常见的协议请求消息有两种类型,第一种是服务通知,设备和服务使用此类通知消息声明自己存在;第二种是查询请求,协议客户端用此请求查询某种类型的设备和服务。因此发现设备也有两种方法

  • 主动搜索方式:当一个控制点加入到网络中,在网络搜索它感兴趣的所有设备和服务,搜索消息采用多播方式发送,而设备针对搜索的响应则是使用单播方式发送。
  • 被动接受通知方式:当设备加入到网络中,向网络上所有控制点通知它所提供的服务,通知消息采用多播方式。

主动搜索方式

当控制点,如手机客户端,加入到网络中,可以通过多播搜索消息来寻找网络上感兴趣的设备。

多播搜索消息

一般情况我们使用多播搜索消息来搜索所有设备即可。多播搜索消息如下:

 多播搜索消息
 M-SEARCH * HTTP/1.1         // 请求头 不可改变
 MAN: "ssdp:discover"        // 设置协议查询的类型,必须是:ssdp:discover 
 MX: 5                       // 设置设备响应最长等待时间,设备响应在0和这个值之间随机选择响应延迟的值。这样可以为控制点响应平衡网络负载。                 
 HOST: 239.255.255.250:1900  // 设置为协议保留多播地址和端口,必须是:239.255.255.250:1900(IPv4)或FF0x::C(IPv6) 
 ST: upnp:rootdevice         // 设置服务查询的目标,它必须是下面的类型:
                             // ssdp:all  搜索所有设备和服务
                             // upnp:rootdevice  仅搜索网络中的根设备
                             // uuid:device-UUID  查询UUID标识的设备
                             // urn:schemas-upnp-org:device:device-Type:version  查询device-Type字段指定的设备类型,设备类型和版本由UPNP组织定义。
                             // urn:schemas-upnp-org:service:service-Type:version  查询service-Type字段指定的服务类型,服务类型和版本由UPNP组织定义。
服务类型表示文字
UPnP_MediaServer1urn:schemas-upnp-org:device:MediaServer:1
UPnP_MediaRenderer1urn:schemas-upnp-org:device:MediaRenderer:1
UPnP_ContentDirectory1urn:schemas-upnp-org:service:ContentDirectory:1
UPnP_RenderingControl1urn:schemas-upnp-org:service:RenderingControl:1
UPnP_ConnectionManager1urn:schemas-upnp-org:service:ConnectionManager:1
UPnP_AVTransport1urn:schemas-upnp-org:service:AVTransport:1

如果需要实现投屏,则设备类型 ST 为 urn:schemas-upnp-org:service:AVTransport:1

核心代码如下:

/// _udpSocket为GCDAsyncUdpSocket对象(udp连接对象创建相关省略)
- (void)searchDevices {
    NSString *mSEARCH = [NSString stringWithFormat:@"M-SEARCH * HTTP/1.1\r\nMX: 1\r\nST: urn:schemas-upnp-org:service:AVTransport:1\r\nMAN: \"ssdp:discover\"\r\nHost: 239.255.255.250:1900\r\n\r\n"];
    [_udpSocket sendData:mSEARCH toHost:@"239.255.255.250" port:1900 withTimeout:-1 tag:1];
} 

多播搜索响应

多播搜索M-SEARCH响应必须以下格式发送:

HTTP/1.1 200 OK             // * 消息头
LOCATION:                   // * 包含根设备描述得URL地址device的webservice路径(如:http://xxxx.x.x.x:xxxx/1.xml)
CACHE-CONTROL:              // * max-age指定通知消息存活时间,如果超过此时间间隔,控制点可以认为设备不存在 (如:max-age=1800)
SERVER:                     // 包含操作系统名,版本,产品名和产品版本信息( 如:Windows NT/5.0, UPnP/1.0)
EXT:                        // 为了符合HTTP协议要求,并未使用。
BOOTID.UPNP.ORG:            // 可以不存在,初始值为时间戳,每当设备重启并加入到网络时+1,用于判断设备是否重启。也可以用于区分多宿主设备。
CONFIGID.UPNP.ORG:          // 可以不存在,由两部分组成的非负十六进制整数,由两部分组成,第一部分代表跟设备和其上的嵌入式设备,第二部分代表这些设备上的服务。
USN:                        // * 表示不同服务的统一服务名
ST:                         // * 服务的服务类型
DATE:                       // 响应生成时间 

其中主要关注带有*的部分即可。
有些设备返回来的字段名称可能包含有小写,如LOCATIONLocation,需要做处理。

核心代码如下:

/// GCDAsyncUdpSocketDelegate 协议方法回调LOCATION、USN
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data fromAddress:(NSData *)address withFilterContext:(nullable id)filterContext{
    /// 搜索方式响应解析 获取部分核心信息例如:LOCATION、USN
    NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    if ([string hasPrefix:@"HTTP/1.1"]) {
        。。。。。。。
    }
}

被动接受通知方式

当设备添加到网络后,定期向(239.255.255.250:1900)发送SSDP通知消息宣告自己的设备和服务。
宣告消息分为 ssdp:alive(设备可用) 和 ssdp:byebye(设备不可用)

ssdp:alive消息

NOTIFY * HTTP/1.1           // 消息头
NT:                         // 在此消息中,NT头必须为服务的服务类型。(如:upnp:rootdevice)
HOST:                       // 设置为协议保留多播地址和端口,必须是:239.255.255.250:1900(IPv4)或FF0x::C(IPv6)
NTS:                        // 表示通知消息的子类型,必须为ssdp:alive
LOCATION:                   // 包含根设备描述得URL地址device的webservice路径(如:http://xxxx.x.x.x:xxxx/1.xml)
CACHE-CONTROL:              // max-age指定通知消息存活时间,如果超过此时间间隔,控制点可以认为设备不存在 (如:max-age=1800)
SERVER:                     // 包含操作系统名,版本,产品名和产品版本信息( 如:Windows NT/5.0, UPnP/1.0)
USN:                        // 表示不同服务的统一服务名,它提供了一种标识出相同类型服务的能力。如:
                            // 根/启动设备 uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx::upnp:rootdevice
                            // 连接管理器 uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx::urn:schemas-upnp-org:service:ConnectionManager:1
                            // 内容管理器 uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx::urn:schemas-upnp-org:service:ContentDirectory:1

ssdp:byebye消息

NOTIFY * HTTP/1.1       // 消息头
HOST:                   // 设置为协议保留多播地址和端口,必须是:239.255.255.250:1900(IPv4)或FF0x::C(IPv6)
NTS:                    // 表示通知消息的子类型,必须为ssdp:byebye
USN:                    // 同上

核心代码如下

/// GCDAsyncUdpSocketDelegate 协议方法回调
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data fromAddress:(NSData *)address withFilterContext:(nullable id)filterContext{
    /// 搜索方式响应解析 获取部分核心信息例如:LOCATION、NTS
    NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    if ([string hasPrefix:@"NOTIFY"]) {
         。。。。。。	  
    }
}