1,UPnP简介

        UPNP(Universal Plug and Play)即通用即插即用协议, 是为了实现电脑与智能的电器设备对等网络连接的体系结构。而内网地址与网络地址的转换就是基于此协议的,因此只要我们的路由器支持 upnp,并且我们使用支持此协议的 操作系统,那么我们就可以借此提高点对点传输速度。

        简单的来说,UPnP 最大的愿景就是希望任何设备只要一接上网络,所有在网络上的设备马上就能知道有新设备加入,这些设备彼此之间能互相沟通,更能直接使用或控制它,一切都不需要设定,完全的 Plug and Play。

2,UPnP工作流程

UPnP工作主要分为以下六个步骤:

2.1 寻址

        寻址(Addressing)是UPnP网络连接的第0步,通过寻址,设备和控制点将获得IP地址。UPnP设备和控制点可以只支持IPv4,也可以同时支持IPv4和IPv6。对于每个控制设备或控制点而言,如果自身没有实现DHCP服务器,则在它们首次接入UPnP网络时,必须通过DHCP客户端来搜寻DHCP服务器(如果它们自身实现了DHCP服务器,则可以给自己从地址池中分配IP地址)。如果存在DHCP服务器,例如托管网络中,那么设备和控制点必须使用分配到的IP地址;如果DHCP服务器不可用,例如在非托管网络中,那么设备或控制点必须使用Auto-IP来获得IP地址。

定义在RFC3927中的Auto-IP定义了设备或控制点:1)判断DHCP服务器是否可用;2)在链路本地的IP地址集合中选择一个IP地址。这种地址分配方式允许设备或控制点在托管网络和非托管网络间自由的移动。

2.2 发现

2.2.1 架构图

设备是服务的载体,可以包含多种服务也可嵌套多个设备。到这一步设备和控制点只完成了相互通信的一小部分,在这一步设备宣告了自己的存在和拥有的服务,以及其他的一些基本信息如设备的UUID,设备描述的URL地址等

2.2.2 发现流程

发现过程如下,粗箭头为多播,细箭头为单播

控制点上线

当控制点(control points)上线后,就会进入发现阶段。向UPnP专用的多播地址发送SSDP发现报文,请求连接到网络上的UPnP设备告知自己其所拥有的服务,设备收到该多播请求,并判断自己能满足该请求,则SSDP单播回复控制点

设备上线

当设备上线后,就会进行通知操作。以多播方式发送SSDP通知报文,通知控制点自己所拥有的服务,且每隔一定的时间就要重新再通知一次,否则会被cp判为离线

这两种情况下的组播消息一般都是设备和服务的基本信息。如类型、唯一标识符、当前状态参数等。具体报文交互见2.3

2.2.3 报文交互

case1:控制节点上线,搜索网络上的设备和服务:

控制节点使用数据报套接字向239.255.255.250:1900发送一条多播请求,格式如下:

M-SEARCH * HTTP/1.1
HOST: 239.255.255.250:1900
MAN:"ssdp:discover"
MX:3
ST:UPnP:rootdevice


设备收到多播请求,如果匹配请求,则将自身情况单播方式告知控制点。报文格式如下:

HTTP/1.1 200 OK
CACHE-CONTROL: max-age=100
DATE: Sun, 15 Jan 2006 06:51:02 GMT
EXT:
LOCATION: http://192.168.14.1:1900/igd.xml
SERVER: TP-LINK Wireless Router WR541G/5, UPnP/1.0
ST: upnp:rootdevice
USN:uuid:upnp-InternetGatewayDevice-192168141678900001::upnp:rootdevice


case2:设备上线,通知控制节点自己的情况

设备采用多播方式通知网络上的控制节点自己的信息,且每隔一定时间(默认30min)需要通知一次,报文格式如下。

NOTIFY * HTTP/1.1
Host: 239.255.255.250:1900
NT: blenderassociation:blender
NTS: ssdp:alive
USN: someunique:idscheme3
AL: <blender:ixl><http://foo/bar>
Cache-Control: max-age = 7393(s)
case3:设备离线,通知网络控制点

一般设备离线会通知控制节点,不通知也没事,一定时间后不回复控制点,就会被判为离线并清除

NOTIFY * HTTP/1.1
Host: 239.255.255.250:1900
NT: someunique:idscheme3  (服务类型)
NTS: ssdp:byebye(通知消息的类型)
USN: someunique:idscheme3


2.3 描述


CP获取了设备的基本信息UUID、URL之后还需要知道更多信息,如设备的状态,可执行的动作等。这时需要通过设备的URL访问设备,获取更多的服务描述信息。

