首页 > 代码库 > UDP穿洞技术,P2P一些原理

UDP穿洞技术,P2P一些原理

因为当前 IPV4地址的缺乏 ,nat、防火墙的中介设备和不对称寻址建立起来的 p2p通信机制造成了地址访问的问题。 

在 internet最初体系结构中,每个节点都有全球唯一的 ip地址,能够直接通信。可是随着节点的增多, ip地址使用紧张,他们需要中介设备如 nat连在一起。 
私有网络中的节点可以直接连接到相同私有网络中的其他节点,也可以连接到全局地址空间中拥有全球唯一 ip地址的节点。。然而 nat通常只允许临时的向外连接申请,对于向内的申请会拒绝。这就造成了在 natA内网中的节点 A连接 natB内网中的节点 B时连接申请报到 natB时就被阻止了。此时我们需要的就是穿越技术。。。 
  
总体来说穿越技术是利用一个公共服务器中转,使节点 A、 B都连接到中转服务器 S之后,通过 S中转 A发送到 B的数据报或者是中转连接申请,,使 A、 B对于 natA和 natB来说都是向外申请。。。 
  
1、         中转数据报: A、 B都先向外与服务器 S建立接连,然后通过 S中转 A、 B之间的数据报。。 
2、         反向连接:当 A、 B都与 S建立了连接,并且只有一个节点在 nat之后(假设 A在 natA之后)。。当 B向 A申请连接时,申请背 natA拒绝。 B可以向 S提出申请要与 A建立连接,然后 S向 A发出指令,通知 A主动向 B申请建立连接。。 
【 UDP打洞】 
1、 A、 B在同一个 nat之后: 
 
用户 A让 S做介绍人来与 B建立对话 
(1) A向 S发送一个消息请求与 B建立连接 
(2) S使用 B的公共终端( 155.99.25.11: 62005)和私有终端( 10.1.1.3)响应 A 
(3) 同时 S也想 B发送 A的公共终端( 155.99.25.11: 62000)和私有终端( 10.0.0.1),但是发送到公共终端的消息不一定能达到 B取决于 NAT是否支持“发夹”转化(回环转化) 
(4) 如果 nat支持发夹转化的话,应用程序就可以免除私有和共有终端都要试图连接的复杂性。。 

  
2、不同 NAT后面的节点 
 
(1) 注册, A、 B都想服务器 S注册 natA安排了 62000端口用作 A和 S对话使用, natB安排了 31000端口用作 B和 S对话使用, A向 S的注册消息中报告了自己的私有终端 10.0.0.1: 4321这种情况下 A的公共终端是 155.99.25.11: 62000,同理 B的私有终端 10.1.1.3: 4321和公共终端 136.76.29.7: 31000 
(2) A发送请求消息到 S,请求与 B建立连接,作为响应 S向 A发送了 B的私有终端和公共终端也向 B发送了 A的私有和公共终端。 
(3) 既然 A、 B处在不同的子网中,那么 A、 B的私有终端是不能公共路由的,发送的消息肯能会发到自己子网中的 ip中(应为不同子网中的私有 ip可以相同) 
(4) 当从 A发向 B的第一个消息到达 natA时, natA注意到这是一个新的外出会话, natA看到源地址是子网中地址,而目的地址是外网地址,所以 natA将从私有终端 10.0.0.1: 4321的外出会话转化到对应公共终端 155.99.25.11: 62000,这样 A的第一个到 B的公共终端的外出会话消息就在 natA上“打了一个洞”。新的 UDP会话由 A的私有网络上的终端 10.0.0.1: 4321/138.76.29.7: 31000和 internet上的公共终端 155.99.25.11: 62000/138.76.29.7:31000标识,同理 B也建立了对 A的私有、公共连接标识。 
(5) 如果 A发向 B的公共终端的消息在 B发向 A的第一个消息穿过 B自己的 natB之前到达了 natB的话, natB会认为 A的内入消息是禁止的,丢弃 B的请求消息,但是 B的请求消息在 natB上为 A打了一洞,此时洞双向打开,通信可以进行下去了。。。 

3、多级 NAT后面的节点: 
 
