NAT和NAT穿透

------------------本文分2部分 第1部分:概念(NAT与NAT穿透) 第2部分:源代码+效果图+专业资料讲解---------------------
---------------------------第1部分:概念(NAT与NAT穿透)---------------------------
NAT概念:NAT(Network Address Translation) , 是网络地址转换
功能:解决了lP地址不足的问题,而且还能够有效地避免来自网络外部的攻击,隐藏并保护网络内部的计算机。
--->实现:NAT将自动修改IP报文的源IP地址和目的IP地址,Ip地址校验则在NAT处理过程中自动完成。
------------------------------
NAT穿透概念:与NAT不同,它是专门针对NAT搞破坏的.NAT是防止网络外部访问,而NAT穿透是放任网络外部访问
------综合来说:NAT与NAT穿透是配合使用的.NAT太封闭,而NAT穿透则解决了外来网的访问,当然是针对具有NAT穿透的专业软件来说

---------------------------第2部分:源代码+效果图+专业资料讲解---------------------------
--------------分4步(注:实现NAT穿透有2个难点 第1个:套接字(在第3步讲) 第2个NAT穿透的不同方式(在第4步讲))------------------
截了一幅讲解NAT穿透的实现效果图


第1步:图中有个服务器X,怎样实现此服务器的功能
第2步:图中有A与B2个客户端,怎样实现此客户端的功能


图上显示的是A与B互相收到了X服务端发送过来的对方的IP与端口
------->
第3步:其实传个IP与端口很容易,关键是套接字:
困难1.有成千上万的套接字来访,所以要创建一个好的数据结构动态来保存这些数据
困难2.要让这成千上万的套接字存活,所以隔几秒发心跳包测试

第4步:根据NAT的原理制定出不同的穿透NAT的方案,此处不是讨论NAT怎样实现,而是讨论出几种穿透NAT的方法

附注1:当NAT穿透成功后,就实现了点对点通信,比如传输文件,网上称做P2P,当然2个是公网,则更好.直接点对点了,这个讨论就是第4步了,
----->勿弄混!NAT穿透,只是为P2P解决部分NAT后面主机通信的问题,但不是全部


3.4. UDP port number prediction
3.4.UPD端口号预言
A variant of the UDP hole punching technique discussed above exists that allows P2P UDP sessions to be created in the presence of some symmetric NATs.   This method is sometimes called the "N+1" technique [BIDIR] and is explored in detail by Takeda [SYM-STUN]. The method works by analyzing the behavior of the NAT and attempting to predict the public port numbers it will assign to future sessions.   
Consider again the situation in which two clients, A and B, each behind a separate NAT, have each established UDP connections with a permanently addressable server S:
让我们来考虑这样一种情况,有两个客户端 A 和 B,他们都藏在不同的NAT后面,他们都开放了一个UDP连接给具有固定IP的Server S,如下图:

                                            Server S
                                    18.181.0.31:1234
                                                   |
                                                   |
                   +----------------------+----------------------+
                    |                                                                |
        Symmetric NAT A                                     Symmetric NAT B
    A-S 155.99.25.11:62000                            B-S 138.76.29.7:31000
                    |                                                                |
                    |                                                                |
              Client A                                                     Client B
         10.0.0.1:1234                                            10.1.1.3:1234
