繁体   English   中英

在Linux上进行TCP打孔和setReuseAddress()

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM