C#:使用UPnP来穿透NAT使内网接口对外网可见
在写完Object 672后,软件的一个致命问题暴露出来,如果服务器和客户端都在内网环境下,即双方都通过NAT来接触外网,那么此时客户端是无法直接和服务器交流的。
解决方案可以是:
1:把服务器部署在不存在NAT的公网环境下。
2:使用常见的NAT穿透方法比如UDP打洞,或者STUN协议,但是这些方法都需要另一个已知的部署在公网环境下的服务器。
3:就是这篇文章主要讨论的方案,即不需要部署任何公网环境下的服务器,通过路由器支持的UPnP协议来把内网的接口绑定到公网接口上。
UPnP的一大优势就是不会像UDP打洞那样,内网接口不需要先向外部接口发送UDP包来把绑定的公网接口告诉NAT,而且对于对称NAT,UDP打洞是无效的。而UPnP一旦设置成功后,内网接口完全以绑定的公网接口暴露在公网中。
演示程序的运行是这样的:
具体过程:
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组件
首先输出本机(也就是内网接口信息),这个很简单了:
var name =Dns.GetHostName();
Console.WriteLine("用户:"+ name);
//从当前Host Name解析IP地址,筛选IPv4地址是本机的内网IP地址。
var ipv4 =Dns.GetHostEntry(name).AddressList.Where(i => i.AddressFamily ==AddressFamily.InterNetwork).FirstOrDefault();
Console.WriteLine("内网IP:"+ ipv4);
接 下来就是设置UPnP了,首先需要初始化UPnPNAT类型(他是一个接口,只不过通过CoClass特性把执行导向UPnPNATClass类型),接 着通过UPnPNAT的StaticPortMappingCollection来添加或者删除UPnP绑定。注意在没有路由器或者路由器的UPnP不开 启的情况下,StaticPortMappingCollection属性可能会返回null。
代码如下:
Console.WriteLine("设置UPnP");
//UPnP绑定信息
;
;
var description ="Mgen测试";
//创建COM类型
var upnpnat =newUPnPNAT();
var mappings = upnpnat.StaticPortMappingCollection;
//错误判断
if (mappings ==null)
{
Console.WriteLine("没有检测到路由器,或者路由器不支持UPnP功能。");
return;
}
//添加之前的ipv4变量(内网IP),内部端口,和外部端口
mappings.Add(eport, "TCP", iport, ipv4.ToString(), true, description);
Console.WriteLine("外部端口:{0}", eport);
Console.WriteLine("内部端口:{0}", iport);
如果成功后,你应该可以在路由器的UPnP选项中看到这些数据:
设置好UPnP后,开始获取外网IP地址,可以通过这个网址(http://checkip.dyndns.org/)。
此时只需要发送一个HTTP GET请求,然后把返回的HTML中的IP地址提取出来就可以了,我们用正则来提取IP地址。
代码如下:
//外网IP变量
string eip;
//正则
var regex =@"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b";
using (var webclient =newWebClient())
{
var rawRes = webclient.DownloadString("http://checkip.dyndns.org/");
eip =Regex.Match(rawRes, regex).Value;
}
Console.WriteLine("外网IP:"+ eip);
OK,这个时候(如果一切顺利的话),一切准备工作都做好了。我们有了:内网IP,内部端口,外网IP,外部端口。那么就可以做一个TCP连接做测试了。
直接建立一个TCP服务端,代表在NAT下的服务器,注意端口号要绑定到UPnP设置时的内部端口。
代码:
//在NAT下的服务器
var socket =newSocket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//绑定内网IP和内部端口
socket.Bind(newIPEndPoint(ipv4, iport));
);
//在另一个线程中运行客户端Socket
Task.Run(() =>
{
);
ClientSocket(eip, eport);
});
//成功连接
var client = socket.Accept();
//服务器向客户端发送信息
client.Send(Encoding.Unicode.GetBytes("=== 欢迎来到Mgen的服务器!==="+Environment.NewLine));
Console.ReadKey(false);
上 面的ClientSocket方法就是客户端的Socket连接执行,注意TCP协议是不保留数据边界的,因此服务器在发送消息时,后面加了个换行符 (Environment.NewLine),然后在客户端接受数据时,使用Socket –> NetworkStream –> StreamReader的嵌套组合,最后由StreamReader的ReadLine读取数据,这样确保会读到最后的换行符。
ClientSocket方法的执行代码:
//ip参数和port参数是公网的IP地址,和UPnP中的外部端口
staticvoid ClientSocket(string ip, int port)
{
try
{
Console.WriteLine("建立客户端TCP连接");
var socket =newSocket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Connect(newIPEndPoint(IPAddress.Parse(ip), port));
using (var ns =newNetworkStream(socket))
using (var sr =newStreamReader(ns, Encoding.Unicode))
{
Console.WriteLine("收到来自服务器的回应:");
Console.WriteLine(sr.ReadLine());
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
OK。
源代码下载 下载页面 注意:链接是微软SkyDrive页面,下载时请用浏览器直接下载,用某些下载工具可能无法下载 源代码环境:Microsoft Visual Studio Express 2012 for Windows Desktop 注意:源代码不包含引用的外部类库文件
出处:http://www.cnblogs.com/cuihongyu3503319/archive/2013/02/05/2892764.html
C#:使用UPnP来穿透NAT使内网接口对外网可见的更多相关文章
- frp内网 穿透映射使内网svn可外网访问
起因 公司svn目前部署在内网服务器上,现在想在家中也可以使用,因此需要外网访问内网的工具 经过 使用过几个产品: utools,一个小巧的windows下的工具,内网映射只是它的一个小功能,支持tc ...
- [转]UDP穿透NAT的原理与实现(UDP“打洞”原理)
NAT(The IP Network Address Translator) 的概念和意义是什么? NAT, 中文翻译为网络地址转换.具体的详细信息可以访问RFC 1631 - http://www. ...
- [p2p]UDP用打洞技术穿透NAT的原理与实现
首先先介绍一些基本概念: NAT(Network Address Translators),网络地址转换:网络地址转换是在IP地址日益缺乏的情况下产生的, ...
- 【转】P2P之UDP穿透NAT的原理与实现(附源代码)
作者:shootingstars (有容乃大,无欲则刚) 日期:2004-5-25 出处:P2P中国(PPcn.net) P2P 之 UDP穿透NAT的原理与实现(附源代码)原创:shootings ...
- P2P之UDP穿透NAT的原理与实现
首先先介绍一些基本概念: NAT(Network Address Translators),网络地址转换:网络地址转换是在IP地址日益缺乏的情况下产生的,它的主要目的就是为了能够地址重用.NAT分为两 ...
- 利用iptables的NAT代理实现内网访问外网
利用NAT代理实现内网访问外网 背景及原理 若局域网中的两台计算机只能有一台能够访问外网,而这两台计算机之间能相互通信,那么可以配置能访问外网的那台服务器实现路由器的功能,即实现其他机器的NAT转换, ...
- UDP穿透NAT原理解析
转自:http://www.2cto.com/net/201201/116793.html NAT(Network Address Translators),网络地址转换:网络地址转换是在IP地址日益 ...
- float使内联支持宽高
float使内联元素支持了宽高,可以设置宽高属性:float消除内联元素的空格:
- P2P之UDP穿透NAT原理
首先先介绍一些基本概念: NAT(Network Address Translators),网络地址转换:网络地址转换是在IP地址日益缺乏的情况下产生的,它的主要目的就 ...
随机推荐
- linux环境变量 【转】
Linux 的变量可分为两类:环境变量和本地变量 环境变量,或者称为全局变量,存在与所有的shell 中,在你登陆系统的时候就已经有了相应的系统定义的环境变量了.Linux 的环境变量具有继承性,即子 ...
- Hibernate关联关系的CRUD
本文以Group和User(一对多.多对一)双向关联为例,介绍关联关系的CRUD 下面先介绍两个属性 cascade:只影响CRUD中的CUD,即存储(save).更新(update).删除(de ...
- Cuckoo Hash——Hash冲突的解决办法
参考文献: 1.Cuckoo Filter hash算法 2.cuckoo hash 用途: Cuckoo Hash(布谷鸟散列).问了解决哈希冲突的问题而提出,利用较少的计算换取较大的空间.占用空间 ...
- 基于 GitHub 搭建/创建自己博客 DIY
此博客主要实现通过github创建个人定制的博客的功能,主要参考如下两篇文章,再次感谢. 创建GitHub技术博客全攻略 “授人以渔”的教你搭建个人独立博客 [说明]:使用本文的正确方式是参考上述两篇 ...
- linux驱动的异步通知(kill_fasync,fasync)---- 驱动程序向应用程序发送信号
应用程序 #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include < ...
- 网络:W5500抓包TCP segment of a reassembled PDU
1.问题描述 W5500 http测试,用wireshark抓包,发现出现很多TCP segment of a reassembled PD. 2. 问题分析 TCP segment of a rea ...
- com.android.dex.DexIndexOverflowException: Cannot merge new index 66299 into a non-jumbo instruction
打包时控制台输出: Error:Execution failed for task ':app:transformClassesWithDexForAll32Release'. > com.an ...
- INSPIRED启示录 读书笔记 - 第12章 产品探索
软件项目可以划分为两个阶段 探索产品阶段:弄清楚要开发什么产品(定义正确的产品) 在探索产品的阶段,产品经理负责分析各种创意,广泛收集用户需求,了解如何运用新技术,拿出产品原型并加以测试 从全局视角思 ...
- RHCE学习笔记 管理1 (第一、二章)
第一章 命令行访问 1.Ctrl+alt+F2~F6 切到虚拟控制台,ctrl+alt+F1 回到图形界面 2.格式 : 命令 选项 参数 [] 为可选项目 ...表示该项目任意 ...
- 语义web相关概念
前言:最近做的项目是自然语言处理相关的,看了一本书<语义web技术基础>,总的来看,接触自然语言处理,语义理解也有差不多一年的时间了.这两天想了一想,自己究竟学到了什么,掌握了哪些新的知识 ...