( 1) A、 B都建立与 S的向外连接 
( 2)最终连接目的: 
   Aà B    10.0.0.1à 10.0.1.2:55000 
   Bà A    10.0.0.3à 10.0.1.1:45000 
( 3)但是在此时 A、 B无法知道伪公众终端 10.0.1.2:55000和 10.0.1.1:45000。 S只看到了 155.99.25.11: 52000和 155.99.25.11: 62005。 
( 4)此时相应的 A、 B也只知道 155.99.25.11: 52000和 155.99.25.11: 62005 
( 5)只能依赖 natC的发夹转化。 
     当 A-à B,即 10.0.0.1—>155.99.25.11: 62005时 natA将数据报中源地址 10.0.0.1转化为 10.0.1.1然后发送到 natC,当 natC发现目的地址 ip是 155.99.25.11是自己转化过的 ip后, natC就会转化数据报中的源地址和目的地址,再发送到私有网络中。 155.99.25.11: 62000--à 10.0.1.2: 55000 
( 6)当数据报到 B私有网络时,同样方法进行转化。。 

另外,局域网内数据共享,可以采用组播方式。


UDPServer.java: 

Java代码  收藏代码
  1. package org.iaiai.test;  
  2.   
  3. import java.net.DatagramPacket;  
  4. import java.net.DatagramSocket;  
  5. import java.net.InetAddress;  
  6.   
  7. /** 
  8.  *  
  9.  * <br/> 
  10.  * Title: UDPServer.java<br/> 
  11.  * E-Mail: 176291935@qq.com<br/> 
  12.  * QQ: 176291935<br/> 
  13.  * Http: iaiai.iteye.com<br/> 
  14.  * Create time: 2013-1-29 上午11:11:56<br/> 
  15.  * <br/> 
  16.  * @author 丸子 
  17.  * @version 0.0.1 
  18.  */  
  19. public class UDPServer {  
  20.   
  21.     public static void main(String[] args) {  
  22.         try {  
  23.             DatagramSocket server = new DatagramSocket(2008);  
  24.             byte[] buf = new byte[1024];  
  25.             DatagramPacket packet = new DatagramPacket(buf, buf.length);  
  26.   
  27.             String sendMessage132 = "";  
  28.             String sendMessage129 = "";  
  29.             int port132 = 0;  
  30.             int port129 = 0;  
  31.             InetAddress address132 = null;  
  32.             InetAddress address129 = null;  
  33.             for (;;) {  
  34.                 server.receive(packet);  
  35.   
  36.                 String receiveMessage = new String(packet.getData(), 0, packet.getLength());  
  37.                 System.out.println(receiveMessage);  
  38.                 //接收到clientA  
  39.                 if (receiveMessage.contains("132")) {  
  40.                     port132 = packet.getPort();  
  41.                     address132 = packet.getAddress();  
  42.                     sendMessage132 = "host:" + address132.getHostAddress() + ",port:" + port132;  
  43.                 }  
  44.                 //接收到clientB  
  45.                 if (receiveMessage.contains("129")) {  
  46.                     port129 = packet.getPort();  
  47.                     address129 = packet.getAddress();  
  48.                     sendMessage129 = "host:" + address129.getHostAddress() + ",port:" + port129;  
  49.                 }  
  50.                 //两个都接收到后分别A、B址地交换互发  
  51.                 if (!sendMessage132.equals("") && !sendMessage129.equals("")) {  
  52.                     send132(sendMessage129, port132, address132, server);  
  53.                     send129(sendMessage132, port129, address129, server);  
  54.                     sendMessage132 = "";  
  55.                     sendMessage129 = "";  
  56.                 }  
  57.             }  
  58.         } catch (Exception e) {  
  59.             e.printStackTrace();  
  60.         }  
  61.     }  
  62.   
  63.     private static void send129(String sendMessage132, int port132, InetAddress address132, DatagramSocket server) {  
  64.         try {  
  65.             byte[] sendBuf = sendMessage132.getBytes();  
  66.             DatagramPacket sendPacket = new DatagramPacket(sendBuf, sendBuf.length, address132, port132);  
  67.             server.send(sendPacket);  
  68.             System.out.println("消息发送成功!");  
  69.         } catch (Exception e) {  
  70.             e.printStackTrace();  
  71.         }  
  72.     }  
  73.   
  74.     private static void send132(String sendMessage129, int port129, InetAddress address129, DatagramSocket server) {  
  75.         try {  
  76.             byte[] sendBuf = sendMessage129.getBytes();  
  77.             DatagramPacket sendPacket = new DatagramPacket(sendBuf, sendBuf.length, address129, port129);  
  78.             server.send(sendPacket);  
  79.             System.out.println("消息发送成功!");  
  80.         } catch (Exception e) {  
  81.             e.printStackTrace();  
  82.         }  
  83.     }  
  84. }  



