1. UPnP 简介
UPNP(Universal Plug and Play)即通用即插即用协议, 是为了实现电脑与智能的电器设备对等网络连接的体系结构。而内网地址与网络地址的转换就是基于此协议的,因此只要我们的路由器支持 upnp,并且我们使用支持此协议的 xp 操作系统,那么我们就可以借此提高点对点传输速度。
简单的来说,UPnP 最大的愿景就是希望任何设备只要一接上网络,所有在网络上的设备马上就能知道有新设备加入,这些设备彼此之间能互相沟通,更能直接使用或控制它,一切都不需要设定,完全的 Plug and Play。
2. UPnP 意义
在网络世界里,当一个主机加入网络时,其行为模式跟我们物理硬件的添加和删除设备是类似的。
在私有网络和公网交互的时候,私有网络中的主机使用的是内网 IP 地址,是无法被外网的主机直接访问的。必须借助 NAT 网关设备(本地路由器)把内网地址映射到网关的公网地址上。
当内网中的主机想要被外界主机直接访问(比如开放 80 端口,对外提供 HTTP 服务),我们就需要在 NAT 设备中为当前主机手工配置端口映射,如果内网中有多台主机都想要被外界主机直接访问的话,我们必须在同一个 NAT 设备上为这些主机分别做端口映射,它们之间不能使用有冲突的端口。
这个过程需要用户手工逐一配置,显然给用户带来了很大的麻烦。
另外,如果内部网络中有多个上网设备,每个设备上都有 Live Message 之类的软件需要在网关上开启一个端口进行数据传输,如果每次都使用一个固定端口的话,就会和其他用户冲突。
UPnP 技术标准的出现就是为了解决这个问题,只要 NAT 设备(路由器)支持 UPnP,并开启。那么,当我们的主机(或主机上的应用程序)向 NAT 设备发出端口映射请求的时候,NAT 设备就可以自动为主机分配端口并进行端口映射。
这样,我们的主机就能够像公网主机一样被网络中任何主机访问了。
3. UPnP 应用场景
UPnP 典型的应用场景就是家庭智能设备的互联,还有,目前在网络应用比如 BitTorrent, eMule,IPFS,Ethereum 等使用 P2P 技术的软件,UPnP 功能为它们带来极大的便利。比如:利用 UPnP 能自动的把它们侦听的端口号映射到公网地址上,这样,公网上的用户也能对当前的 NAT 内网主机直接发起连接。
实现 UPnP 必须同时满足三个条件:
- NAT 网关设备必须支持 UPnP 功能;这是因为它需要扮演控制点(239.255.255.250:1900)的角色,控制点提供的是 SSDP 服务。
- 操作系统必须支持 UPnP 功能;比如 Windows 系列操作系统;
- 应用程序必须支持 UPnP 功能;比如 Bt、eMule、IPFS, Ethereum 等。
以上三个条件必须同时满足,缺一不可。
注:大多数路由器都是支持 UPnP 的,有的是默认开启,有的需要手工开启。
4. UPnP 协议体系
UPnP 是一个多层协议构成的框架体系,每一层都以相邻的下层为基础,同时又是相邻上层的基础。直至达到应用层为止。
最底部两层:IP 和 TCP
负责设备的 IP 地址。
第三层:传送层
这一层主要是将信息用特定的方式进行传送。
这个特定的方式就是指 HTTP、HTTPU、HTTPMU 协议,传送的数据就是按照 SSDP、GENA、SOAP 协议规定的 XML 数据。到这一层,已经解决了 UPnP 设备的 IP 地址和传送信息问题。
第四层:UPnP 设备体系定义
仅仅是一个抽象的、公用的设备模型。任何 UPnP 设备都必须使用这一层。
第五层:UPnP 论坛的设备定义层
在这个论坛中,不同电器设备由不同的专业委员会定义,例如:电视委员会只负责定义网络电视设备部分,空调器委员会只负责定义网络空调设备部分……,依此类推。
所有的不同类型的设备都被定义成一个专门的架构或者模板,供建立设备的时候使用。可以推知,进入这一层,设备已经被指定了明确用途。
当然,这些都必须遵守标准化的规范。从目前看,UPnP 已经可以支持大部分的设备:从电脑、电脑外设,移动设备和家用消费类电子设备等等,无所不包,随着这个体系的普及,将可能有更多的厂家承认这一标准,最终,可能演化为公认的行业标准。
最上层:应用层
由 UPnP 设备制造厂商定义的部分。
这一层的信息是由设备制造厂商来 “填充” 的,这部分一般有设备厂商提供的、对设备控制和操作的底层代码,然后,就是名称序列号呀,厂商信息之类的东西。
整个协议体系也可以参见这个图片:
UPnP 的一些术语
-
UUID(Universally Unique Identifier)
UUID 含义是通用唯一识别码,其目的是让分布式系统中的所有元素,都有唯一的辨识资讯,而不需要透过中央控制端来做辨识资讯的指定。其格式为 xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxxxxxx(8-4-4-16), 分别为当前日期和时间,时钟序列,全局唯一的 IEEE 机器识别号,如果有网卡,从网卡 mac 地址获得,没有网卡以其他方式获得。
-
UPC(Universal Product Code)
通用产品编码的缩写,它由 12 个数字构成,由统一编码委员会(Uniform Code Council)管理。
这个值可由 UPnP 制造商指定。
-
UDN(Unique Device Name)
单一设备名,基于 UUID,表示一个设备。在不同的时间,对于同一个设备此值应该是唯一的。
-
HTTPMU
在 UDP 上实现 HTTP 协议的多址传送
-
HTTPU
在 UDP 上实现普通的 HTTP 传送协议。
-
SSDP(Simple Service Discovery Protocol:SSDP)
简单服务发现协议,是内建在 HTTPU/HTTPMU 里,定义如何让网络上有的服务被发现的协议。 具体包括控制点如何发现网络上有哪些服务,以及这些服务的资讯,还有控制点本身宣告他提供哪些服务。 在 UPnP 工作流程的设备发现部分使用到该协议。
UPnP 工作流程
整个工作过程需要处理六个方面的内容,即地址分配、发现设备、对设备的描述、设备控制、设备事件、设备展示。
1. 寻址(Addressing)
地址是整个 UPnP 系统工作的基础条件,每个设备都应当是 DHCP 的客户。
当设备首次与网络建立连接后,利用 DHCP 服务,使设备得到一个 IP 地址。这个 IP 地址可以是 DHCP 系统指定的,也可以是由设备选择的。
2. 发现(Discovery)
可分成两种情况:
- 在有控制请求之后,在当前的网络中查找有无对应的可用设备
- 某一设备接入网络、取得 IP 地址之后,就开始向网络 “广播” 自己已经进入网络,即寻找控制请求
UPnP 使用简单发现协议(SSDP)来完成发现,这是一个工作在 UDP 上的 HTTP 协议。
当一个设备加入网络,它将向组播发送类似如下的消息:
NOTIFY * HTTP/1.1
Host:239.255.255.250:1900
Cache-control:max-age=1800
Location:http://192.168.0.1:49152/des.xml
Nt:upnp:rootdevice
Nts:ssdp:alive
Usn:uuid:de5d6118-bfcb-918e-0000-00001eccef34::upnp:rootdevice
复制代码
- Host:这里必须使用 IANA(InternetAssigned Numbers Authority)为 SSDP 预留的组播地址:239.255.255.250:1900
- Cache-control:max-age 的数值表明设备将在这段时间(单位为秒)后失效
- Location:设备描述文件的 URL
- Nt:Notification Type,这里的值 upnp:rootdevice)表明这是一个 “根设备”。每个设备可以有自己的子设备。
- Nts:Notification Sub Type,标准规定必须是 ssdp:alive。
- Usn:Unique Service Name,是一个设备实例的标识符。
通过这样的消息,控制点便可以知道网络中的设备。
当控制点加入网络时,它也将组播一个消息,用来发现设备:
M-SEARCH* HTTP/1.1
Host:239.255.255.250:1900
Man:"ssdp:discover"
Mx:5
ST:ssdp:rootdevice
复制代码
- Host:同上。
- Man:必须是 “ssdp:discover”。
- Mx:1 到 5 之间的一个值,表示最大的等待应答的秒数。
- ST:Seatch Targer,表示搜索的节点类型。
设备可以根据这样的搜索产生应答:
HTTP/1.1200 OK
Cache-control:max-age=1800
Ext:
Location:http://192.168.0.1:2345/xx.xml
Server:Microsoft-Windows-NT/5.1UPnP/1.0 UPnP-Device-Host/1.0
ST:ST:urn:schemas-upnp-org:service:ContentDirectory:1
USN:uuid:60b2e186-b084-44af-ac09-1c64ea1bb364::urn:schemas-upnp-org:service:ContentDirectory:1
复制代码
3. 描述(Description)
简单说,这是声明 “自己” 是什么样的设备,例如名称、制造厂商、序列号码等等。
刚开始 “发现” 设备后,控制点对这个设备的 “了解” 还很少,需要依据 URL 找到该设备的描述文件,从这些文件中读取更多的描述信息。
描述信息的范围很广,一般都是由设备的制造厂商提供的。主要的描述项目有:控制的模式名称和模式号码、设备序列号、制造厂商名称、厂商的 WEB 的 URL 等等。这些一般都存放在特定的 XML 文件中。这个 XML 文件在 SSDP 的响应数据包中有告知:Location:http://192.168.0.1:2345/xx.xml
该 XML 中的描述就可以抽象成如下图片:
设备的描述通过 HTTP 协议来完成。例如下面就是一个真实设备返回的 XML 信息:
对于每一个设备:
-
deviceType:设备类型,格式为:“urn:schemas-upnp-org:device:deviceType:v”
这里 deviceType 和 v 是由设备定义的
-
friendlyName:一个更加友好的设备名
-
Manufacturer:制造商
-
modeName:型号
-
UDN:Unique Device Name,设备的 UUID
-
serviceList:服务列表。
对于每一个服务:(注意:这里的三个 URL 很重要)
- serviceType:与 deviceType 类似,这里的后两段由服务定义
- serviceId:服务 ID,通常于 serviceType 对应
- controlURL:用于控制的 URL
- eventSubURL:用于订阅事件的 URL
- SCPDURL:服务描述的 URL
一个服务描述(即 SCPD 地址中的 XML)的示例如下:
Service Control Protocol Document 的 XML 最外层主要由两个部分构成:
actionList 定义了 “行为”
- 每个 action 由 name 和 argumentList 组成
- 每个 argument 都由一个名称(name)、传递方向(direction,取值 in 或者 out)、关联状态变数(relatedStateVariable)组成。
- 状态变数用来标明 UPnP 设备或程序的一些状态。一个程序可以订阅(subscribe)状态的变化,从而得到通知。而一个参数之所以必须关联一个状态变数,是因为状态变数的类型决定了参数的类型。
serviceStateTable 定义了 “状态变数”
4. 控制(Control)
控制点找到设备描述之后,会从描述中 (即 XML 文件)“提炼” 出要进行的操作并获悉所有的服务。
对每个 UPnP 设备来说,这些描述必须是很确切、很详细的,描述中可能包含有命令或行为列表、服务响应信息、用到的参数等等。
对于服务的每个行为,也伴有描述信息:主要是整个服务进行期间的变量、变量的数据类型、可用的取值范围和事件的特征。
要控制某个设备,控制点必须先发送一个控制行为请求,要求设备开始服务,然后再按设备的 URL 发送相应的控制消息,控制消息就是放置在 SCPDXML 文件中的那些 SOAP 格式的信息。最后,服务会返回响应信息,指出服务是成功或是失败。
UPnP 使用SOAP完成控制。SOAP 工作在 HTTP 上,使用 XML 来描述远程调用并返回结果。
actionName 和 argumentName 就是在协议描述(SCPDXML)中定义的行为名称和参数名称。
而调用的结果同样以 XML 方式返回,对应格式为:
这里给出一个具体的例子,获取设备外网 IP:
5. 事件(Eveting)
在服务进行的整个时间内,只要变量值发生了变化或者模式的状态发生了改变,就产生了一个事件,系统将修改上述提到的事件列表的内容。
随之,事件服务器把事件向整个网络进行广播。另一方面,控制点也可以事先向事件服务器预约事件信息,保证将该控制点感兴趣的事件及时准确地传送过来。
广播或预约事件,传送的都是事件消息,事件消息也放在 XML 文件中,使用的格式是 GENA。
设备投入工作之前的准备―――初始化过程,也是一个事件,初始化需要的各种信息也是用事件消息传送的。包括的内容主要是:变量初始值,模式的初始状态等等。
订阅的 URL 事在服务描述中给出的(如 eventSubURL)。
SUBSCRIBE publisher pathHTTP/1.1
HOST: publisherhost:publisher port
USER-AGENT: OS/versionUPnP/1.1 product/version
CALLBACK: <deliveryURL>
NT: upnp:event
复制代码
- NT 取 upnp:event 表示订阅事件
- CALLBACK 的值是回调的 URL。
与之对应的事订阅的应答:
HTTP/1.1 200 OK
DATE: when response was generated
SERVER: OS/version UPnP/1.1 product/version
SID: uuid:subscription-UUID
CONTENT-LENGTH: 0
TIMEOUT: Second-1800
复制代码
- SID:本订阅的标识符,通常使用 UUID
- TIMETOUT:这里的值表示本订阅的有效期。这意味着在超时前,必须续订。
续订的请求与订阅类似,只是附加了 SID:
SUBSCRIBE publisher path HTTP/1.1
HOST: publisher host:publisher port
SID: uuid:subscription UUID
复制代码
退订也类似:
UNSUBSCRIBE publisher path HTTP/1.1
HOST: publisher host:publisher port
SID: uuid:subscription UUID
复制代码
事件消息:
NOTIFY delivery path HTTP/1.1
HOST: delivery host:delivery port
CONTENT-TYPE: text/xml; charset="utf-8"
NT: upnp:event
NTS: upnp:propchange
SID: uuid:subscription-UUID
SEQ: event key
CONTENT-LENGTH: bytes in body
<?xml version="1.0"?>
<e:propertysetxmlns:e="urn:schemas-upnp-org:event-1-0">
<e:property>
<variableName>new value</variableName>
</e:property>
</e:propertyset>
复制代码
- 每个产生事件的状态变数对应一个 e:property 标识,variableName 是变数名
- SEQ 是消息的顺序号,从 0 开始
- 当订阅者收到消息之后,必须在 30 秒内发送确认
- 如果订阅者没有确认,设备仍然会发送之后的消息,直到本次订阅超时
6. 展示(Presentation)
只要得到了设备的 URL,就可以取得该设备表达页面的 URL,然后可以将此表达 (展示) 纳入用户的本地浏览器上。
这部分还包括与用户对话的界面,以及与用户进行会话的处理。