如何让IdTCPServer&IdTCPClient的应用穿透NAT?

如何让IdTCPServer&IdTCPClient的应用穿透NAT? Delphi / Windows SDK/API
http://www.delphi2007.net/DelphiNetwork/html/delphi_20061217114600108.html
IdTCPServer拥有公网IP和开放的端口,  
  IdTCPClient位于NAT之后,没法端口映射,  
  但是由IdTCPClient主动发起连接,连接之后互相传递文件.  
  请问:这样的应用是否能够实现,并说明原理,谢谢!

强烈关注,貌似Tcp穿透Nat网上很难搜到实质性的东西

顶!我也想知道相关方法.

在IdTCPClient的属性里BoundIP和BoundPort不用填,使用如下代码就能穿透NAT连接IdTCPServer:  
   
      IdTCPClient.Host   :=   ‘0.0.0.0’;//IdTCPServer公网IP  
      IdTCPClient.Port   :=   0;                     //IdTCPServer的端口  
      try  
          IdTCPClient.Connect();  
      except  
          IdTCPClient.Disconnect;  
          exit;  
      end;  
   
  要注意的是,IdTCPServer一定要在拥有公网IP的机子上运行,这样可以使得世界任何地方的电脑用上述代码就能跟IdTCPServer相连接,连接上后,可以用IdTCPServer的OnConnect取得IdTCPClient经过NAT转换后的IP和Port,代码如下:  
   
  procedure   TForm1.IdTCPServer1Connect(AThread:   TIdPeerThread);  
  begin  
      IP       :=   AThread.Connection.Socket.Binding.PeerIP   ;  
      Port   :=   AThread.Connection.Socket.Binding.PeerPort;  
  end;  
   
  使用IdTCPServer的OnConnect获得的IP和Port才能与IdTCPClient通讯。  
  你会发现OnConnect里获得的IP和Port和未经过NAT转换后的IdTCPClient的机子的IP和Port大不一样。

典型的Client/Server结构,由Client向Server发出TCP/IP连接,此连接是可以双向传输数据。

chenzhechenge()   的方法能行????是不是有点搞笑了  
   
  用UDP吧

"IdTCPServer拥有公网IP和开放的端口"  
  既然服务器有固定IP,那Client直接连就可以了。  
   
 

楼主可能想问的是在两个不同的内网中,各自的客户端都可以访问公网服务器,现在想在这两个内网机之间穿透NAT建立连接。。。  
  关注,UDP的实现可以在晚上找到,TCP的还没有看到过。。。

看到没,楼上说了,udp的晚上可以找到,楼主晚上再来吧。  
  ^_^

TCP穿透需要RAW   Socket,XP以上系统不支持  
  需要进入驱动模式才OK  
   
  那个复杂~~

Server端在公网里,Client无论处在公网还是处在NAT之后,均能建立连接.连接一旦建好,可以相互通信.我用DELPHI6里的SOCKET控件(分别为服务端和客户端)实现过.SERVER端可以不必知道CLIENT端的地址和端口,只要CLIENT知道SERVER   的IP:PORT即可.SERVER端通过CONNECTION[n].SENDBUF向客户端通信.

两个都在NAT后面的客户端,如果使用UDP可以借助具有公网的服务器,双向打洞就可以穿透NAT了。

我也在找相关的代码,但好像楼上说的都是理论?  
  谁有相关的源码发一下?

楼主的情况根本不需要考虑NAT什么的麻烦事.  
   
  服务器既然有公网IP,客户端只要主动连接得上,这时候建立的TCP连接就是一条双向链路.  
  通常很多应用都是单向发起的问答式应用协议,  
  但这条TCP连接始终是可以双向的!  
   
  INDY控件组大多使用阻塞方式SOCKET,在阻塞方式,多半是另创建工作线程专用于接收数据.  
  楼主设计通讯协议的时候,注意区分一下数据是对方主动数据还是回应数据.  
   
 

chenzhechenge()   的方法能行????是不是有点搞笑了  
   
   
  可以,我老早就已经实现过了,现在还在运行中~~~

