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_MediaServer1 | urn:schemas-upnp-org:device:MediaServer:1 |
| UPnP_MediaRenderer1 | urn:schemas-upnp-org:device:MediaRenderer:1 |
| UPnP_ContentDirectory1 | urn:schemas-upnp-org:service:ContentDirectory:1 |
| UPnP_RenderingControl1 | urn:schemas-upnp-org:service:RenderingControl:1 |
| UPnP_ConnectionManager1 | urn:schemas-upnp-org:service:ConnectionManager:1 |
| UPnP_AVTransport1 | urn: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: // 响应生成时间
其中主要关注带有
*的部分即可。
有些设备返回来的字段名称可能包含有小写,如LOCATION和Location,需要做处理。
核心代码如下:
/// 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"]) {
。。。。。。
}
}