基於DLNA實現iOS,Android投屏: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_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 |
裝置型別 | 表示文字 |
---|---|
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 |
主動通知方式
當裝置新增到網路後,定期向(239.255.255.250:1900)傳送SSDP通知訊息宣告自己的裝置和服務。
宣告訊息分為 ssdp:alive(裝置可用)
和ssdp:byebye(裝置不可用)
ssdp:alive 訊息
1 2 3 4 5 6 7 8 9 10 11 |
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
訊息,其格式如下:
1 2 3 4 |
NOTIFY * HTTP/1.1 // 訊息頭 HOST: // 設定為協議保留多播地址和埠,必須是:239.255.255.250:1900(IPv4)或FF0x::C(IPv6 NTS: // 表示通知訊息的子型別,必須為ssdp:byebye USN: // 同上 |
搜尋——響應方式
當控制點,如手機客戶端,加入到網路中,可以通過多播搜尋訊息來尋找網路上感興趣的裝置。我寫DLNA模組時候也用主動搜尋方式來發現裝置。主動搜尋可以使用多播方式在整個網路上搜索裝置和服務,也可以使用單播方式搜尋特定主機上的裝置和服務。
多播搜尋訊息
一般情況我們使用多播搜尋訊息來搜尋所有裝置即可。多播搜尋訊息如下:
1 2 3 4 5 6 7 8 9 10 |
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欄位。響應必須以一下格式傳送:
1 2 3 4 5 6 7 8 9 10 |
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和埠地址。
響應例子如下:
1 2 3 4 5 6 7 8 |
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。如上述裝置的描述檔案獲取請求為
1 2 |
GET http://192.168.1.243:46201/dev/88024158-a0e8-2dd5-ffff-ffffc7831a22/desc.xml HTTP/1.1 HOST: 192.168.1.243:46201 |
裝置響應如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
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
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?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語法如下:
1 2 |
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 |
裝置響應如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
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動作為例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<?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> |
為了實現簡單的投屏和控制(播放、暫停、停止、快進)操作並不需要解析服務描述檔案。所有動作均為UPnP規範動作,具體動作請求參見基於DLNA實現iOS,Android投屏:SOAP控制裝置