轉載自 https://eliyar.biz/DLNA_with_iOS_Android_Part_1_Find_Device_Using_SSDP/
SSDP能夠在局域網能簡單地發現設備提供的服務。SSDP有兩種發現方式:主動通知和搜索響應方式。
尋址
UPnP 技術是架構在 IP 網絡之上。因此擁有一個網絡中唯一的 IP 地址是 UPnP 設備正常工作的基礎。UPnP 設備首先查看網絡中是否有 DHCP 服務器,如果有,那么使用 DHCP 分配的 IP 即可;如果沒有,則需要使用LLA技術來為自己找適合的IP地址。
另外,在 UPnP 運行過程中,UPnP 設備都需要周期性檢測網絡中是否有 DHCP 服務器存在,一旦發現有 DHCP 服務器,就必須終止使用 LLA 技術獲取的 IP 地址,改用 DHCP 分配的 IP 地址。
發現
SSDP
SSDP:Simple Sever Discovery Protocol,簡單服務發現協議,此協議為網絡客戶提供一種無需任何配置、管理和維護網絡設備服務的機制。此協議采用基於通知和發現路由的多播發現方式實現。協議客戶端在保留的多播地址:239.255.255.250:1900(IPV4)發現服務,(IPv6 是:FF0x::C)同時每個設備服務也在此地址上上監聽服務發現請求。如果服務監聽到的發現請求與此服務相匹配,此服務會使用單播方式響應。
常見的協議請求消息有兩種類型,第一種是服務通知,設備和服務使用此類通知消息聲明自己存在;第二種是查詢請求,協議客戶端用此請求查詢某種類型的設備和服務。
iOS中使用GCDAsyncUdpSocket發送和接受SSDP請求、響應及通知,安卓也需要用類此框架來完成
所以我們發現設備也有兩種方法
- 主動通知方式:當設備加入到網絡中,向網絡上所有控制點通知它所提供的服務,通知消息采用多播方式。
- 搜索——響應方式:當一個控制點加入到網絡中,在網絡搜索它感興趣的所有設備和服務,搜索消息采用多播方式發送,而設備針對搜索的響應則是使用單播方式發送。
SSDP 設備類型及服務類型
設備類型 | 表示文字 |
---|---|
UPnP_RootDevice | upnp:rootdevice |
UPnP_InternetGatewayDevice1 | urn:schemas-upnp-org:device:InternetGatewayDevice:1 |
UPnP_WANConnectionDevice1 | urn:schemas-upnp-org:device:WANConnectionDevice:1 |
UPnP_WANDevice1 | urn:schemas-upnp-org:device:WANConnectionDevice:1 |
UPnP_WANCommonInterfaceConfig1 | urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1 |
UPnP_WANIPConnection1 | urn:schemas-upnp-org:device:WANConnectionDevice:1 |
UPnP_Layer3Forwarding1 | urn:schemas-upnp-org:service:WANIPConnection:1 |
UPnP_WANConnectionDevice1 | urn:schemas-upnp-org:service:Layer3Forwarding:1 |
服務類型 | 表示文字 |
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 |
主動通知方式
當設備添加到網絡后,定期向(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://127.0.0.1:2351/1.xml)
CACHE-CONTROL: // max-age指定通知消息存活時間,如果超過此時間間隔,控制點可以認為設備不存在 (如:max-age=1800)
SERVER: // 包含操作系統名,版本,產品名和產品版本信息( 如:Windows NT/5.0, UPnP/1.0)
USN: // 表示不同服務的統一服務名,它提供了一種標識出相同類型服務的能力。如:
// 根/啟動設備 uuid:f7001351-cf4f-4edd-b3df-4b04792d0e8a::upnp:rootdevice
// 連接管理器 uuid:f7001351-cf4f-4edd-b3df-4b04792d0e8a::urn:schemas-upnp-org:service:ConnectionManager:1
// 內容管理器 uuid:f7001351-cf4f-4edd-b3df-4b04792d0e8a::urn:schemas-upnp-org:service:ContentDirectory:1
ssdp:byebye 消息
當設備即將從網絡中退出時,設備需要對每一個未超期的 ssdp:alive 消息多播形式發送 ssdp:byebye 消息,其格式如下:
NOTIFY * HTTP/1.1 // 消息頭
HOST: // 設置為協議保留多播地址和端口,必須是:239.255.255.250:1900(IPv4)或FF0x::C(IPv6
NTS: // 表示通知消息的子類型,必須為ssdp:byebye
USN: // 同上
搜索——響應方式
當控制點,如手機客戶端,加入到網絡中,可以通過多播搜索消息來尋找網絡上感興趣的設備。我寫DLNA模塊時候也用主動搜索方式來發現設備。主動搜索可以使用多播方式在整個網絡上搜索設備和服務,也可以使用單播方式搜索特定主機上的設備和服務。
多播搜索消息
一般情況我們使用多播搜索消息來搜索所有設備即可。多播搜索消息如下:
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組織定義。
如果需要實現投屏,則設備類型 ST 為 urn:schemas-upnp-org:service:AVTransport:1
多播搜索響應
多播搜索 M-SEARCH 響應與通知消息很類此,只是將NT字段作為ST字段。響應必須以一下格式發送:
HTTP/1.1 200 OK // * 消息頭
LOCATION: // * 包含根設備描述得URL地址 device 的webservice路徑(如:http://127.0.0.1:2351/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,需要做處理。 此外還需根據LOCATION保存設備的IP和端口地址。 響應例子如下:
HTTP/1.1 200 OK
Cache-control: max-age=1800
Usn: uuid:88024158-a0e8-2dd5-ffff-ffffc7831a22::urn:schemas-upnp-org:service:AVTransport:1
Location: http://192.168.1.243:46201/dev/88024158-a0e8-2dd5-ffff-ffffc7831a22/desc.xml
Server: Linux/3.10.33 UPnP/1.0 Teleal-Cling/1.0
Date: Tue, 01 Mar 2016 08:47:42 GMT+00:00
Ext:
St: urn:schemas-upnp-org:service:AVTransport:1
描述
控制點發現設備之后仍然對設備知之甚少,僅能知道UPnP類型,UUID和設備描述URL。為了進一步了解設備和服務,需要獲取並解析XML描述文件。 描述文件有兩種類型:設備描述文檔(DDD)和服務描述文檔(SDD) 設備描述文檔
設備描述文檔是對設備的基本信息描述,包括廠商制造商信息、設備信息、設備所包含服務基本信息等。
設備描述采用XML格式,可以通過HTTP GET請求獲取。其鏈接為設備發現消息中的Location。如上述設備的描述文件獲取請求為
GET http://192.168.1.243:46201/dev/88024158-a0e8-2dd5-ffff-ffffc7831a22/desc.xml HTTP/1.1
HOST: 192.168.1.243:46201
設備響應如下
HTTP/1.1 200 OK
Content-Length : 3612
Content-type : text/xml
Date : Tue, 01 Mar 2016 10:00:36 GMT+00:00
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="urn:schemas-upnp-org:device-1-0" xmlns:qq="http://www.tencent.com">
<specVersion>
<major>1</major>
<minor>0</minor>
</specVersion>
<device>
<deviceType>urn:schemas-upnp-org:device:MediaRenderer:1</deviceType>
<UDN>uuid:88024158-a0e8-2dd5-ffff-ffffc7831a22</UDN>
<friendlyName>客廳的小米盒子</friendlyName>
<qq:X_QPlay_SoftwareCapability>QPlay:1</qq:X_QPlay_SoftwareCapability>
<manufacturer>Xiaomi</manufacturer>
<manufacturerURL>http://www.xiaomi.com/</manufacturerURL>
<modelDescription>Xiaomi MediaRenderer</modelDescription>
<modelName>Xiaomi MediaRenderer</modelName>
<modelNumber>1</modelNumber>
<modelURL>http://www.xiaomi.com/hezi</modelURL>
<serialNumber>11262/180303452</serialNumber>
<presentationURL>device_presentation_page.html</presentationURL>
<UPC>123456789012</UPC>
<dlna:X_DLNADOC xmlns:dlna="urn:schemas-dlna-org:device-1-0">DMR-1.50</dlna:X_DLNADOC>
<dlna:X_DLNACAP xmlns:dlna="urn:schemas-dlna-org:device-1-0">,</dlna:X_DLNACAP>
<iconList>
<icon>
<mimetype>image/png</mimetype>
<width>128</width>
<height>128</height>
<depth>8</depth>
<url>icon/icon128x128.png</url>
</icon>
</iconList>
<serviceList>
<service>
<serviceType>urn:schemas-upnp-org:service:AVTransport:1</serviceType>
<serviceId>urn:upnp-org:serviceId:AVTransport</serviceId>
<controlURL>/dev/88024158-a0e8-2dd5-ffff-ffffc7831a22/svc/upnp-org/AVTransport/action</controlURL>
<eventSubURL>/dev/88024158-a0e8-2dd5-ffff-ffffc7831a22/svc/upnp-org/AVTransport/event</eventSubURL>
<SCPDURL>/dev/88024158-a0e8-2dd5-ffff-ffffc7831a22/svc/upnp-org/AVTransport/desc.xml</SCPDURL>
</service>
<service>
<serviceType>urn:schemas-upnp-org:service:RenderingControl:1</serviceType>
<serviceId>urn:upnp-org:serviceId:RenderingControl</serviceId>
<controlURL>/dev/88024158-a0e8-2dd5-ffff-ffffc7831a22/svc/upnp-org/RenderingControl/action</controlURL>
<eventSubURL>/dev/88024158-a0e8-2dd5-ffff-ffffc7831a22/svc/upnp-org/RenderingControl/event</eventSubURL>
<SCPDURL>/dev/88024158-a0e8-2dd5-ffff-ffffc7831a22/svc/upnp-org/RenderingControl/desc.xml</SCPDURL>
</service>
<service>
<serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType>
<serviceId>urn:upnp-org:serviceId:ConnectionManager</serviceId>
<controlURL>/dev/88024158-a0e8-2dd5-ffff-ffffc7831a22/svc/upnp-org/ConnectionManager/action</controlURL>
<eventSubURL>/dev/88024158-a0e8-2dd5-ffff-ffffc7831a22/svc/upnp-org/ConnectionManager/event</eventSubURL>
<SCPDURL>/dev/88024158-a0e8-2dd5-ffff-ffffc7831a22/svc/upnp-org/ConnectionManager/desc.xml</SCPDURL>
</service>
<service>
<serviceType>urn:mi-com:service:RController:1</serviceType>
<serviceId>urn:upnp-org:serviceId:RController</serviceId>
<controlURL>/dev/88024158-a0e8-2dd5-ffff-ffffc7831a22/svc/upnp-org/RController/action</controlURL>
<eventSubURL>/dev/88024158-a0e8-2dd5-ffff-ffffc7831a22/svc/upnp-org/RController/event</eventSubURL>
<SCPDURL>/dev/88024158-a0e8-2dd5-ffff-ffffc7831a22/svc/upnp-org/RController/desc.xml</SCPDURL>
</service>
</serviceList>
<av:X_RController_DeviceInfo xmlns:av="urn:mi-com:av">
<av:X_RController_Version>1.0</av:X_RController_Version>
<av:X_RController_ServiceList>
<av:X_RController_Service>
<av:X_RController_ServiceType>controller</av:X_RController_ServiceType>
<av:X_RController_ActionList_URL>http://192.168.1.243:6095/</av:X_RController_ActionList_URL>
</av:X_RController_Service>
<av:X_RController_Service>
<av:X_RController_ServiceType>data</av:X_RController_ServiceType>
<av:X_RController_ActionList_URL>http://api.tv.duokanbox.com/bolt/3party/</av:X_RController_ActionList_URL>
</av:X_RController_Service>
</av:X_RController_ServiceList>
</av:X_RController_DeviceInfo>
</device>
</root>
其中響應消息體為XML格式的設備描述內容。信息結構比較明確,就不一一介紹了。解析該XML,保存設備的一些基本信息如 deviceType 、 friendlyName 、 iconList 等。之后我們關注該設備提供的服務列表,投屏最關注的服務為 urn:schemas-upnp-org:service:AVTransport:1:
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="urn:schemas-upnp-org:device-1-0" xmlns:qq="http://www.tencent.com">
<device>
<serviceList>
<service>
<serviceType>urn:schemas-upnp-org:service:AVTransport:1</serviceType>
<serviceId>urn:upnp-org:serviceId:AVTransport</serviceId>
<controlURL>/dev/88024158-a0e8-2dd5-ffff-ffffc7831a22/svc/upnp-org/AVTransport/action</controlURL>
<eventSubURL>/dev/88024158-a0e8-2dd5-ffff-ffffc7831a22/svc/upnp-org/AVTransport/event</eventSubURL>
<SCPDURL>/dev/88024158-a0e8-2dd5-ffff-ffffc7831a22/svc/upnp-org/AVTransport/desc.xml</SCPDURL>
</service>
</serviceList>
</device>
</root>
- serviceId : 必有字段。服務表示符,是服務實例的唯一標識。
- serviceType : 必有字段。UPnP服務類型。格式定義與deviceType類此。詳看文章開頭表格。
- SCPDURL : 必有字段。Service Control Protocol Description URL,獲取設備描述文檔URL。
- controlURL : 必有字段。向服務發出控制消息的URL,詳見 基於DLNA實現iOS,Android投屏:SOAP控制設備
- eventSubURL : 必有字段。訂閱該服務時間的URL,詳見 基於DLNA實現iOS,Android投屏:SOAP控制設備
如只需要實現簡單的投屏,則保存urn:schemas-upnp-org:service:AVTransport:1服務的上述信息即可。如需要進一步了解該服務,則需要獲取並解析服務描述文檔。
坑點1:有些設備 SCPDURL 、 controlURL 、 eventSubURL 開頭包含 / ,有些設備不包含,拼接URL時需要注意。
服務描述文檔
為了實現簡單的投屏和控制(播放、暫停、停止、快進)操作並不需要解析服務描述文件。所有動作均為UPnP規范動作,具體動作請求參見基於DLNA實現iOS,Android投屏:SOAP控制設備
服務描述文檔是對服務功能的基本說明,包括服務上的動作及參數,還有狀態變量和其數據類型、取值范圍等。
和設備描述文檔一致,服務描述文檔也是采用XML語法,並遵守標准UPnP服務schema文件格式要求。獲取上述服務SDD語法如下:
GET http://192.168.1.243:46201/dev/88024158-a0e8-2dd5-ffff-ffffc7831a22/svc/upnp-org/AVTransport/desc.xml
HOST: 192.168.1.243:46201
設備響應如下:
HTTP/1.1 200 OK
Content-Length : 3612
Content-type : text/xml
Date : Tue, 01 Mar 2016 10:00:36 GMT+00:00
<!-- 省略了部分動作和狀態變量 -->
<?xml version="1.0" encoding="UTF-8"?>
<scpd xmlns="urn:schemas-upnp-org:service-1-0">
<specVersion>
<major>1</major>
<minor>0</minor>
</specVersion>
<actionList>
<action>
<name>Pause</name>
<argumentList>
<argument>
<name>InstanceID</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
</argument>
</argumentList>
</action>
<action>
<name>Play</name>
<argumentList>
<argument>
<name>InstanceID</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
</argument>
<argument>
<name>Speed</name>
<direction>in</direction>
<relatedStateVariable>TransportPlaySpeed</relatedStateVariable>
</argument>
</argumentList>
</action>
<action>
<name>Previous</name>
<argumentList>
<argument>
<name>InstanceID</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
</argument>
</argumentList>
</action>
<action>
<name>SetAVTransportURI</name>
<argumentList>
<argument>
<name>InstanceID</name>
<direction>in</direction>
<relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
</argument>
<argument>
<name>CurrentURI</name>
<direction>in</direction>
<relatedStateVariable>AVTransportURI</relatedStateVariable>
</argument>
<argument>
<name>CurrentURIMetaData</name>
<direction>in</direction>
<relatedStateVariable>AVTransportURIMetaData</relatedStateVariable>
</argument>
</argumentList>
</action>
...
</actionList>
<serviceStateTable>
<stateVariable sendEvents="no">
<name>CurrentTrackURI</name>
<dataType>string</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>CurrentMediaDuration</name>
<dataType>string</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>AbsoluteCounterPosition</name>
<dataType>i4</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>RelativeCounterPosition</name>
<dataType>i4</dataType>
</stateVariable>
<stateVariable sendEvents="no">
<name>A_ARG_TYPE_InstanceID</name>
<dataType>ui4</dataType>
</stateVariable>
...
</serviceStateTable>
</scpd>
- actionList 目前服務上所包含的動作列表。
- actionList 目前服務上所包含的狀態變量。
以Pause動作為例
<?xml version="1.0" encoding="UTF-8"?>
<scpd xmlns="urn:schemas-upnp-org:service-1-0">
<actionList>
<action>
<!-- 動作名稱 -->
<name>Pause</name>
<!-- 參數列表 -->
<argumentList>
<argument>
<!-- 參數名稱 -->
<name>InstanceID</name>
<!-- 輸出或輸出-->
<direction>in</direction>
<!-- 聲明參數有關的狀態變量 -->
<relatedStateVariable>A_ARG_TYPE_InstanceID</relatedStateVariable>
</argument>
</argumentList>
</action>
...
</actionList>
<serviceStateTable>
<!-- 狀態變量 -->
<stateVariable>
<!-- 是否發送事件消息,如果為yes則該狀態變量發生變化時生成事件消息。 -->
<stateVariable sendEvents="no">
<!-- 狀態變量名稱 -->
<name>A_ARG_TYPE_InstanceID</name>
<!-- 狀態數據類型 -->
<dataType>ui4</dataType>
</stateVariable>
...
</serviceStateTable>
</scpd>
為了實現簡單的投屏和控制(播放、暫停、停止、快進)操作並不需要解析服務描述文件。