TCP协议的NAT穿透技术【视频监控可用此技术】

NAT的全称是Network Address Translator,网络地址转换。

转自:http://blog.csdn.net/q376420785/article/details/8286292

前言:
       最近我用java做了一个C/S的类似QQ之类的IM系统(即时通讯系统),遇到了不能跨局域网通讯的问题,经过在网上,和书上查阅了一些资料,了解了一些情况,现在就总结一下我的解决方案吧(本人也是在不断错误中学习,如果有什么不对的地方,请大家多多指教)。


问题描述:
         一般情况下,只能在同一个子网即同一个虚拟局网里通讯,或者子网访问外网,外网访问不了内网。因为虚拟局域网相对于外网都是透明的,所以外网是不能直接访问子网,举个例子:校园网里的学生信息管理系统(MIS),需要是校园网里面的IP地址才能访问,校园网以外的IP是不能访问,这样的做法的好处不用我多说吧。。
而即时通讯需要相互交流信息,所以需要相互都能访问到,问题是:如何跨子网通讯?

问题分析:

首先,在不同网络点之间(即peer-to-peer对等网络),可以分为以下几种情况:

1.ClientA在子网,ClientB也在同一个子网
2.ClientA在子网,ClientB在外网
3.ClientA在一个子网内,ClientB在另一个子网内

对于第一种情况,不需要经过路由器的转发,可以直接相互通讯。

对于第二种情况,ClientB对于ClientA是公开的,ClientA对于ClientB是透明的。
ClientA-->直接相通-->ClientB,ClientB-->不能直接想通-->ClientA。换句话说,ClientA都能接受到包涵真正发送者IP的数据包;如果ClientA发送数据包给ClientB,ClientB接受数据包的IP来源并不是ClientA,而已中转站(路由器)的IP。

对于第三种情况,两者之间都是相互透明的。


解决方案:

要解决P2P跨局域网,就要用到NAT穿透技术,我在这里先讲UDP穿透,TCP穿透原理一样,实现更简单,这里就不多说了。

NAT的全称是Network Address Translator,网络地址转换。一般是应用于路由器,更详细资料请自己查阅资料吧。
NAT分两大类:
1)NAT
2)NAPT ,Network Address Port Translator,网络地址端口转换
这里默认是用NAT,端口号没有转换,如果是NAPT,实现的原理也是差不多的,只是细节不同。

关于第二种情况,原理如下
A:192.168.1.10,处于内网
NATA:路由器对外的wlanIP:168.38.2.2;路由器对内的lanIP:192.168.1.10
B:168.38.2.225,处于外网

1.首先,A(内网IP)先向B(外网IP)发送UDP包,NAT会产生session,存放192.168.1.10-- >168.38.2.225的映射关系



此时,A为了B在NAT上打了一个洞



2.接下来,只要B把信息发给NATA,NATA会自动把信息转发给A


这样A,B之间的NATA会有个洞来让两者通讯,但是此方案不适用于第三种情况。


关于第三种情况,原理如下
需要用到第三方中转站,而且这中转站必须在外网,即全球唯一IP。我是直接一台连接所有客户端的服器,记录了所有客户端的真实IP(如果是内网IP就记录内网IP),我是用TCP来实现客户端和服务器的通讯,里要用到了TCP穿透,这里先不讲,最后总结说一下。


或者如图:(ps:图片是网上下载的)


首先,我先介绍下这个服务器的作用。所有的客户端都和服务器建立了面向连接可靠的TCP连接,即服器--客户端之间可以相互通讯。客户端会向客户端发送自身的真实IP(如A:192.168.1.110),服务器接受到信息之后,同时也会知道信息源的IP(如:NATA的外网IP:210.38.196.110),因为接受方总能知道信息包的直接来源。此时,服务器就记录了ClientA的IP、NATA外网IP和ClientB的IP、NATB外网IP。


要想A<------>B之间无需经过第三方的通讯,必须在NATA中为B打个洞,在NATB中为A打个洞。
实际上就是在NATA中存放A(192.168.1.110)--->NATB(外网IP:210.38.196.30)的映射,这样只要是从NATB发送到NATA的,都会转发到A子网中,此过程叫NATA为B打洞。同理NATB也可以为A打洞。

具体过程:server会发送信息告知A,B的NATB的外网IP;告知B,A的NATA的外网IP。这样子,A可以先发送一个数据包到NATB(外网IP:210.38.196.30),NATA会生成一个session记录A————>NATB的映射关系。数据包到达NATB时,NATB因为还未为A打洞,会丢包。然后B向NATA(外网IP:210.38.196.110)发送数据包,NATB会生成一个session记录B————>NATA的映射关系。此时,NATA接受到来自NATB的数据包,会查找下session映射,发现有A————>NATB的映射关系,会把包转发给A。只要A再发信息给NATB时,NATB也会根据session来转发给B。双方打洞完成。

所以对于第二种情况,也可以用此方案,如图:

利用第三方服务器来帮P2P对等点之间的UDP打洞,是比较全面的方案,此方案适用于P2P对等点在任何网络点(说得可能比较绝对吧,大家有什么不同意见记得提出来哦,纯属个人的偏见)


总结:
简单说下TCP穿透原理吧,分四种情况:
1.建立TCP等待连接方和请求TCP连接方在同一个子网,或者是外网(不用穿透)
2.建立TCP等待连接方在外网,请求TCP连接方在内网(请求方会先发请求信息,同时为建立方打洞)
3.建立TCP等待连接方在内网,请求TCP连接方在外网(需要第三方服务器,原理同上面的UDP穿透一样)
4.建立TCP等待连接方,请求TCP连接方分别在不同的子网(需要第三方服务器,原理同上面的UDP穿透一样)