IdTCPServer拥有公网IP和开放的端口,  
   
   
  ---------------  
  那就不存在穿透NAT   了。

[转帖]NAT的完全分析及其UDP穿透的完全解决方案作者:陈敏  
  一:基本术语  
  防火墙  
  防火墙限制了私网与公网的通信,它主要是将(防火墙)认为未经授权的的包丢弃,防火墙只是检验包的数据,并不修改数据包中的IP地址和TCP/UDP端口信息。  
  网络地址转换(NAT)  
  当有数据包通过时,网络地址转换器不仅检查包的信息,还要将包头中的IP地址和端口信息进行修改。以使得处于NAT之后的机器共享几个仅有的公网IP地址(通常是一个)。网络地址转换器主要有两种类型.  
  P2P应用程序  
  P2P应用程序是指,在已有的一个公共服务器的基础上,并分别利用自己的私有地址或者公有地址(或者两者兼备)来建立一个端到端的会话通信。  
  P2P防火墙  
  P2P防火墙是一个提供了防火墙的功能的P2P代理,但是不进行地址转换.  
  P2P-NAT  
  P2P-NAT   是一个   P2P代理,提供了NAT的功能,也提供了防火墙的功能,一个最简的P2P代理必须具有锥形NAT对Udp通信支持的功能,并允许应用程序利用Udp打洞技术建立强健的P2P连接。  
  回环转换  
  当NAT的私网内部机器想通过公共地址来访问同一台局域网内的机器的时,NAT设备等价于做了两次NAT的事情,在包到达目标机器之前,先将私有地址转换为公网地址,然后再将公网地址转换回私有地址。我们把具有上叙转换功能的NAT设备叫做“回环转换”设备。  
  二:NAT分类  
  可以分为基础NAT与网络地址和端口转换(NAPT)两大类  
  (一):基础NAT  
  基础NAT   将私网主机的私有IP地址转换成公网IP地址,但并不将TCP/UDP端口信息进行转换。基础NAT一般用在当NAT拥有很多公网IP地址的时候,它将公网IP地址与内部主机进行绑定,使得外部可以用公网IP地址访问内部主机。(实际上是只将IP转换,192.168.0.23   <->   210.42.106.35,这与直接设置IP地址为公网IP还是有一定区别的,特别是对于企业来说,外部的信息都要经过统一防火墙才能到达内部,但是内部主机又可以使用公网IP)  
  (二):网络地址和端口转换   (NAPT)  
  这是最普遍的情况,网络地址/端口转换器检查、修改包的IP地址和TCP/UDP端口信息,这样,更多的内部主机就可以同时使用一个公网IP地址。  
  请参考[RFC1631]和[RFC2993]及[RFC2663]这三个文档了解更多的NAT分类和术语信息。另外,关于NAPT的分类和术语,[RFC2663]做了更多的定义。当一个内部网主机通过NAT打开一个“外出”的TCP或UDP会话时,NAPT分配给这个会话一个公网IP和端口,用来接收外网的响应的数据包,并经过转换通知内部网的主机。这样做的效果是,NAPT在   [私有IP:私有端口]   和[公网IP:公网端口]之间建立了一个端口绑定。  
  端口绑定指定了NAPT将在这个会话的生存期内进行地址转换任务。这中间存在一个这样的问题,如果P2P应用程序从内部网络的一个[私有IP地址:端口]对同时发出多条会话给不同的外网主机,那么NAT会怎样处理呢?这又可以分为锥形NAT   (CONE   NAT)与对称NAT   (SYMMTRIC   NAT)两大类来考虑:    
  A.锥形NAT  
  (为什么叫做锥形呢?请看以下图形,终端和外部服务器,都通过NAT分派的这个绑定地址对来传送信息,就象一个漏斗一样,筛选并传递信息)  
                                                                   
      当建立了一个   [私有IP:端口]-[公网IP:端口]   端口绑定之后,对于来自同一个[私有IP:端口]会话,锥形NAT服务器允许发起会话的应用程序   重复使用这个端口绑定,一直到这个会话结束才解除(端口绑定)。  
      例如,假设   Client   A(IP地址信息如上图所示)通过一个锥形NAT   同时发起两个外出的连接,它使用同一个内部端口(10.0.0.1:1234)给公网的两台不同的服务器,S1和S2。锥形NAT   只分配一个公网IP和端口(155.99.25.11:62000)给这个两个会话,通过地址转换可以确保   Client使用端口的“同一性”(即这个Client只使用这个端口)。而基础NATs和防火墙却不能修改经过的数据包端口号,它们可以看作是锥形NAT的精简版本。  
  进一步分析可以将CONE   NAT受限制锥形NAT   (RESTRICT   CONE)   与端口受限锥形NAT   (PORT   RESTRICT   CONE)   三大类,以下是详细论述:   分为全双工锥形NAT   (FULL   CONE)   ,  
  1.全双工锥形NAT  
  当内部主机发出一个“外出”的连接会话,就会创建了一个公网/私网   地址,一旦这个地址对被创建,全双工锥形NAT会接收随后任何外部端口传入这个公共端口地址的通信。因此,全双工锥形NAT有时候又被称为"混杂"NAT。  
  2.受限制锥形NAT  
  受限制的锥形NAT会对传入的数据包进行筛选,当内部主机发出“外出”的会话时,NAT会记录这个外部主机的IP地址信息,所以,也只有这些有记录的外部IP地址,能够将信息传入到NAT内部,受限制的锥形NAT   有效的给防火墙提炼了筛选包的原则——即限定只给那些已知的外部地址“传入”信息到NAT内部。  
  3.端口受限锥形NAT  
  端口受限制的锥形NAT,与受限制的锥形NAT不同的是:它同时记录了外部主机的IP地址和端口信息,端口受限制的锥形NAT给内部节点提供了同一级别的保护,在维持端口“同一性”过程中,将会丢弃对称NAT传回的信息。  
  B.对称NAT  
  对称NAT,与Cone   NAT是大不相同的,并不对会话进行端口绑定,而是分配一个全新的公网端口   给每一个新的会话。  
  还是上面那个例子:如果   Client   A   (10.0.0.1:1234)同时发起两个   "外出"   会话,分别发往S1和S2。对称Nat会分配公共地址155.99.25.11:62000给Session1,然后分配另一个不同的公共地址155.99.25.11:62001给Session2。对称Nat能够区别两个不同的会话并进行地址转换,因为在   Session1   和   Session2中的外部地址是不同的,正是因为这样,Client端的应用程序就迷失在这个地址转换边界线了,因为这个应用程序每发出一个会话都会使用一个新的端口,无法保障只使用同一个端口了。  
  在TCP和UDP通信中,(到底是使用同一个端口,还是分配不同的端口给同一个应用程序),锥形NAT和对称NAT各有各的理由。当然锥形NAT在根据如何公平地将NAT接受的连接直达一个已创建的地址对上有更多的分类。这个分类一般应用在Udp通信(而不是Tcp通信上),因为NATs和防火墙阻止了试图无条件传入的TCP连接,除非明确设置NAT不这样做。

三:NAT对session的处理  
  以下分析NAPT是依据什么策略来判断是否要为一个请求发出的UDP数据包建立Session的.主要有一下几个策略:    
  A.   源地址(内网IP地址)不同,忽略其它因素,   在NAPT上肯定对应不同的Session  
  B.   源地址(内网IP地址)相同,源端口不同,忽略其它的因素,则在NAPT上也肯定对应不同的Session  
  C.   源地址(内网IP地址)相同,源端口相同,目的地址(公网IP地址)相同,目的端口不同,则在NAPT上肯定对应同一个Session  
  D.   源地址(内网IP地址)相同,源端口相同,目的地址(公网IP地址)不同,忽略目的端口,则在NAPT上是如何处理Session的呢?  
  A,B,C三种情况的都是比较简单的,可以很容易的实现.而D的情况就比较复杂了.所以D情况才是我们要重点关心和讨论的问题。  
  四:完全解决方案  
  以下针对四种SESSION与四种NAT的完全解决方案,为了方便将使用以下缩写形式:  
  C代表   CONE   NAT  
  S代表SYMMETRIC   NAT,  
  FC代表   FULL   CONE   NAT,  
  RC代表   RESTRICT   CONE   NAT,  
  PC   代表   PORT   RESTRICT   CONE   NAT.  
  首先依据CLIENT   (客户)端在NAT后   的个数不同可以分为两大类:  
  TYPE   ONE   :一个在NAT后   +   一个在公网中.  
  这种情况下可以分为两大类:  
  A.   S   VS   公网:此种情况下,由于公网的地址在一个SESSION内是不变的,所以可以打洞是可以成功的.  
  B.   C   VS   公网:   与上面类似,这种情口下打洞是可以成功的.  
  TYPE   TWO:两个客户都在NAT后面.  
  这种情况下也可以细分为两大类:  
  A.   其中一个NAT   是   S(SYMMETRIC   NAT)   型的,既:S   VS   C或者是S   VS   S   .  
  下面论证这种情口下按照常规打洞是行不通的,在常规打洞中,所有的客户首先登陆到一个服务器上去.服务器记录下每个客户的[公网IP:端口],然后在打洞过程中就使用这个记录的值,然而对于S型的NAT来说,它并不绑定[私网IP:端口]和[公网IP:端口]的映射.所以在不同的SESSION中,NAT将会重新分配一对[公网IP:端口].这样一来对于S型的NAT来说打洞的[公网IP:端口]与登记在服务器上的[公网IP:端口]是不同的.而且也没有办法将打洞的[公网IP:端口]通知到另一个位于NAT下的客户端,   所以打洞是不会成功的.然而如果另一个客户端是在公网时,打洞是可以的.前面已经论证了这种情况.  
  这种情况下的解决方案是只能通过端口预测来进行打洞,具体解决方法如下:例如(以两个都是S型的为例)   NAT   A   分配了它自己的UDP端口62000,用来保持   客户端A   与服务器S的通信会话,   NAT   B   也分配了31000端口,用来保持客户端B与服务器S   的通信会话。通过与   服务器S的对话,客户端A   和   客户端B都相互知道了对方所映射的真实IP和端口。  
      客户端A发送一条UDP消息到138.76.29.7:31001(请注意到端口号的增加),同时客户端B发送一条UDP消息到155.99.25.11:62001。如果NAT   A   和NAT   B继续分配端口给新的会话,并且从A-S和B-S的会话时间消耗得并不多的话,那么一条处于客户端A和客户端B之间的双向会话通道就建立了。  
      客户端A发出的消息送达B导致了NAT   A打开了一个新的会话,并且我们希望NAT   A将会指派62001端口给这个新的会话,因为62001是继62000后,NAT会自动指派给   从服务器S到客户端A之间的新会话的端口号;类似的,客户端B发出的消息送达A导致了   NAT   B打开了一个新的会话,并且我们希望   NAT   B将会指派31001这个端口给新的会话;如果两个客户端都正确的猜测到了对方新会话被指派的端口号,那么这个   客户端A-客户端B的双向连接就被打通了。其结果如下图所示:  
  明显的,有许多因素会导致这个方法失败:如果这个预言的新端口(62001和31001)   恰好已经被一个不相关的会话所使用,那么NAT就会跳过这个端口号,这个连接就会宣告失败;如果两个NAT有时或者总是不按照顺序来生成新的端口号,那么这个方法也是行不通的。  
  如果隐藏在NATA后的一个不同的客户端X(或者在NAT   B后)打开了一个新的“外出”UDP   连接,并且无论这个连接的目的如何;只要这个动作发生在客户端A   建立了与服务器S的连接之后,客户端A   与   客户端B   建立连接之前;那么这个无关的客户端X   就会趁人不备地“偷”   到这个我们渴望分配的端口。所以,这个方法变得如此脆弱而且不堪一击,只要任何一个NAT方包含以上碰到的问题,这个方法都不会奏效。  
  在处于   cone   NAT   系列的网络环境中这个方法还是实用的;如果有一方为   cone   NAT   而另外一方为   symmetric   NAT,那么应用程序就应该预先发现另外一方的   NAT   是什么类型,再做出正确的行为来处理通信,这样就增大了算法的复杂度,并且降低了在真实网络环境中的普适性。  
          最后,如果P2P的一方处在两级或者两级以上的NAT下面,并且这些NATS   接近这个客户端是SYMMETRIC   NAT的话,端口号预言是无效的!  
  因此,并不推荐使用这个方法来写新的P2P应用程序,这也是历史的经验和教训!  
  B.   两个都是CONE   NAT型的.  
  这种情况下可以分为六大类型:  
  A:   FC   +   FC  
  B:   FC   +   RC  
  C:   FC   +   PC    
  D:   PC   +   RC    
  E:   PC   +   PC    
  F:   RC   +   RC    
  虽然有这么多种情况,但是由于CONE   NAT   的特性,所以还是很好办的,因为对于CONE   NAT   来说,在同一个SESSION中它会绑定一对[私网IP:端口]和[公网IP:端口]的映射,所以它们打洞用的[公网IP:端口]与登记在服务器上的[公网IP:端口]是一致的,所以打洞是可以行的通的.  
  综上所述,就已经完全的概括了所有类型的NAT之间的可能的通信情况了.并且都提供了可行的解决方案.  
  五:对前一阶段的总结  
  1.前一阶段使用的打洞方法是有缺陷的,它只适应于两个都是FULL   CONE   NAT的类型的CLIENT(客户端).以下论证它不适应于两个都是CONE   NAT的类型中的  
  B:   FC   +   RC  
  C:   FC   +   PC    
  D:   PC   +   RC    
  E:   PC   +   PC    
  F:   RC   +   RC    
  这五种情况.  
  因为对于受限的NAT它登记了外出包的[IP地址&端口],它仅仅接受这些已登记地址发过来的包,所以它们报告服务器的端口只能接受来自服务器的包.不能接受来自另一客户端的包.所以前一阶段的打洞方法是不可行的.  
  六:   存在的问题  
  按照理论.NAT将在一定时间后关闭UDP的一个映射,所以为了保持与服务器能够一直通信,服务器必须要发送UDP心跳包,来保持映射不被关闭.这就需要一个合适的时间值.