NAT A has assigned its own UDP port 62000 to the communication session between A and S, and NAT B has assigned its port 31000 to the session between B and S. By communicating through server S, A and B learn each other's public IP addresses and port numbers as observed    by S. Client A now starts sending UDP messages to port 31001 at address 138.76.29.7 (note the port number increment), and client B simultaneously starts sending messages to port 62001 at address 155.99.25.11. If NATs A and B assign port numbers to new sessions sequentially, and if not much time has passed since the A-S and B-S sessions were initiated, then a working bi-directional communication channel between A and B should result.
A's messages to B cause NAT A to open up a new session, to which NAT A will (hopefully) assign public port number 62001, because 62001 is next in sequence after the port number 62000 it previously assigned to the session between A and S. Similarly, B's messages to A will cause NAT B to open a new    session, to which it will (hopefully) assign port number 31001. If both clients have correctly guessed the port numbers each NAT assigns to the new sessions, then a bi-directional UDP communication channel will have been established as shown below.
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 ANAT B继续分配端口给新的会话,并且从A-SB-S的会话时间消耗得并不多的话,那么一条处于客户端A客户端B之间的双向会话通道就建立了。
客户端A发出的消息送达B导致了NAT A打开了一个新的会话,并且我们希望 NAT A将会指派62001端口给这个新的会话,因为62001是继62000后,NAT会自动指派给 从服务器S客户端A之间的新会话的端口号;类似的,客户端B发出的消息送达A导致了 NAT B打开了一个新的会话,并且我们希望 NAT B 将会指派31001这个端口给新的会话;如果两个客户端都正确的猜测到了对方新会话被指派的端口号,那么这个 客户端A-客户端B的双向连接就被打通了。其结果如下图所示:

                                            Server S
                                    18.181.0.31:1234
                                                  |
                                                  |
                  +----------------------+----------------------+
                   |                                                                |
              NAT A                                                         NAT B
    A-S 155.99.25.11:62000                            B-S 138.76.29.7:31000
    A-B 155.99.25.11:62001                            B-A 138.76.29.7:31001
                   |                                                                |
                   |                                                                |
             Client A                                                      Client B
         10.0.0.1:1234                                            10.1.1.3:1234
Obviously there are many things that can cause this trick to fail. If the predicted port number at either NAT already happens to be in use by an unrelated session, then the NAT will skip over that port number and the connection attempt will fail.   If either NAT sometimes or always chooses port numbers non-sequentially, then the trick will fail.
If a different client behind NAT A (or B respectively) opens up a new outgoing UDP connection to any external destination after A (B) establishes its connection with S but before sending its first message to B (A), then the unrelated client will inadvertently "steal" the desired port number.   This trick is therefore much less likely to work when either NAT involved is under load.
明显的,有许多因素会导致这个方法失败:如果这个预言的新端口(62001和31001) 恰好已经被一个不相关的会话所使用,那么NAT就会跳过这个端口号,这个连接就会宣告失败;如果两个NAT有时或者总是不按照顺序来生成新的端口号,那么这个方法也是行不通的。
如果隐藏在NAT A后的一个不同的客户端X(或者在NAT B后)打开了一个新的“外出”UDP 连接,并且无论这个连接的目的如何;只要这个动作发生在 客户端A建立了与服务器S的连接之后,客户端A客户端B建立连接之前;那么这个无关的客户端X 就会趁人不备地“偷” 到这个我们渴望分配的端口。所以,这个方法变得如此脆弱而且不堪一击,只要任何一个NAT方包含以上碰到的问题,这个方法都不会奏效。
Since in practice a P2P application implementing this trick would still need to work if the NATs are cone NATs, or if one is a cone NAT and the other is a symmetric NAT, the application would need to detect beforehand what kind of NAT is involved on either end [STUN] and modify its behavior accordingly, increasing the complexity of the algorithm and the general brittleness of the network.  
Finally, port number prediction has no chance of working if either client is behind two or more levels of NAT and the NAT(s) closest to the client are symmetric.   For all of these reasons, it is NOT recommended that new applications implement this trick; it is mentioned here for historical and informational purposes.
自从使用这种方法来实践P2P的应用程序以来,在处于 cone NAT 系列的网络环境中这个方法还是实用的;如果有一方为 cone NAT 而另外一方为 symmetric NAT,那么应用程序就应该预先发现另外一方的 NAT 是什么类型,再做出正确的行为来处理通信,这样就增大了算法的复杂度,并且降低了在真实网络环境中的普适性。
    最后,如果P2P的一方处在两级或者两级以上的NAT下面,并且这些NATs 接近这个客户端是 symmetric的话,端口号预言 是无效的!
