[英]TCP Hole Punching and setReuseAddress() on Linux
我正在研究一个涉及通过TCP进行P2P通信的Java客户端/服务器应用程序。 我正在尝试按此处所述实现TCP打孔: http : //www.brynosaurus.com/pub/net/p2pnat/#sec-tcp 。 这要求同时侦听并尝试使用同一本地TCP端口建立传出连接。 显然,如果使用了SO_REUSEADDR
套接字选项(我正在通过Java中的setReuseAddress()
方法进行设置setReuseAddress()
,这应该可以工作。 但是,这不符合我的预期。 这是一些测试代码:
import java.io.IOException;
import java.net.*;
public class Test {
public static void main(String args[]) {
new Thread() {
public void run() {
try {
ServerSocket ss = new ServerSocket();
ss.setReuseAddress(true);
ss.bind(new InetSocketAddress(7077));
ss.accept();
} catch (Exception e) {
System.out.println("ServerSocket exception: " + e.getMessage());
}
}
}.start();
Socket s;
while (true) {
s = new Socket();
try {
s.setReuseAddress(true);
s.bind(new InetSocketAddress(7077));
s.connect(new InetSocketAddress("192.168.0.103", 7077));
break;
} catch (Exception e) {
System.out.println("Socket exception: " + e.getMessage());
try { s.close(); } catch (IOException e1) { }
try { Thread.sleep(1000); } catch (InterruptedException e1) { }
}
}
}
}
这可以在Windows 7中按预期工作: ServerSocket
在其自己的线程中侦听端口7077,并且Socket反复尝试连接至192.168.0.103:7077。 但是,在Linux(Ubuntu)下,只有第一个Socket连接尝试有效,随后的尝试将获得“地址已在使用中” BindException
。 因为启用了SO_REUSEADDR
选项,我不应该能够从同时监听的TCP源端口建立传出连接,并在关闭套接字后立即重用本地端口号吗?
在Linux中,两个套接字都需要设置SO_REUSEADDR套接字选项。 因此,如果我们希望将两个套接字sock1和sock2绑定到同一端口,则只有在sock1和sock2都设置了SO_REUSEADDR的情况下,s2才能够重用该端口/地址。
除非有例外,否则您永远都不会关闭客户端套接字,从而使SO_REUSEADDR
成为不可能。
....
s = new Socket();
try {
// ...
} catch (Exception e) {
System.out.println("Socket exception: " + e.getMessage());
// remove try block from here
try { Thread.sleep(1000); } catch (InterruptedException e1) { }
} finally {
try { s.close(); } catch (IOException e1) { }
}
....
在上面的代码中,我将套接字的关闭位置移到了新创建的finally块中,因此即使您中断了全局while循环,它也始终会执行。
由于套接字现在在所有情况下都是关闭的,因此SO_REUSEADDR
现在将正确使用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.