我试图在Java中实现UDP-Holepunching的简单草图以测试其概念,并稍后在我的C / C ++应用程序中使用它。
从Wikipedia来说,我的理解是:A和B是未定义网络结构背后的客户端,C是著名的公共可访问服务器。
这在localhost上都运行良好(这并不奇怪),但是在实际示例中,此操作失败。
A和B都可以连接到服务器C,服务器C获取其数据包,存储其地址和端口,并将其传输到另一个客户端。但是在这一点上它失败了。A和B无法互相通信。所以我问自己我做错了什么。我花了几天时间在google和stackoverflow中搜索工作示例,但我偶然发现的是使用STUN的建议,这不是我想要的。
下面,我将用Java发布我的草图,因为我不知道我的概念或实现是否有问题。
public class Server { public static void main(String[] args) { int port1 = 0, port2 = 0; String address1 = null, address2; byte[] bytes = new byte[1024]; try { System.out.println("Server waiting"); DatagramSocket ds = new DatagramSocket(789); while(!Thread.interrupted()) { DatagramPacket p = new DatagramPacket(bytes, bytes.length); ds.receive(p); if(port1 == 0) { port1 = p.getPort(); address1 = p.getAddress().getHostAddress(); System.out.println("(1st) Server received:" + new String(bytes) + " from " + address1 + " on port " + port1); } else { port2 = p.getPort(); address2 = p.getAddress().getHostAddress(); System.out.println("(2nd) Server received:" + new String(bytes) + " from " + address1 + " on port " + port1); sendConnDataTo(address1, port1, address2, port2, ds); sendConnDataTo(address2, port2, address1, port1, ds); } } ds.close(); } catch(Exception e) { e.printStackTrace(); } } public static void sendConnDataTo(String a1, int p1, String a2, int p2, DatagramSocket ds) { byte[] bA, bP; bA = a1.getBytes(); bP = Integer.toString(p1).getBytes(); DatagramPacket pck; try { pck = new DatagramPacket(bA, bA.length, InetAddress.getByName(a2), p2); ds.send(pck); pck = new DatagramPacket(bP, bP.length, InetAddress.getByName(a2), p2); ds.send(pck); } catch(Exception e) { e.printStackTrace(); } } }
请注意,这只是一些草图,没有实际应用。服务器应该只从两个客户端接收数据包,保存它们的地址和端口,然后将其传递给另一个客户端。
public class Client { private DatagramSocket socket; private int init = 0; private String target; private int port; public Client() { try { socket = new DatagramSocket(); } catch(SocketException e) { e.printStackTrace(); } Thread in = new Thread() { public void run() { while(true) { byte[] bytes = new byte[1024]; DatagramPacket packet = new DatagramPacket(bytes, bytes.length); try { socket.receive(packet); bytes = Arrays.copyOfRange(bytes, 0, packet.getLength()); String s = new String(bytes); System.out.println("Received: " + s); if(init == 0) { target = s; System.out.println("Target: " + target); init++; } else if(init == 1) { port = Integer.parseInt(s); System.out.println("Port: " + port); init++; } else System.out.println(new String(bytes)); } catch(IOException e) { e.printStackTrace(); } } } }; in.start(); connectToSupervisor(); } private void connectToSupervisor() { byte[] bytes = new byte[1024]; System.out.println("Greeting server"); System.arraycopy("EHLO".getBytes(), 0, bytes, 0, 4); try { DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("localhost"), 789); socket.send(packet); System.out.println("Greetings sent..."); } catch(IOException e) { e.printStackTrace(); } send(); } private void send() { while(init != 2) { try { Thread.sleep(20L); } catch(InterruptedException e) { e.printStackTrace(); } } System.out.println("Init completed!"); while(true) { byte[] b2 = "Hello".getBytes(); byte[] b1 = new byte[6]; System.arraycopy(b2, 0, b1, 0, b2.length); try { DatagramPacket packet = new DatagramPacket(b1, b1.length, InetAddress.getByName(target), port); socket.send(packet); } catch(Exception e) { e.printStackTrace(); } } } public static void main(String[] args) { new Client(); } }
客户端将只是向服务器发送一个数据包,侦听来自它的数据包,从另一个客户端获取连接数据,然后将持续向其发送包含“ Hello”的数据包。
很长的代码,我很抱歉,但是我想保持完整。
如果你们中的任何人都可以指出我在做的错误,向我解释为什么这没有用,给我一个有效的例子,或者至少指出一个替代方案,我将很高兴。
您的代码似乎是正确的。我测试了您的代码,效果很好。这个概念也是正确的。但是,请检查您运行的两个客户端是在同一NAT设备中还是在不同的NAT设备中。如果您的两个客户端都在同一NAT设备下运行,则可能无法正常工作,因为并非所有NAT设备都支持发夹,即两个客户端都将数据包发送到NAT的外部IP,而该IP需要传递给自身。有关更多信息,请参考以下链接:http : //tools.ietf.org/html/rfc4787#section-6