不足:由于我开发的只是简单的即时通讯系统,可能很多复杂的情况我没有考虑进去,例如存在多个NAT的嵌套,NAPTIP地址端口号转换没有考虑进去,路由器中的session的生命周期的不确定(有时候是几分钟,有时候几个小时不定),所以需要定期地进行穿透等等等等。

最后:

这是我第一次在CSND发的博客,有什么不足或者错误希望大家多多指教,也希望大家能和我多多分享更资源和知识~~!




转自:http://blog.csdn.net/u013920085/article/details/24400979

其实很早我就已经实现了使用TCP协议穿透NAT了,但是苦于一直没有时间,所以没有写出来,现在终于放假有一点空闲,于是写出来共享之。


    一直以来,说起NAT穿透,很多人都会被告知使用UDP打孔这个技术,基本上没有人会告诉你如何使用TCP协议去穿透(甚至有的人会直接告诉你TCP协议是无法实现穿透的)。但是,众所周知的是,UDP是一个无连接的数据报协议,使用它就必须自己维护收发数据包的完整性,这常常会大大增加程序的复杂度,而且一些程序由于某些原因,必须使用TCP协议,这样就常常令一些开发TCP网络程序的人员“谈穿透色变”。那么,使用TCP协议是不是就不能实现穿透呢?答案当然是否定的:TCP协议不仅能实现NAT穿透,而且实现起来比UDP穿透甚至还简单一些。 


    要了解如何使用TCP穿透NAT,就要首先看看如何使用UDP穿透NAT。 
    我们假设在两个不同的局域网后面分别有2台客户机A和 B,AB所在的局域网都分别通过一个路由器接入互联网。互联网上有一台服务器S。 
    现在AB是无法直接和对方发送信息的,AB都不知道对方在互联网上真正的IP和端口, AB所在的局域网的路由器只允许内部向外主动发送的信息通过。对于B直接发送给A的路由器的消息,路由会认为其“不被信任”而直接丢弃。 
    要实现 AB直接的通讯,就必须进行以下3步:A首先连接互联网上的服务器S并发送一条消息(对于UDP这种无连接的协议其实直接初始会话发送消息即可),这样S就获取了A在互联网上的实际终端(发送消息的IP和端口号)。接着 B也进行同样的步骤,S就知道了AB在互联网上的终端(这就是“打洞”)。接着S分别告诉A和B对方客户端在互联网上的实际终端,也即S告诉A客户B的会话终端,S告诉B客户A的会话终端。这样,在AB都知道了对方的实际终端之后,就可以直接通过实际终端发送消息了(因为先前双方都向外发送过消息,路由上已经有允许数据进出的消息通道)。


    用UDP来实现以上3步不存在什么理论上的问题,因为UDP是无连接的协议,它允许socket进行“多对一”的通讯(即几个具有不同IP和端口号的socket向一个接收socket发送消息)。但是使用TCP就出现了问题:在一般情况下,TCP socket不允许在已经建立连接的端口上再进行监听和使用该本地端口。换句话说,当AB连接上服务器S后,S将AB的实际终端告诉对方,下一步本该是AB利用对方的实际终端进行直连,但这时你会发现对方的实际终端已经被占用了(就是各自连接到服务器S的会话占用了终端),无法同时listen和 connect。于是很多人得出结论:TCP无法实现NAT穿透。 
    于是问题的关键变成了如何复用一个TCP连接的本地终端,这其实不是协议的问题,而是一个API的问题。幸运的是,所有主流操作系统都支持一个特定的TCP套接字选项——SO_REUSEADDR。这个选项允许将多个socket绑定到同一个本地终端。我们建立socket的时候只要加上这么一行:

setsockopt(socket, SOL_SOCKET, SO_REUSEADDR,  & flag, len) ;    // C++就这么做

 

_Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress,  True )   ' 这是vb.net 更加简单


    知道上面的知识就很好办了,下面我来说说TCP协议的穿透流程: 
    机器布局还是和上面使用UDP的一样。现在假设客户A想和客户B建立TCP连接。 
首先还是 AB分别和服务器S分别建立连接,S记录AB的互联网实际终端。然后S分别向AB发送对方的实际终端。接着,从A和B向S连接时使用的端口,AB都异步调用connect函数连接对方的实际终端(就是S告诉的终端),同时,AB双方都在同一个本地端口监听到来的连接(也可以先监听,再connect更好)。由于双方都向对方发送了connect请求(假设各自的SYN封包已经穿过了自己的NAT),因此在对方connect请求到达本地的监听端口时,路由器会认为这个请求是刚刚那个connect会话的一部分,是已经被许可的,本地监听端口就会用SYN-ACK响应,同意连接。这样,TCP穿透NAT的点对点连接就成功了。

下面是示例代码下载,VB.NET代码,演示如何用TCP协议穿透NAT实现文件传送,请用vs2005打开解决方案

http://dl2.csdn.net/down4/20070724/24133943521.rar 

代码中有一个我自己封装的模仿vb6 winsock的控件ZXMSocket,这个socket可以让你设置是否使用SO_REUSEADDR参数,socket是事件驱动的。

如果你要测试代码,需要使用一个bat来启动发送和接收程序(文件格式请参照bin/Debug文件夹下的run.bat文件),这个bat的功能是以命令行的方式告诉程序登录服务器缩使用的用户名,对于服务器来说,这个用户名必须是唯一的,当然,这可能有点不科学,但是这毕竟只是一个demo。


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值