私は、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
できませんか?