私はタイトルの質問をはっきりと説明する方法がわかりません。私にとっては少し複雑です。私がやっていることは、ローカルポートがリッスンとソケットの開始の両方でなければならないTCPピアツーピアデモを実装しようとすることです。Java TCP SO_RESUEADDR、接続できません
詳細な説明をします。 私は、単一のローカルポートで接続をリッスンして開始するJava実装を提供します。コードが私の考えを説明します。 2つのプロセスが安定して得たときに最後に、彼らは出力の下に与える
ps#1> java TcpPeer localhost 2000 4000
ps#2> java TcpPeer localhost 4000 2000
:
PS#1>
[Server]The server is listening on 2000.
[Client]socket.isBound():false
[Client]connect to localhost:4000 successfully.
[Client][RESP-1]hello world!
PS
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
/**
* Just for testing socket SO_RESUEADDR. If set SO_RESUEADDR to true, we can use
* a single local port to listen for incoming TCP connections, and to initiate
* multiple outgoing TCP connections concurrently. By this way we can implement
* TCP hole punching(establish P2P connection traversal through NAT over TCP).
*/
public class TcpPeer {
// TCP port is a different source from UDP port, it means you can listen on
// same port for both TCP and UDP at the same time.
private int localport = 7890;
private ServerSocket peerSock;
private Socket serverSocket;
public TcpPeer(final String serverHost, final int serverPort, final int localPort)
throws Exception {
this.localport = localPort;
Thread server = new Thread(new Runnable() {
@Override
public void run() {
try {
peerSock = new ServerSocket();
peerSock.setReuseAddress(true);
peerSock.bind(new InetSocketAddress("localhost", localport));
System.out.println("[Server]The server is listening on " + localport + ".");
while (true) {
try {
serverSocket = peerSock.accept();
// just means finishing handshaking, and connection
// established.
System.out.println("[Server]New connection accepted"
+ serverSocket.getInetAddress() + ":" + serverSocket.getPort());
BufferedReader br = getReader(serverSocket);
PrintWriter pw = getWriter(serverSocket);
String req = br.readLine();
System.out.println("[Server][REQ]" + req);
pw.println(req);
pw.close();
br.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (serverSocket != null)
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
// server.setDaemon(true);
server.start();
Thread.currentThread();
// sleep several seconds before launch of client
Thread.sleep(5 * 1000);
final int retry = 5;
Thread client = new Thread(new Runnable() {
@Override
public void run() {
Socket socket = new Socket();
try {
socket.setReuseAddress(true);
System.out.println("[Client]socket.isBound():" + socket.isBound());
socket.bind(new InetSocketAddress("localhost", localport));
for (int i = 1; i < retry; i++) {
try {
socket.connect(new InetSocketAddress(serverHost, serverPort));
System.out.println("[Client]connect to " + serverHost + ":"
+ serverPort + " successfully.");
break;
} catch (Exception e) {
e.printStackTrace();
System.out.println("[Client]fail to connect " + serverHost + ":"
+ serverPort + ", try again.");
Thread.currentThread().sleep(i * 2 * 1000);
if (i == retry - 1) return;
}
}
PrintWriter pw = getWriter(socket);
String msg = "hello world!";
pw.println(msg);
/**
* Got response from the server socket.
*/
BufferedReader br = getReader(socket);
String resp = br.readLine();
System.out.println("[Client][RESP-1]" + resp);
pw.close();
br.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
client.start();
}
private PrintWriter getWriter(Socket socket) throws IOException {
OutputStream socketOut = socket.getOutputStream();
return new PrintWriter(socketOut, true);
}
private BufferedReader getReader(Socket socket) throws IOException {
InputStream socketIn = socket.getInputStream();
return new BufferedReader(new InputStreamReader(socketIn));
}
public static void main(String[] args) throws Exception {
if (args.length != 3) {
System.out.println("[Usage] java " + TcpPeer.class.getCanonicalName()
+ " [serverHost] [serverPort] [localPort]");
System.exit(0);
}
new TcpPeer(args[0], Integer.parseInt(args[1]), Integer.parseInt(args[2]));
}
}
は、今、私たちは2つのJVMプロセスを起動します#2>
[Server]The server is listening on 4000.
[Server]New connection accepted/127.0.0.1:2000
[Server][REQ]hello world!
[Client]socket.isBound():false
java.net.BindException: Address already in use: connect
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:333)
at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:195)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:182)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366)
at java.net.Socket.connect(Socket.java:525)
at java.net.Socket.connect(Socket.java:475)
at org.clinic4j.net.TcpPeer$2.run(TcpPeer.java:92)
at java.lang.Thread.run(Thread.java:619)
[Client]fail to connect localhost:2000, try again.
java.net.SocketException: Socket operation on nonsocket: connect
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:333)
at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:195)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:182)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366)
at java.net.Socket.connect(Socket.java:525)
at java.net.Socket.connect(Socket.java:475)
at org.clinic4j.net.TcpPeer$2.run(TcpPeer.java:92)
at java.lang.Thread.run(Thread.java:619)
[Client]fail to connect localhost:2000, try again.
java.net.SocketException: Socket operation on nonsocket: connect
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:333)
at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:195)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:182)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366)
at java.net.Socket.connect(Socket.java:525)
at java.net.Socket.connect(Socket.java:475)
at org.clinic4j.net.TcpPeer$2.run(TcpPeer.java:92)
at java.lang.Thread.run(Thread.java:619)
[Client]fail to connect localhost:2000, try again.
java.net.SocketException: Socket operation on nonsocket: connect
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:333)
at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:195)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:182)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366)
at java.net.Socket.connect(Socket.java:525)
at java.net.Socket.connect(Socket.java:475)
at org.clinic4j.net.TcpPeer$2.run(TcpPeer.java:92)
at java.lang.Thread.run(Thread.java:619)
[Client]fail to connect localhost:2000, try again.
の出力から
、我々は相互作用は以下のように流れていることを把握することができます:
は- PS#1は2がPSに接続する4000
- PS#1に聞く2000年
- のps#に聞きます#2、from localhost:2000 - > localhost:4000。
- ps#2は、ステップ#3で確立された接続を閉じます。
- ps#2は2000年にps#1に接続しようとしましたが、失敗しました!
なぜ、ps#2がステップ#4でps#1に接続できないのですか?私はまた、OSのネットステータスを監視します。
以下は、手順3の直後のネットステータスです。
また、手順4の直後のネットステータス。
- 192.168.2.107は
をlocalhostですあなたは私の私の場合にコメントをお願いできますか?ありがとう!
再接続に失敗した場合、元の例外メッセージが表示されていますが、その例外についてはわかりません。
"ローカルポートは、リスンとソケットの開始の両方でなければなりません。"どうして? – EJP
私は[Peer-to-Peer Communication of Network Address Translators](http://www.brynosaurus.com/pub/net/p2pnat/)の論文を読んで、NAT経由でP2P TCP接続を確立しようとします。 – Ramon
こんにちはラモン、同様の状況で立ち往生。あなたはこれのための解決策を見つけましたか?ありがとう! – pkrish