我想对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()
现在的问题是:
Socket.connect()
)时,A和B将保持套接字打开以供将来的消息传递。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,共享地址很容易。
socketAddress
,答案 0 :(得分:0)
SO_REUSEADDR选项应在绑定之前在套接字上设置。最初,我使用Python(无法访问Java环境)进行测试,并且以下脚本在Windows 10系统上运行时没有错误:
import socket
serv = socket.socket() # set a listening socket
serv.bind(('0.0.0.0',8080))
serv.listen(5)
s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('0.0.0.0',8080))
s.connect(('127.0.0.1', 8090))
使用另一个进程监听端口8090
不幸的是,在Java setReuseAddr
中,javadoc明确表示(强调我的意思):
在使用bind(SocketAddress)绑定套接字之前启用SO_REUSEADDR,即使先前的连接处于超时状态,也可以绑定套接字。
出于我无法猜测的原因,Java在这里更具限制性。看起来更怪异的是,根据其他question,它曾经在较旧的JRE版本(最高JRE 7U5)上被允许使用
原始(错误的)帖子如下:
技巧是在绑定之前设置SO_REUSEADDR选项。这意味着您将需要同时为ServerSocket
和Socket
使用无参数的构造函数。或多或少:
ServerSocket localServer = new ServerSocket();
localServer.setReuseAddress(true);
localServer.bind(InetSocketAddress(localServerport));
... // Ok listening...
Socket socket = new Socket();
socket.setReuseAddress(true);
socket.bind(InetSocketAddress(localServerport));
socket.connect(...);
这样,您可以从本地侦听端口进行连接,以便对等方将知道在连接关闭后如何重新连接。
当心:未经测试...
答案 1 :(得分:0)
我已修正您的测试-希望这就是您想要的方式。在这里看看代码:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.junit.Test;
public class DualSocketTest {
ExecutorService service = Executors.newFixedThreadPool(10);
int echoServerport = 8080;
int localServerport = 8080;
@Test
public void testConnectivity() throws IOException {
// create a echo server on port 8080
startEcho();
// create a socket to connect the echo server using the same port used by localServer
Socket socket = new Socket();
// but this will throw SocketBindException
socket.connect(new InetSocketAddress(echoServerport));
// write hello
socket.getOutputStream().write("Hello !".getBytes());
socket.getOutputStream().flush();
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 {
byte[] buffer = new byte[1024];
int read = -1;
while ((read = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, 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.
}