在评论中指出正确使用SO_REUSEADDR后进行编辑
我想在Java中同时使用相同的端口进行入站和出站连接。
目的是在分布式环境中创建一个节点。但在Tcp中,我需要使用两个不同的端口来接受和发起连接。
// accept incoming connection on one port
ServerSocket.accept()
// connect to remote, the port used will be different from the one used for accepting
Socket.connect()
现在问题是:
- A监听端口a,B监听端口b,C监听端口c。 - 当A连接B(使用Socket.connect()),A和B将保持套接字打开以进行未来的消息传递。 - B仍然不知道A正在侦听的端口,因为从B接收连接的端口与a不同。 - 当C连接B时,B将A的套接字地址提供给C,但该端口是由一个没有accept()方法的Socket()实例绑定的。
当然,A可以告诉B它正在监听的端口,但难道没有直接的方法吗?
如何使此测试通过?
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.DatagramSocket;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class DualSocketTest {
ExecutorService service= Executors.newFixedThreadPool(10);
int echoServerport=8080;
int localServerport=8090;
@Test
public void testConnectivity() throws IOException {
// create a echo server on port 8080
startEcho();
// create a local Server instance
ServerSocket localServer=new ServerSocket();
// set the reuseAddress to true
localServer.setReuseAddress(true);
// bind the serverSocket
localServer.bind(new InetSocketAddress(localServerport));
// create a socket to connect the echo server using the same port used by localServer
Socket socket = new Socket();
socket.setReuseAddress(true);
// but this will throw SocketBindException
socket.bind(new InetSocketAddress(localServerport));
socket.connect(new InetSocketAddress(echoServerport));
// write hello
socket.getOutputStream().write("Hello !".getBytes());
byte[] result=new byte[100];
// receive hello
String ans=new String(result,0,socket.getInputStream().read(result));
System.out.println("Server replied with : "+ans);
// what was written and what was received must be same.
assert(ans.equals("Hello !"));
}
// start a echo server listening on the specified port
private void startEcho() throws IOException {
ServerSocket echoServer=new ServerSocket(echoServerport);
service.submit(()->{
try {
while(!echoServer.isClosed()) {
Socket socket = echoServer.accept();
System.out.println("connected with :" + socket.getInetAddress().toString() + ":" + socket.getPort());
InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream();
service.submit(() -> {
while (socket.isConnected()) {
try {
outputStream.write(inputStream.read());
} catch (IOException e) {
break;
}
}
System.out.println("The Client has closed connection.");
});
}
} catch (IOException e) {
e.printStackTrace();
}
});
Thread.yield();
}
// Write something to the socket.
}
之前使用udp时没有这样的问题。同一个socket支持receive()
和send()
方法。对于udp,共享地址很容易。
- 当A连接B时,B会保存A的
socketAddress
- 当C连接B时,B会将A的地址发送给C,C会连接到A
A
正在侦听端口a
,那么B
和C
都可以通过端口a
连接到A
。 - Kayaman