因此,并不推荐使用这个方法来写新的P2P应用程序,这也是历史的经验和教训!
3.5. Simultaneous TCP open
3.5. 同时开放TCP连接
There is a method that can be used in some cases to establish direct peer-to-peer TCP connections between a pair of nodes that are both behind existing middleboxes. Most TCP sessions start with one    endpoint sending a SYN packet, to which the other party responds with a SYN-ACK packet. It is possible and legal, however, for two endpoints to start a TCP session by simultaneously sending each other SYN packets, to which each party subsequently responds with a separate ACK. This procedure is known as a "simultaneous open."
这里有一种方法能够在某种情况下建立一个穿透NAT的端对端TCP直连。我们知道,绝大多数的TCP会话的建立,都是通过一端先发送一个SYN包开始,另一方则回发一个SYN-ACK包的过程。然而,这里确实存在另外一种情况,就是P2P的双方各自同时地发出一个SYN包到对方的公网地址上,然后各自都单独地返回一个ACK响应来建立一个TCP会话,这个过程被称之为:“Simultaneous open”同时开放连接)。
If a middlebox receives a TCP SYN packet from outside the private network attempting to initiate an incoming TCP connection, the middlebox will normally reject the connection attempt by either dropping the SYN packet or sending back a TCP RST (connection reset) packet. If, however, the SYN packet arrives with source and destination addresses and port numbers that correspond to a TCP session that the middlebox believes is already active, then the middlebox will allow the packet to pass through. In particular, if the middlebox has just recently seen and transmitted an outgoing SYN packet with the same addresses and port numbers, then it will consider the session active and allow the incoming SYN through.
     If clients A and B can each correctly predict the public port number that its respective middlebox will assign the next outgoing TCP connection, and if each client initiates an outgoing TCP connection with the other client timed so that each client's outgoing SYN passes through its local middlebox before either SYN reaches the opposite middlebox, then a working peer-to-peer TCP connection will result.
如果一个NAT接收到一个来自私有网络外面的 TCP SYN 包,这个包想发起一个引入 TCP 连接,一般来说,NAT会拒绝这个连接请求并扔掉这个SYN 包,或者回送一个TCP RSTconnection reset,重建连接)包给请求方。但是,有一种情况,当这个接收到的 SYN 中的源IP地址和端口、目标IP地址和端口都与NAT登记的一个已经激活的TCP会话中的地址信息相符时,NAT将会放行这个SYN 包,让它进入NAT内部。特别要指出,如果NAT恰好看到一个刚刚发送出去的一个SYN包也和上面接收到的SYN包中的地址信息相符合的话,那么NAT将会认为这个TCP连接已经被激活,并将允许这个方向的SYN包进入NAT内部。
如果 Client A Client B 能够彼此正确的预知对方的NAT将会给下一个TCP连接分配的公网TCP端口,并且两个客户端能够同时地发起一个外出TCP连接,并在对方的 SYN 包到达之前,自己刚发送出去的SYN包都能顺利的穿过自己的NAT的话,一条端对端的TCP连接就成功地建立了。
Unfortunately, this trick may be even more fragile and timing-sensitive than the UDP port number prediction trick described above. First, unless both middleboxes are simple firewalls or implement cone    NAT behavior on their TCP traffic, all the same things can go wrong with each side's attempt to predict the public port numbers that the respective NATs will assign to the new sessions. In addition, if either client's SYN arrives at the opposite middlebox too quickly, then the remote middlebox may reject the SYN with a RST packet, causing the local middlebox in turn to close the new session and make future SYN retransmission attempts using the same port numbers futile. Finally, even though support for simultaneous open is technically a mandatory part of the TCP specification [TCP], it is not implemented correctly in some common operating systems. For this reason, this trick is likewise mentioned here only for historical reasons; it is NOT recommended for use by applications. Applications that require efficient, direct peer-to-peer communication over existing NATs should use UDP.
不幸的是,这个诡计比3.4节所讲的UDP端口预言更容易被粉碎,并且对时间的敏感性的依赖更多!首先,除非双方的NAT都是Simple firewalls 或者都像cone NAT那样处理TCP通信,否则两个客户端还是无法建立这个TCP直连,因为它们无法预知对方的NAT会分配给新的会话的端口号是多少。另外,如果双方的 SYN 到达对方的NAT 速度太快(举例来说,就是SYN A的还未穿过NAT A时,SYN B已经提早到达了NAT A),对方的NAT会将这个SYN扔掉,并返回一个 RST 包,这样就使得这个发快了的一方NAT关闭原来的会话,又重新建立一个新的会话,再利用这个新的会话重发一个SYN,这时端口预言就失效了,因为再次分配到相同的端口地址的几率太小了。
原文地址:https://www.cnblogs.com/zhiweiyouzhishenghuo/p/5005541.html