UPNP自动端口映射的实现与路由器UPNP相关资料

UPNP的全称是 Universal plug-and-play( 通用即插即用).UPnP 是针对智能家电、无线设备以及各种外观尺寸的个人电脑的普遍对等(peer-to-peer)网络连接而设计的一种架构。它旨在为家庭、小型企业、公共场 所中或连接到互联网的ad-hoc 网或未管理网络提供易于使用、灵活且基于标准的连接。(引自 这里.)

  我们这里用到的自动端口映射只是UPNP的一个小应用。按照UPNP的相关规范,UPNP网络的第0步是寻址(获得一个IP地址,在我要解决的问题中这不是一个问题。)       
    第1步是发现,控制点在网上搜索感兴趣的设备,而设备向网络中的控制点宣告其服务。对于自动端口映射来说就是发现带UPNP功能的路由器。

 发现这个过程主要有两步。第一,使用数据报套接字向239.255.255.250:1900,发送一条多播请求,格式如下

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

      这个多播请求的含义如下:M-SEARCH SSDP协议定义的搜索请求方法。HOST必须是这个多播地址。MAN的值也必须是"ssdp:discover" 不可少了双引号。MX的含义是最长等待时间,可以自己设置。ST表示search target 搜索目标。我们在这里用找根设备。另外在编程中我们要在每一行后面加上"rn" 表示换行。(详见源码 UPNPNAT.discovery()).

      第二步,如果你的网络存在一个UPNP设备的话,为了被找到,设备必须向发送查找请求的多播通道的源 IP 地址与端口发送响应信息。所以你可以从239.255.255.250:1900这个地址接收到响应消息。类似下面的消息。

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

      接下来我们要从里面获得我们要的消息。首先,我们必须找到" 200 OK ",说明没有错误发生,否则一切免谈。接着,我们要找到LOCATION项,获得设备描述URL。(程序中的处理归根到底就是一个子字符的查找。)

       到这里,我们的第一步“发现”完成。


   第2步是描述。在第1步中我们往往能获得一个设备的描述URL,在第2步中我们要通过一个URL,下载一个XML文件。并从中找到有关设备的类型,服务类型,控制URL,事件触发URL等。

我们同样分两步进行,首先下载设备描述文件。(请看源码中UPNPNAT::get_description()函数)
    1.解析描述文件的URL,获得主机(host)、端口(port)、路径(path).(parseUrl函数)
    2.连接到host:port (tcp_connect 函数)
    3.构造类似

                                                GET path HTTP/1.1
                                                Host: host:port
的信息(第二行下要一个空行),并通过刚才的TCP 套接字,发送到路由器。(sprintf ,send 函数).
    4.接收数据,我使用flag为 MSG_WAITALL的recv函数,函数一直阻塞直到数据全部读完。 数据最终保存在std::string description_info中。
    我想通过浏览器下载这个文件的过程是类似的吧。
 
    然后,解析这个XML文件。(请看 源码 中UPNPNAT:: parser_description()函数)
我们找到"root"的"deviceType"是 "urn:schemas-upnp-org:device:InternetGatewayDevice:1"的"device" childNode ,获得这个"device"的"deviceList",记为A。
    找到A的"deviceType"是 "urn:schemas-upnp-org:device:WANDevice:1"的"device" childNode ,获得这个"device"的"deviceList",记为B。
    找到B的"deviceType"是 "urn:schemas-upnp-org:device:WANConnectionDevice:1"的"device" childNode ,获得这个"device"的"serviceList",记为C。
    找到C的"serviceType"是 "urn:schemas-upnp-org:service:WANIPConnection:1""urn:schemas-upnp-org:service:WANPPPConnection:1" 的"service"  childNode ,记为D.
    获得D的"controlUrl"保存在std::string control_url中。
    但是这里获得control_url一般为相对URL,所以要从"root"下面,找到"URLBase"的值,(如果是空,则用describe_url的"htpp://xxx.xxx.xxx.xxx:xxxx"部分代替.)
    最后在相对的control_url前加上URLBasr 获得完整的control_url.
 
    至此,第二步“获得控制URL”完成。
                                      

    第3步是控制。通过第2步获得的控制URL,通过向其发送控制消息(同样用XML描述)来实现某些功能。对于自动端口映射来说就是查看、增加、删除等。