投屏设备提供 控制服务、事件服务和展示服务,android设备通过控制点控制投屏设备的这些服务。

  • 根设备 与 设备:
    设备 就是那些物联网设备 如:家用电器、盒子 等。因为一台 UPnP 设备可以是多个服务的载体或多个子设备的嵌套,所以存在一个根设备的概念。

  • 服务:
    是指 设备所支持 能提供的服务,例如:支持 控制服务、事件服务、展示服务。

  • 控制点:
    控制点 就是可以发现并控制其他设备的控制设备。听起来有点绕,其实就是控制设备,例如: android 设备需要控制设备来控制投屏设备的视频播放、暂停等操作,就需要控制点对设备进行控制。

2.3.1 设备描述

UPnP设备的描述以XML形式来表示,设备描述包括制造商信息、模块名称和编号、序列号等等。

对于一个物理设备可以包含多个逻辑设备,多个逻辑设备既可以是一个根设备其中嵌入多个设备,也可以是多个根设备的方式存在。描述模板如下:

(图中红色字段为UPnP论坛定义;紫色字段为UPnP设备制造商定义;绿色字段为设备架构定义)

◆ <root>

必需标签。<root>到</root>之间包含根设备属性信息,根设备服务信息,根设备的嵌入式设备信息。其中根设备的服务信息在<serviceList>标签内,根设备内部的嵌入式设备的信息在<deviceList>标签内。

◆ <UDN>

必需标签。UPnP设备的唯一标识符,由一个小于64个字符的字符串组成。

◆ <deviceType>

必需标签。UPnP设备类型,通常有UPnP论坛定义的标准设备和UPnP设备制造商定义的设备两种情况。UPnP 论坛定义的标准设备模型为“urn:schemas-upnp-org:device:deviceType:ver”,而UPnP设备制造商定义的设备模型为“urn:domain-name:device:deviceType:ver”。其中“deviceType”为设备类型,“ver”为最高支持版本,“domain-name”为UPnP设备制造商的域名,不超过64个字符。

◆ <friendlyName>

必需标签。用户可见的设备名,由UPnP厂商指定一个小于64个字符的字符串。

◆ <service>

当该设备提供服务时必需。<service>到</service>之间包含服务描述。有服务类型(<serviceType>)、服务ID(<serviceId>)、服务描述文件的URL(<SCPDURL>)、服务控制的URL(<controlURL>)和服务事件的URL(<eventSubURL>)。

◆ <serviceType>

必需标签。服务类型,通常有UPnP 论坛定义的标准服务和UPnP设备制造商定义的服务两种情况。UPnP 论坛定义的标准服务模型为“urn:schemas-upnp-org:service:serviceType:ver”,而UPnP设备制造商定义的设备模型为“urn:domain-name:service:serviceType:ver”。其中serviceType”为服务类型,其余定义的意义与<deviceType>标签一致。

◆ <serviceId>

必需标签。服务ID,在其设备描述中必须唯一。通常有UPnP 论坛定义的标准服务ID和UPnP设备制造商定义的服务ID两种情况。UPnP 论坛定义的标准服务ID模型为“urn:schemas-upnp-org:service:serviceID”,而UPnP设备制造商定义的设备模型ID为“urn:domain-name:service:serviceID”。其中serviceID”一般与该服务的<serviceType>中的“serviceType”一致,其余定义的意义与<deviceType>标签一致。

◆ <SCPDURL>

必需标签。服务描述文件的URL,在其设备描述中必须唯一,获取该服务的描述文件时需要。

◆ <controlURL>

必需标签。服务控制的URL,在其设备描述中必须唯一,向该服务发出控制请求时需要。

◆ <eventSubURL>

必需标签。服务事件的URL,在其设备描述中必须唯一,对该服务进行事件订阅、续订、退订以及事件通知时需要。

2.3.2 服务描述

服务的描述包含服务运行时刻的状态,运行时间等等。服务描述也由设备制造商提供,采用XML描述,遵循UPnP框架协议。

(图中红色字段为UPnP 论坛定义;紫色字段为UPnP设备制造商定义;绿色字段为设备架构定义)

◆ <actionList>

当该服务提供动作时必需。服务的动作列表,包含该服务的所有动作描述信息,每个动作使用一个<action>标签描述。

◆ <action>

必需标签。动作描述,<action>到</action>之间包含一个动作的描述。有动作名称(<name>),与该动作相关的参数表(<argumentList>)。

◆ <argument>

必需标签。动作的参数描述,<argument>到<argument>之间包含一个该动作的相关参数描述信息。有参数名(<name>),是输入参数还是输出参数(<direction>),如果需要返回时还包含返回值的描述(<retval>),以及与该参数对应的状态变量名(relatedStateVariable)。

