1.搜索目前网络上的UPnP设备。Control Point生成一个MulticastSocket,绑定多播地址239.255.255.250 1900端口,然后发送一个搜索请求,默认将TimeToLive设为4,该请求在UPnP Device Architecture中是如下定义的:
M-SEARCH * HTTP/1.1
HOST:
239.255.255.250:
1900
MAN: "
ssdp:discover"
MX:
seconds to delay response
ST:
search target
要注意的一点,该请求基于HTTPMU(HTTP Multicast over UDP),上面这些信息都是属于HTTP Header,没有HTTP Body,而HTTP Header和HTTP Body之间是有一个空白行分隔的,通过Socket发送出去的时候不要忘了发送那个空白行,以后提到的消息也都请注意这一点。
UPnP设备收到该请求后会延时相当于Random.nextInt(MX)返回响应,以便Control Point能够有充足时间来处理请求;ST是要搜索的目标,如果搜索所有的设备和服务,则为ssdp:all,如果是搜索根设备,则为upnp:rootdevice,象大部分程序中使用UPnP是为了找到支持UPnP的网关来动态映射端口,则可以赋为urn:schemas-upnp-org:device:InternetGatewayDevice:1。
下面是一个示例,搜索所有的根设备,这些设备收到该消息后应该在0~3秒内返回响应:
M-SEARCH * HTTP/1.1
HOST: 239.255.255.250:1900
MAN: "ssdp:discover"
MX: 3
ST: upnp:rootdevice
2.网络上的UPnP设备返回响应。如果该UPnP设备和要搜索的UPnP设备匹配,则该设备会返回一个响应,响应的格式在UPnP Device Architecture中是如下定义的:
HTTP/1.1 200 OK
CACHE-CONTROL: max-age = seconds until advertisement expires
DATE: when response was generated
EXT:
LOCATION: URL for UPnP description for root device
SERVER: OS/version UPnP/1.0 product/version
ST: search target
USN: advertisement UUID
max-age表示收到该消息后若干秒内没有收到该设备发出的任何通知消息,就认为该设备已经不存在网络上了;LOCATION表示该设备的描述文件,用于确定该设备包含哪些逻辑设备和哪些服务等等;USN表示Unique Service Name。
比如对于上面那条示例搜索消息,我的ADSL返回的响应是:
HTTP/1.1 200 OK
CACHE-CONTROL:max-age=1800
EXT:
LOCATION:http://10.0.0.138:80/IGD.xml
SERVER:SpeedTouch 510 4.0.2.0.1 UPnP/1.0 (14E31Y7)
ST:upnp:rootdevice
USN:uuid:UPnP-SpeedTouch510-1_00-90-D0-7F-AD-37::upnp:rootdevice
3.接下来就是取得该设备的描述文件,用于确定该设备信息和所支持的功能。通过上面的响应信息的LOCATION属性,可以得到一个URL,可以通过HTTP请求该URL得到该设备的描述文件。注意,仅仅在搜索和通知的时候是基于UDP的,其余的都是基于TCP的。设备描述文件定义可在UPnP Device Architecture中找到,比较长就不贴了。
比如上面我的ADSL返回LOCATION是http://10.0.0.138:80/IGD.xml,访问该地址,返回的一个XML片断是:
<?
xml version="1.0"
?>
<
root
xmlns
="
urn:schemas-upnp-org:device-1-0
"
>
<
URLBase
>
http://10.0.0.138
</
URLBase
>
<
device
>
<
deviceType
>
urn:schemas-upnp-org:device:InternetGatewayDevice:1
</
deviceType
>
<
friendlyName
>
SpeedTouch 510 (14E31Y7)
</
friendlyName
>
<
manufacturer
>
THOMSON multimedia
</
manufacturer
>
<
manufacturerURL
>
http://www.thomson-multimedia.com
</
manufacturerURL
>
<
modelDescription
>
DSL Internet Gateway
</
modelDescription
>
<
modelName
>
SpeedTouch
</
modelName
>
<
modelNumber
>
510
</
modelNumber
>
<
modelURL
>
http://www.speedtouch.com
</
modelURL
>
<
serialNumber
>
14E31Y7
</
serialNumber
>
<
UDN
>
uuid:UPnP-SpeedTouch510-1_00-90-D0-7F-AD-37
</
UDN
>
<
presentationURL
>
/index.htm
</
presentationURL
>
<
serviceList
>
<
service
>
<
serviceType
>
urn:schemas-upnp-org:service:Layer3Forwarding:1
</
serviceType
>
<
serviceId
>
urn:upnp-org:serviceId:layer3f
</
serviceId
>
<
controlURL
>
/upnp/control/layer3f
</
controlURL
>
<
eventSubURL
>
/upnp/event/layer3f
</
eventSubURL
>
<
SCPDURL
>
/Layer3Forwarding.xml
</
SCPDURL
>
</
service
>
</
serviceList
>
</
device
>
</
root
>
在UPnP规范中规定,一个设备可以包含若干的嵌入设备和服务。比如对于最常用到的Internet Gateway Device中,UPnP InternetGatewayDevice模板中规定
可以看到在根设备中包含了Layer3 Forwarding Service和两个嵌入设备:WANDevice和LANDevice,而WANDevice下面又包含了若干WANConnectionDevice等等。
4.得到设备所提供的服务描述。在刚才的设备描述中有一个ServiceList节点,该节点下每个Service节点都包含一个
SCPDURL节点,这个就是服务描述文件所在的位置,比如上面我的ADSL中Layer3 Forwarding Service服务描述文件的位置就是/Layer3Forwarding.xml,再组合URLBase节点属性值http://10.0.0.138,即得到该服务描述文件URL为http://10.0.0.138/Layer3Forwarding.xml,该文件详细的描述了该服务所提供的操作列表以及相应的参数和参数范围。
5.调用服务所提供的操作。调用是通过发送相应SOAP消息到该服务的控制URL上来完成的。该信息在UPnP Device Architecture中是如下定义的:
POST path of control URL HTTP/1.1
HOST: host of control URL:port of control URL
CONTENT-LENGTH: bytes in body
CONTENT-TYPE: text/xml; charset="utf-8"
SOAPACTION: "urn:schemas-upnp-org:service:serviceType:v#actionName"
<s:Envelope
xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<s:Body>
<u:actionName xmlns:u="urn:schemas-upnp-org:service:serviceType:v">
<argumentName>in arg value</argumentName>
other in args and their values go here, if any
</u:actionName>
</s:Body>
</s:Envelope>
就用在IGD设备上增加端口映射这个操作来举例,将网关的5678端口映射到内网10.0.0.1的8765端口,该操作被发送的SOAP消息如下:
POST /upnp/control/wanpppcpppoe HTTP/1.0
CONTENT-TYPE: text/xml; charset="utf-8"
HOST: 10.0.0.138:80
CONTENT-LENGTH: 649
SOAPACTION: "urn:schemas-upnp-org:service:WANPPPConnection:1#AddPortMapping"
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<s:Body>
<u:AddPortMapping xmlns:u="urn:schemas-upnp-org:service:WANPPPConnection:1">
<NewRemoteHost></NewRemoteHost>
<NewExternalPort>5678</NewExternalPort>
<NewProtocol>tcp</NewProtocol>
<NewInternalPort>8765</NewInternalPort>
<NewInternalClient>10.0.0.1</NewInternalClient>
<NewEnabled></NewEnabled>
<NewPortMappingDescription></NewPortMappingDescription>
<NewLeaseDuration></NewLeaseDuration>
</u:AddPortMapping>
</s:Body>
</s:Envelope>
操作成功,设备的返回值是:
HTTP/1.0 200
CONTENT-TYPE: text/xml; charset="utf-8"
SERVER: SpeedTouch 510 4.0.2.0.1 UPnP/1.0 (14E31Y7)
CONTENT-LENGTH: 304
Connection: close
EXT:
<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<s:Body>
<m:AddPortMappingResponse xmlns:m="urn:schemas-upnp-org:service:WANPPPConnection:1"></m:AddPortMappingResponse>
</s:Body>
</s:Envelope>
6.在设备一些属性变化了的时候,如果Control Point订阅了它的事件通知,则它会发送相应的通知给Control Point。这部分我还未研究透彻,有待进一步了解。
这些只是一个基本的Control Point的流程,详细的部分可以访问 http://www.upnp.org/download/UPnPDA10_20000613.htm,看看UPnP Device Architecture,里面有详细的描述。
大部分程序员对UPnP的兴趣只限于动态映射IGD设备端口,因此可以去看看IGD设备规范,网址是 http://www.upnp.org/standardizeddcps/igd.asp
在Window下实现动态端口映射,要做的完美一点,最好是按照前面一篇文章说的步骤,首先检测Window下ICF是否打开,打开的话就得调用ICF的API在防火墙上打开相应的端口,要不多播信息这些都被防火墙拦截下来了,也就没有办法找到UPnP设备了。不少非微软的应用都没有注意这一点,让我在初步了解UPnP技术的时候郁闷了半天,以为Windows 2003取消了对UPnP的支持就没有办法在Windows 2003下使用UPnP设备了,最后才发现是防火墙惹得祸。
因此Windows支不支持UPnP没有关系,如果不支持也只是限于没有办法调用微软提供的API而已:)