在这里我先把各种控制信息的格式说明一下。(下面的rn都是表示换行,我输入不了反斜杠。)

  • 增加端口映射。 "AddPortMapping"
  • "<NewRemoteHost></NewRemoteHost>rn"                              "<NewExternalPort>ExternalPort</NewExternalPort>rn"                               "<NewProtocol>Protocol</NewProtocol>rn"                                      "<NewInternalPort>InternalPort</NewInternalPort>n"
    "<NewInternalClient>InternalClient</NewInternalClient>rn"                
    "<NewEnabled>1</NewEnabled>rn"
    "<NewPortMappingDescription>PortMappingDescription"       "</NewPortMappingDescription>rn"      
    "<NewLeaseDuration>LeaseDuration</NewLeaseDuration>rn"
  • 删除端口映射 "DeletePortMapping"
  • "<NewRemoteHost></NewRemoteHost>rn" "<NewExternalPort>ExternalPort</NewExternalPort>rn"   "<NewProtocol>Protocol</NewProtocol>rn"
  • 获得端口映射信息 "GetGenericPortMappingEntry"
  • "<NewPortMappingIndex>PortMappingIndex</NewPortMappingIndex>"  "<NewRemoteHost></NewRemoteHost>rn"   "<NewExternalPort></NewExternalPort>rn" "<NewProtocol></NewProtocol>rn"     "<NewInternalPort></NewInternalPort>rn" "<NewInternalClient></NewInternalClient>rn" "<NewEnabled>1</NewEnabled>rn"  "<NewPortMappingDescription>"                                          "</NewPortMappingDescription>rn"            "<NewLeaseDuration></NewLeaseDuration>rn"

     其中斜体部分需要在编程是填入的。ExternalPort 外部端口。InternalPort内部端口。这 两者一般就填映射的端口。Protocal 填TCP或UDP。InterClient 一般就是本地IP地址。PortMappingDescription 填写端口映射的描述,比如什么程序建立了这个端口。LeaseDuration 是映射的持续时间,用0表示不永久。PortMappingIndex 是端口映射索引,路由上第几个映射。

     我们再来看下面这个XML文档结构。

     "<?xml version="1.0" encoding="utf-8"?>rn"
     "<s:Envelope xmlns:s="
     ""http://schemas.xmlsoap.org/soap/envelope/" "
     "s:encodingStyle="                                  
     ""http://schemas.xmlsoap.org/soap/encoding/">rn"
     "<s:Body>rn"
     "<u:actionName xmlns:u="serviceType">rn"
     "actionParams</u:actionName>rn"
     "</s:Body>rn"
     "</s:Envelope>rn"

     我们在actionName 处填入"AddPortMapping" "DeletePortMapping" "GetGenericPortMappingEntry"。serviceType 处填入设备的服务类型。"urn:schemas-upnp-org:service:WANIPConnection:1"或"urn:schemas-upnp-org:service:WANPPPConnection:1"。actionParams 填入上面的各种控制信息。

    最后在前面加上HTTP头。

    "POST path HTTP/1.1rn"
    "HOST: host:portrn"     
    "SOAPACTION:"serviceType#actionName"rn" 
    "CONTENT-TYPE: text/xml ; charset="utf-8"rn"
    "Content-Length: contentLength rnrn"

    path host port 意思很明显。contentLength面那个XML文档的长度。

  然后连接到host:port,发送到整个信息即可完成控制            

    第4步事件触发和第5步展示在自动端口映射没用用到。有兴趣可以自己看文档。

-----------------------------------------------------------------------------------------------------------

UPNP自动端口映射的实现
2008年02月25日 星期一 22:54

UPNP是微软定义的一个网络标准,大概是通过网络发送xml定义的数据实现控制网络上的设备等。感兴趣的可以自己到 UPnP™ Forum (http://www.upnp.org/)上下载资料,或者网上搜索一下了。

端口映射: 就是把内网机器的网络端口映射的外部网络的相应端口上,这很多路由器都支持这样的功能。这样人家访问外网ip相应端口时就可以跳转到内网来。很多网络下载 工具都实现这样的功能的,这样人家从外部就可以连接到你内部的机器,便于网络操作,像BitComet,PPstream,迅雷等。

我就是看到BitComet的这个功能才学着自己也做一个。因为路由器实现了UPNP的标准接口了,就可以通过upnp来做到编程来做端口映射,免得去找网管了^_^,就是找了他也不一定给你弄呵呵。

参考资料:

微软的UPnP APIs:http://msdn2.microsoft.com/en-us/library/aa382303.aspx

UPNP标准 设备Internet Gateway Device (IGD) V 1.0 http://www.upnp.org/standardizeddcps/igd.asp

另外如果想实现UPNP的服务端的,好像也有Intel的 UPNP SDK库可以用的。网上也有人很多人写了自动端口映射的程序,我就找到一个使用 socket和 自己处理xml文件的方法实现的。不过我觉得微软有API了用起来也很简单的,不用自己处理那些东西,所以就把msdn上面的实例代码copy一个过来 了,改了一下,还真可以。

大概流程就是:

InternetGatewayDevice -》WANDevice-》WANConnectionDevice-》WANIPConnection或者 WANPPPConnection,WANIPConnection的 DeletePortMapping,AddPortMapping,GetGenericPortMappingEntry,GetExternalIPAddress 等方法就可以用来做端口映射了。

这是两个实例图:

注意在windows的防火墙里面开放 “UPNP框架” 相应的端口UPNP才能正常工作,因为它是通过网络来进行的。

程序的运行效果如图,我在自己的机器上可以正常显示端口映射,并且进行删除,添加等操作了。发现有的人用PPsteam,迅雷啊等,那些程序都添加了端口映射了。不知道我把他们的映射删了,我的网络会不会快点,哈哈

一些关键代码:

case IDC_START:
    {
     BSTR bstrTypeURI = NULL;
     bstrTypeURI =    SysAllocString(L"urn:schemas-upnp-org:device:InternetGatewayDevice:1");
     IUPnPDevice * internetGatewayDevice =NULL;
     if (FindUPnPDevice(bstrTypeURI,&internetGatewayDevice))
     {
      char devicename[256];
      if (GetDeviceFriendlyName(internetGatewayDevice,devicename))
      {
       hwndTreeView = GetDlgItem(hDlg,IDC_TREE1);
       AddItemToTree(hwndTreeView, devicename, 1);
      }
      SysFreeString(bstrTypeURI);
      bstrTypeURI = SysAllocString(L"urn:schemas-upnp-org:device:WANDevice:1");
      IUPnPDevice * wanDevice =NULL;
      if (GetChildDevice(internetGatewayDevice ,bstrTypeURI ,&wanDevice))
      {
       if (GetDeviceFriendlyName(wanDevice,devicename))
       {
        hwndTreeView = GetDlgItem(hDlg,IDC_TREE1);
        AddItemToTree(hwndTreeView, devicename, 2);
       }
       SysFreeString(bstrTypeURI);
       bstrTypeURI = SysAllocString(L"urn:schemas-upnp-org:device:WANConnectionDevice:1");
       IUPnPDevice * wanConnectionDevice =NULL;
       if (GetChildDevice(wanDevice ,bstrTypeURI ,&wanConnectionDevice))
       {
        if (GetDeviceFriendlyName(wanConnectionDevice,devicename))
        {
         hwndTreeView = GetDlgItem(hDlg,IDC_TREE1);
         AddItemToTree(hwndTreeView, devicename, 3);
        }

        SysFreeString(bstrTypeURI);
        bstrTypeURI = SysAllocString(L"urn:schemas-upnp-org:service:WANIPConnection:1");
        IUPnPService * WANIPConnection =NULL;
        if (GetService(wanConnectionDevice ,bstrTypeURI ,&WANIPConnection))
        {

         hwndTreeView = GetDlgItem(hDlg,IDC_TREE1);
         AddItemToTree(hwndTreeView, "WANIPConnection", 4);

        }
        if (WANIPConnection)
        {  
                    
                                char IPAddress[64];
         if (InvokeGetExternalIPAddress(WANIPConnection,IPAddress))
         {
             hwndTreeView = GetDlgItem(hDlg,IDC_TREE1);
                            AddItemToTree(hwndTreeView, IPAddress, 4);
         }

         hwndTreeView = GetDlgItem(hDlg,IDC_TREE1);
                       AddItemToTree(hwndTreeView, "RemoteHost,ExternalPort,PortMappingProtocol,InternalPort,InternalClient,PortMappingEnabled,PortMappingDescription,PortMappingLeaseDuration", 4);
                               
                                char PortMapping[256];
         int i =0;
                         while(InvokeGetGenericPortMappingEntry(WANIPConnection,i,PortMapping))
         {
             hwndTreeView = GetDlgItem(hDlg,IDC_TREE1);
                            AddItemToTree(hwndTreeView, PortMapping, 4);
          i ++;
         }


         WANIPConnection->Release();
        }
        SysFreeString(bstrTypeURI);
        bstrTypeURI = SysAllocString(L"urn:schemas-upnp-org:service:WANPPPConnection:1");
        IUPnPService * WANPPPConnection =NULL;
        if (GetService(wanConnectionDevice ,bstrTypeURI ,&WANPPPConnection))
        {

         hwndTreeView = GetDlgItem(hDlg,IDC_TREE1);
         AddItemToTree(hwndTreeView, "WANPPPConnection", 4);

        }
        if (WANPPPConnection)
        {
         WANPPPConnection->Release();
        }
       }
       if (wanConnectionDevice)
       {
        wanConnectionDevice->Release();
       }

      }
      if (wanDevice)
      {
       wanDevice->Release();
      }

     }
     SysFreeString(bstrTypeURI);
     if (internetGatewayDevice)
     {
      internetGatewayDevice->Release();
     }
    }

    break;

============

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值