通过UPNP实现内网端口映射

最近在写一个下载软件,HTTP和FTP实现起来比较简单,通过JDK自带的包就能实现。但是P2P的下载实现起来确实比较麻烦,要实现的东西比较多。

由于P2P需要本机的端口被外网访问,所以这里就需要用到这个端口映射的技术。

首先我们需要使用M-SEARCH来扫描内网里面的设备和服务。
M-SEARCH这个是SSDP协议里面的一个请求方法,使用的是HTTPU协议,其实和HTTP协议很像,只不过是用的UDP实现的一个多播请求,只有请求头,没有body

M-SEARCH * HTTP/1.1
HOST: 239.255.255.250:1900
MX: 2
ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1
MAN: "ssdp:discover"

上面的请求头除了MX都是固定的。

然后就能收到一个响应,响应头里面有个location,访问这个地址获取一个XML
然后找到<serviceType>urn:schemas-upnp-org:service:WANIPConnection:1</serviceType>这个服务,访问对应的SCPDURL,可以得到类似WebService里面的接口描述。

这个里面就有很多接口,这里只用到一下几个:

  1. 添加端口映射:AddPortMapping
  2. 删除端口映射:DeletePortMapping
  3. 获取外网IP地址:GetExternalIPAddress
  4. 获取端口映射:GetSpecificPortMappingEntry

参数在XML描述里面已经有了,很简单易懂。

方法请求时,直接使用HTTP POST请求即可,请求body就是XML,请求头添加SOAPAction

例如获取外网IP地址,使用POST请求:

添加请求头:

SOAPAction: "urn:schemas-upnp-org:service:WANIPConnection:1#GetExternalIPAddress"

请求XML:

<?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:GetExternalIPAddress xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1"/>
    </s:Body>
</s:Envelope>

其他的请求基本上修改请求头后面的方法名称,还有s:Body里面的请求参数即可。

参考代码:https://gitee.com/acgist/snail/tree/master/src/main/java/com/acgist/snail/net/upnp

测试代码:

https://gitee.com/acgist/snail/blob/master/src/test/java/com/acgist/main/UpnpClientTest.java
https://gitee.com/acgist/snail/blob/master/src/test/java/com/acgist/main/UpnpServiceTest.java