在评论正确使用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的套接字地址,但该端口是由
Socket()
实例绑定的,该实例没有accept()
方法
当然,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时没有这样的问题 . 相同的套接字支持 receive()
和 send()
方法 . 对于udp,共享地址很容易 .
-
当A连接B时,B将保存A的
socketAddress
, -
当C连接B时,B将A的地址发送给C,C将连接到A.
2 回答
编辑:不会在JAVA中工作
在绑定之前,应在套接字上设置SO_REUSEADDR选项 . 我最初在Python中进行了测试(无法访问Java环境),并且以下脚本在Windows 10系统上运行时没有错误:
与另一个进程侦听端口8090
不幸的是,在Java
setReuseAddr
中javadoc明确地说(强调我的):由于我无法猜测的原因,Java在这里更具限制性 . 看起来更奇怪的是,根据其他question它曾经允许在较旧的JRE版本上使用(最高为JRE 7U5)
原始(和错误)帖子如下:
诀窍是在绑定之前设置SO_REUSEADDR选项 . 这意味着您需要为
ServerSocket
etSocket
使用无参数构造函数 . 或多或少:这样,您就可以从本地侦听端口进行连接,以便对等端在连接关闭后知道如何重新连接 .
小心:未经测试......
我已经修好了你的考试 - 希望这是你想要的方式 . 看看这里的代码: