[英]Use same TCP port for accept and connect
I want to use same port for both inbound and outbound connections in java 我想在java中为入站和出站连接使用相同的端口
The purpose is to make a node in distributed environment. 目的是在分布式环境中创建节点。 But in Tcp I need to use two different ports for accepting and initiating connections.
但在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()
Now the problem is: 现在的问题是:
Socket.connect()
), A & B will keep the socket open for future message passing. Socket.connect()
)时,A&B将保持套接字打开以便将来传递消息。 Socket()
instance which doesn't have a accept()
method Socket()
实例绑定的,该实例没有accept()
方法 Of course, A can inform B about the port it is listening, but isn't there a direct way? 当然,A可以通知B关于它正在收听的端口,但是没有直接的方法吗?
How can I make this test to pass? 我怎样才能通过这个测试?
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.
}
There is no such problem in when I previously used udp. 我之前使用udp时没有这样的问题。 The same socket supports
receive()
and send()
method. 同一个套接字支持
receive()
和send()
方法。 For udp, sharing address is easy. 对于udp,共享地址很容易。
socketAddress
of A, socketAddress
, SO_REUSEADDR option shall be set on a socket before binding. 在绑定之前,应在套接字上设置SO_REUSEADDR选项。 I initially did my tests in Python (no access to a Java environment) and following script worked without errors on a Windows 10 system:
我最初在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))
with another process listening on port 8090 与另一个进程侦听端口8090
Unfortunately, in Java setReuseAddr
javadoc says explicitely (emphasize mine): 不幸的是,在Java
setReuseAddr
javadoc明确地说(强调我的):
Enabling SO_REUSEADDR prior to binding the socket using bind(SocketAddress) allows the socket to be bound even though a previous connection is in a timeout state .
在使用bind(SocketAddress)绑定套接字之前启用SO_REUSEADDR允许套接字绑定,即使先前的连接处于超时状态 。
For reasons I cannot guess, Java is more restrictive here. 由于我无法猜测的原因,Java在这里更具限制性。 What looks even more weird, is that according to this other question it used to be allowed on older JRE versions (up to JRE 7U5)
看起来更奇怪的是,根据这个其他问题,它曾经被允许用于较旧的JRE版本(最高为JRE 7U5)
Original (and wrong) post follows: 原始(和错误)帖子如下:
The trick is to set the SO_REUSEADDR option before binding. 诀窍是在绑定之前设置SO_REUSEADDR选项。 That means that you will need to use a parameterless constructor for both
ServerSocket
et Socket
. 这意味着您将需要为
ServerSocket
和Socket
使用无参数构造函数。 More or less: 或多或少:
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(...);
That way you can connect from your local listening port so that the peer will know how to reconnect after the connection will be closed. 这样,您就可以从本地侦听端口进行连接,以便对等端在连接关闭后知道如何重新连接。
Beware: untested... 小心:未经测试......
I've fixed your test - hope that it's the way you wanted it to be. 我已经修好了你的考试 - 希望这是你想要的方式。 Take a look at the code here:
看看这里的代码:
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.
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.