1

私は、TCP を介した P2P 通信を含む Java クライアント/サーバー アプリケーションに取り組んでいます。ここで説明されているように、TCPホールパンチングを実装しようとしています: http://www.brynosaurus.com/pub/net/p2pnat/#sec-tcp。これには、同じローカル TCP ポートを使用して、同時にリッスンし、送信接続を確立しようとする必要があります。どうやら、JavaSO_REUSEADDRのメソッドを介して設定しているソケットオプションが使用されている場合、これは機能するはずです。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 接続試行のみが機能し、その後の試行で "Address already in use" が表示されますBindException。オプションを有効にしているので、同時にリッスンしている TCP ソース ポートからの発信接続を確立し、ソケットを閉じた直後にローカル ポート番号を再利用することはSO_REUSEADDRできませんか?

4

2 に答える 2

1

Linux では、両方のソケットで SO_REUSEADDR ソケット オプションを設定する必要があります。したがって、sock1 と sock2 の 2 つのソケットを同じポートにバインドする場合、s2 は、sock1 と sock2 の両方が SO_REUSEADDR を設定している場合にのみ、ポート/アドレスを再利用できます。

于 2013-09-12T19:37:34.610 に答える
0

例外がない限り、クライアント ソケットを閉じることはありません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正しく使用できるようになりました。

于 2015-12-10T15:07:13.793 に答える