UDPClientA.java: 

Java代码  收藏代码
  1. package org.iaiai.test;  
  2.   
  3. import java.net.DatagramPacket;  
  4. import java.net.DatagramSocket;  
  5. import java.net.InetAddress;  
  6. import java.net.InetSocketAddress;  
  7. import java.net.SocketAddress;  
  8.   
  9. /** 
  10.  *  
  11.  * <br/> 
  12.  * Title: UDPClientA.java<br/> 
  13.  * E-Mail: 176291935@qq.com<br/> 
  14.  * QQ: 176291935<br/> 
  15.  * Http: iaiai.iteye.com<br/> 
  16.  * Create time: 2013-1-29 上午11:11:56<br/> 
  17.  * <br/> 
  18.  * @author 丸子 
  19.  * @version 0.0.1 
  20.  */  
  21. public class UDPClientA {  
  22.   
  23.     public static void main(String[] args) {  
  24.         try {  
  25.             // 向server发起请求  
  26.             SocketAddress target = new InetSocketAddress("10.1.11.137", 2008);  
  27.             DatagramSocket client = new DatagramSocket();  
  28.             String message = "I am UPDClinetA 192.168.85.132";  
  29.             byte[] sendbuf = message.getBytes();  
  30.             DatagramPacket pack = new DatagramPacket(sendbuf, sendbuf.length, target);  
  31.             client.send(pack);  
  32.             // 接收请求的回复,可能不是server回复的,有可能来自UPDClientB的请求内  
  33.             receive(client);  
  34.         } catch (Exception e) {  
  35.             e.printStackTrace();  
  36.         }  
  37.     }  
  38.   
  39.     //接收请求内容  
  40.     private static void receive(DatagramSocket client) {  
  41.         try {  
  42.             for (;;) {  
  43.                 byte[] buf = new byte[1024];  
  44.                 DatagramPacket packet = new DatagramPacket(buf, buf.length);  
  45.                 client.receive(packet);  
  46.                 String receiveMessage = new String(packet.getData(), 0, packet.getLength());  
  47.                 System.out.println(receiveMessage);  
  48.                 int port = packet.getPort();  
  49.                 InetAddress address = packet.getAddress();  
  50.                 String reportMessage = "tks";  
  51.                 //获取接收到请问内容后并取到地址与端口,然后用获取到地址与端口回复内容  
  52.                 sendMessaage(reportMessage, port, address, client);  
  53.             }  
  54.         } catch (Exception e) {  
  55.             e.printStackTrace();  
  56.         }  
  57.     }  
  58.   
  59.     //回复内容  
  60.     private static void sendMessaage(String reportMessage, int port, InetAddress address, DatagramSocket client) {  
  61.         try {  
  62.             byte[] sendBuf = reportMessage.getBytes();  
  63.             DatagramPacket sendPacket = new DatagramPacket(sendBuf, sendBuf.length, address, port);  
  64.             client.send(sendPacket);  
  65.             System.out.println("消息发送成功!");  
  66.         } catch (Exception e) {  
  67.             e.printStackTrace();  
  68.         }  
  69.     }  
  70. }  



UDPClientB.java 

