在写完Object 672后,软件的一个致命问题暴露出来,如果服务器和客户端都在内网环境下,即双方都通过NAT来接触外网,那么此时客户端是无法直接和服务器交流的。

解决方案可以是:

1:把服务器部署在不存在NAT的公网环境下。

2:使用常见的NAT穿透方法比如UDP打洞,或者STUN协议,但是这些方法都需要另一个已知的部署在公网环境下的服务器。

3:就是这篇文章主要讨论的方案,即不需要部署任何公网环境下的服务器,通过路由器支持的UPnP协议来把内网的接口绑定到公网接口上。

UPnP的一大优势就是不会像UDP打洞那样,内网接口不需要先向外部接口发送UDP包来把绑定的公网接口告诉NAT,而且对于对称NAT,UDP打洞是无效的。而UPnP一旦设置成功后,内网接口完全以绑定的公网接口暴露在公网中。

 

演示程序的运行是这样的:

image

 

具体过程:

1. 输出用户Host Name和内网IP地址。

2. 通过UPnP把内网IP地址,内部端口号绑定到一个外部端口号上。

3. 通过HTTP从外部网站获取公网IP地址。

4. 在内网中创建TCP Socket服务器。

5. 建立另一个TCP Socket客户端,然后尝试连接上面获取的公网IP和UPnP绑定的外部端口。

6. 如果一切没有问题的话,此时会成功连接到服务器,并收到回应!

 

在.NET环境下使用Windows的UPnP组件需要现在工程中引用:NATUPnP 1.0 Type Library,这是一个COM类库。

下面开始逐句分析源代码,源代码均拟用户已加入下列命名空间:

using System.Net;                     

using System.Net.Sockets;            

using System.Text.RegularExpressions;  //提取IP时的正则

using System.Threading.Tasks;          //Task

using System.IO;                       //读取服务器信息用到StreamReader

using NATUPNPLib;                      //Windows UPnP COM组件

 

首先输出本机(也就是内网接口信息),这个很简单了:

//获取Host Name

var name = Dns</