代码到处都是!

要注意的是,IdTCPServer一定要在拥有公网IP的机子上运行,这样可以使得世界任何地方的电脑用上述代码就能跟IdTCPServer相连接,连接上后,可以用IdTCPServer的OnConnect取得IdTCPClient经过NAT转换后的IP和Port,代码如下:  
   
  procedure   TForm1.IdTCPServer1Connect(AThread:   TIdPeerThread);  
  begin  
      IP       :=   AThread.Connection.Socket.Binding.PeerIP   ;  
      Port   :=   AThread.Connection.Socket.Binding.PeerPort;   //!!!!!根本得不到   peerport  
  end;                                                                                                     //   不信大家试试,,,编译出错!  
   
  使用IdTCPServer的OnConnect获得的IP和Port才能与IdTCPClient通讯。  
  你会发现OnConnect里获得的IP和Port和未经过NAT转换后的IdTCPClient的机子的IP和Port大不一样。  
 

算了,,,,大家不要试了,,,,,  
   
  上面我说明一下,可以编译成功,,,有AThread.Connection.Socket.Binding.PeerPort   这个属性  
   
   
  listbox1.Items.Add(athread.Connection.Socket.Binding.PeerIP+':'+inttostr(athread.Connection.Socket.Binding.peerPort));  
  但是,,,加了这一句后,,,,,程序一运行就"程序无法响应",,,,直接死机了!

athread.Connection.Socket.Binding.PeerIP    
  athread.Connection.Socket.Binding.IP  
   
  athread.Connection.Socket.Binding.PeerPort  
  athread.Connection.Socket.Binding.Port  
   
  加个peer在前面是什么意思啊,,     两者有什么区别哦???

athread.Connection.Socket.Binding.PeerIP    
  athread.Connection.Socket.Binding.IP  
   
  也想知道有什么区别?

posted on 2008-10-28 09:53  delphi2007  阅读(590)  评论(0编辑  收藏  举报