Я не знаю, как четко описать мой вопрос в заголовке, так как для меня это немного сложно.Я пытаюсь реализовать одноранговую демонстрацию TCP, в которой локальный порт должен быть как для прослушивания, так и для инициирования сокета.
Я приведу подробное описание.Я дам реализацию Java, которая будет прослушивать и инициировать соединение на одном локальном порту.Код объяснит мою идею.
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:
ps#1> java TcpPeer localhost 2000 4000
ps#2> java TcpPeer localhost 4000 2000
Наконец, когда 2 процесса стабилизировались, они будут давать следующие выходные данные:
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 # 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 прослушивает 2000.
- ps # 2 прослушивает 4000.
- ps # 1 подключается к ps # 2, из localhost: 2000 -> localhost: 4000.
- ps # 2 закройте соединение, которое установилось на шаге # 3.
- ps # 2 попытаться подключиться к ps # 1 на 2000, не удалось!
Почему ps # 2 не может подключиться к ps # 1 на шаге # 4?Я также отслеживаю состояние сети ОС.
Ниже приведено состояние сети сразу после шага № 3.
А также статус сети сразу после шага № 4.
- 192.168.2.107 is localhost
Не могли бы вы дать мне комментарий к моему делу?спасибо!
Я распечатал исходное сообщение об исключении, когда не удалось восстановить соединение, но у меня нет особого представления об этом исключении.