◆ <serviceStateTable>

当该服务存在状态变量时必需。该服务的状态变量表,<serviceStateTable>到</serviceStateTable>之间包含所有的状态变量描述,每一个状态变量使用一个<stateVariable>标签描述。

◆ <stateVariable>

必需标签。状态变量,<stateVariable>到</stateVariable>之间包含一个状态变量的描述信息。有状态变量名(<name>)、该状态变量的数据类型(<dataType>),推荐包含该状态变量的默认值(<defaultValue>)、状态变量的可取值列表(<allowedValueList>)、数值型状态变量的取值范围(<allowedValueRange>)。

2.4 控制

2.4.1 控制流程

拿到设备和服务描述后,怎么控制设备呢?设备描述提供Control URL,CP通过SOAP协议向Control URL发送操控请求,SOAP协议含有消息Body,指示如何控制设备。可能还要传参数,比如想播放一个视频,要把视频的URL传过去;设备收到后要respone,表示能不能执行调用,出错的话会返回一个错误代码。

2.4.2 报文交互

控制流程交互使用的是SOAP报文,具体格式如下:

所有的SOAP消息都使用XML编码,一条SOAP消息就是一个普通的XML文档,文档包括下列元素:

A、Envelope(信封)元素,必选,可把此XML文档标识为一条SOAP消息。

B、Header(报头)元素,可选,包含头部信息(包含了使消息在到达最终目的地之前,能够被路由到 一个或多个中间节点的信息,可能会定义认证信息、事务信息、本地信息)。

C、Body(主体)元素,必选,包含所有的调用和响应信息。

D、Fault 元素,位于Body内,可选,提供有关处理此消息所发生错误的信息。

E、Attachment(附件)元素,可选,可通过添加一个或多个附件扩展SOAP消息。

2.5 事件


2.5.1 订阅发布流程

当设备上的服务在运行,其状态变量发生改变之后,就产生了一个事件。控制点可以订阅这个事件,保证能及时捕获事件信息,做出响应的控制动作。

下面是一个单播事件的结构图,控制点是subscriber(订阅者),设备是publisher(发布者)。

订阅者向发布者发送订阅消息,更新订阅消息,退订消息。发布者向订阅者推送订阅

2.5.2 订阅发布报文交互

事件的订阅和推送这块用的通信协议是GENA(General Event Notification Architecture) ,通过HTTP/TCP/IP传送。

A.订阅请求

【订阅者】

订阅请求

订阅者向发布者发送一个包含发布者URL、服务标识符和事件消息交付URL的订阅消息

请求订阅消息格式如下,采用SUBSCRIBE方法的请求,没有消息体,但该消息与最后一个http标头之间需要空一行。

SUBSCRIBE publisher_path  HTTP/1.1
HOST:  publisher_host    publisher_port
CALLBACK: <delivery URL>
NT: UPnP event
TIMEOUT: second-requested  subscription  duration
消息解析:

(1)命令行

SUBSCRIBE 开始订阅或续订,方法由GENA定义

publisher_path 事件触发URL(设备描述中的服务元素eventSubURL子元素)路径组成

HTTP/1.1 HTTP版本

(2)标头

HOST host:事件触发URL的域名或ip; port:端口,默认80

CALLBACK 事件消息发往的位置,UPnP厂商定义,如果有多个url则挨个尝试,直至成功

NT 同CALLBACK一样为GENA规定,通知类型必须采用upnp:event形式

SID 订阅不采用SID标头

TIMEOUT 订阅有效期,second+整数/infinite(无限)

【发布者】

接受订阅:

如果资源足够,发布者应当30s(包含预计传输时间)内接受订阅。发布者接受订阅后,返回唯一订阅标识符和订阅持续时长,并尽快发送第一个初始化消息。

HTTP/1.1  200  OK
DATE:when response was generated
SERVER:  OS/version   UPnP/1.0  product/version
SID: uuid:subscription-UUID
TIMEOUT: second-actual subscription duration


(1)标头

DATE 响应生成时间,建议采用RFC1123日期;例 Tue, 30 Oct 2018 15:33:48 CST

SERVER 操作系统名称/版本 UPnP/1.0 产品名称/版本

SID GENA规定的标头,订阅标识符,必须以唯一UUID开头,由UPnP厂商规定单一url

TIMEOUT 定期,论坛推荐厂商规定。一般>=1800s; 格式second+整数/infinite

拒绝订阅

若发布者不能接受订阅,或者订阅请求存在错误,发布者应该在30s(包含传输时间)内反馈以下一个错误进行响应。

B.发布消息

【发布者】

发布消息