Java代码  收藏代码
    1. package org.iaiai.test;  
    2.   
    3. import java.net.DatagramPacket;  
    4. import java.net.DatagramSocket;  
    5. import java.net.InetAddress;  
    6. import java.net.InetSocketAddress;  
    7. import java.net.SocketAddress;  
    8.   
    9. /** 
    10.  *  
    11.  * <br/> 
    12.  * Title: UDPClientB.java<br/> 
    13.  * E-Mail: 176291935@qq.com<br/> 
    14.  * QQ: 176291935<br/> 
    15.  * Http: iaiai.iteye.com<br/> 
    16.  * Create time: 2013-1-29 上午11:11:56<br/> 
    17.  * <br/> 
    18.  * @author 丸子 
    19.  * @version 0.0.1 
    20.  */  
    21. public class UDPClientB {  
    22.   
    23.     public static void main(String[] args) {  
    24.         try {  
    25.             //向server发起请求  
    26.             SocketAddress target = new InetSocketAddress("10.1.11.137", 2008);  
    27.             DatagramSocket client = new DatagramSocket();  
    28.             String message = "I am UDPClientB 192.168.85.129";  
    29.             byte[] sendbuf = message.getBytes();  
    30.             DatagramPacket pack = new DatagramPacket(sendbuf, sendbuf.length, target);  
    31.             client.send(pack);  
    32.             //接收server的回复内容  
    33.             byte[] buf = new byte[1024];  
    34.             DatagramPacket recpack = new DatagramPacket(buf, buf.length);  
    35.             client.receive(recpack);  
    36.             //处理server回复的内容,然后向内容中的地址与端口发起请求(打洞)  
    37.             String receiveMessage = new String(recpack.getData(), 0, recpack.getLength());  
    38.             String[] params = receiveMessage.split(",");  
    39.             String host = params[0].substring(5);  
    40.             String port = params[1].substring(5);  
    41.             System.out.println(host + ":" + port);  
    42.             sendMessage(host, port, client);  
    43.         } catch (Exception e) {  
    44.             e.printStackTrace();  
    45.         }  
    46.     }  
    47.   
    48.     //向UPDClientA发起请求(在NAT上打孔)  
    49.     private static void sendMessage(String host, String port, DatagramSocket client) {  
    50.         try {  
    51.             SocketAddress target = new InetSocketAddress(host, Integer.parseInt(port));  
    52.             for (;;) {  
    53.                 String message = "I am master 192.168.85.129 count test";  
    54.                 byte[] sendbuf = message.getBytes();  
    55.                 DatagramPacket pack = new DatagramPacket(sendbuf, sendbuf.length, target);  
    56.                 client.send(pack);  
    57.                 //接收UDPClientA回复的内容  
    58.                 receive(client);  
    59.             }  
    60.         } catch (Exception e) {  
    61.             e.printStackTrace();  
    62.         }  
    63.     }  
    64.   
    65.     //收到UDPClientA的回复内容,穿透已完成  
    66.     private static void receive(DatagramSocket client) {  
    67.         try {  
    68.             for (;;) {  
    69.                 //将接收到的内容打印  
    70.                 byte[] buf = new byte[1024];  
    71.                 DatagramPacket recpack = new DatagramPacket(buf, buf.length);  
    72.                 client.receive(recpack);  
    73.                 String receiveMessage = new String(recpack.getData(), 0, recpack.getLength());  
    74.                 System.out.println(receiveMessage);  
    75.   
    76.                 //记得重新收地址与端口,然后在以新地址发送内容到UPDClientA,就这样互发就可以了。  
    77.                 int port = recpack.getPort();  
    78.                 InetAddress address = recpack.getAddress();  
    79.                 String reportMessage = "I am master 192.168.85.129 count test";  
    80.   
    81.                 //发送消息  
    82.                 sendMessage(reportMessage, port, address, client);  
    83.             }  
    84.         } catch (Exception e) {  
    85.             e.printStackTrace();  
    86.         }  
    87.     }  
    88.   
    89.     private static void sendMessage(String reportMessage, int port, InetAddress address, DatagramSocket client) {  
    90.         try {  
    91.             byte[] sendBuf = reportMessage.getBytes();  
    92.             DatagramPacket sendPacket = new DatagramPacket(sendBuf, sendBuf.length, address, port);  
    93.             client.send(sendPacket);  
    94.             System.out.println("send success");  
    95.         } catch (Exception e) {  
    96.             e.printStackTrace();  
    97.         }  
    98.     }  
    99. }  

UDP穿洞技术,P2P一些原理