发布者保留订阅者列表,当一个或多个状态变量被事件触发时,发布者向订阅者传递以下信息:

1)唯一订阅标识符

2)事件交付URL

3) 事件编号

4)订阅持续时间

如果订阅者未响应,会一直发布直到订阅期满。如果需要修复订阅,比如订阅者丢失一条或多条信息,则需要取消订阅后重新订阅,

NOTIFY  delivery_path  HTTP/1.1
HOST:delivery_host  delivery_port
CONTENT-TYPE:  text/xml
CONTENT-LENGTH: Bytes  in  body
NT: upnp:event
NTS:upnp:propchange
SID:uuid:subscription-UUID
SEQ:event key
<e:propertyset> xmlns:e="urn:schemas-upnp-org :event-1-0>
<e:property>
<variableName>new value</variableName>
<e:property>
Other variable names and values (if any) go here.
</e:propertyset>


(1)命令行

NOTIFY 通知消息,方法由GENA定义

交付路径 交付URL(订阅消息中的CALLBACK标头)路径组成,事件消息的目的地,单一、相对的url.

HTTP/1.1 HTTP版本

(2)标头

HOST 要求交付URL(订阅消息中的CALLBACK标头)的域名或ip地址和端口,端口默认80

CONTENT-TYPE 固定为text/xml

CONTENT-LENGTH:消息体长度,以字节为单位,取整

NT 通知类型,固定upnp:event

NTS 通知子类型,固定upnp:propchange

SID 订阅标识符

SEQ 事件编号,初始化消息编号为0,发送给特定订阅者的每条消息必须以1为增量,长度为8字节, 为防溢出,必须回置到1,整数类型。

(3)消息体

<Propertyset>

a.要求Xmlns名字空间属性必须为urn: schemas-upnp-org :event-1-0,所有子元素必 须符合这一名字空间

b.propertyset包含子元素特性,特性要求事件消息中的变量名称和值重复一次,值必须符合名字空间

c.子元素variableName是更改的状态变量名称(服务描述中stateVariable的子元素)

为满足架构的可扩展性,处理上述xml时要遵循灵活的XML处理框架(FXPP),设备和控制点必须忽略:

(a)任何未知元素和其子元素或内容

(b)任何未知属性和值

【订阅者】

确认订阅消息

要确认收到这一事件消息,订阅者必须30s内做出响应。确认消息格式如下

HTTP/1.1    200    OK

【订阅者】

到期续订

订阅者必须在订阅到期之前通过发送续订消息进行续订,除非你不想在订阅了。如果到期不续订,发布者则默认你已退订。

SUBSCRIBE  publisher_path   HTTP/1.1
HOST:  publisher_host   publisher_port
SID: uuid: subscription  UUID
TIMEOUT: second-requested  subscription  duration


注:采用SUBSCRIBE方法的请求没有消息体,但该消息与最后一个HTTP标头之间必须空一行

(1)命令行

SUBSCRIBE 开始订阅或续订,方法由GENA定义

publisher_path 事件触发URL(设备描述中的服务元素eventSubURL子元素)路径组成

HTTP/1.1 HTTP版本

(2)标头

HOST host:事件触发(eventSubURL) URL的域名或ip; port:端口,默认80

SID 订阅标识符

TIMEOUT 订阅有效期,second+整数/infinite(无限)

【发布者】

接受续订

要接受订阅,发布者要重新分配一个订阅持续时间,且必须以与响应新订阅请求相同格式发送一个响应,但不需要发送初始化事件。

具体报文看【B.接受订阅】

拒绝续订

若发布者不能接受续订,或者续订请求错误,发布者发送下列一个错误进行响应,该响应要在30s(包括发送时间)内发出。

D.取消订阅

【订阅者】

取消订阅

当订阅者不再需要来自特定服务的消息,便取消订阅。

UNSUBSCRIBE   publisher_path  HTTP/1.1
HOST:  publisher_host   publisher_port
SID: uuid: subscription  UUID


注:采用UNSUBSCRIBE方法的请求没有消息体,但该消息与最后一个HTTP标头之间必须空一行

(1)命令行

UNSUBSCRIBE 取消订阅,方法由GENA定义

publisher_path 事件触发URL(设备描述中的服务元素eventSubURL子元素)路径组成

HTTP/1.1 HTTP版本

(2)标头

HOST host:事件触发(eventSubURL) URL的域名或ip; port:端口,默认80

SID 订阅标识符

【发布者】

同意取消

要取消订阅,发布者必须30s内以下面格式发送一条响应

HTTP/1.1   200   OK
取消失败

如果取消请求出现错误,发布者必须30s内返回以下一种错误进行响应

2.6 UPnP工作时序

UPnP工作时序图如下

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