使用相同的TCP端口进行接受和连接

Sun*_*nal 6 java sockets tcp ip-address

在评论正确使用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()
Run Code Online (Sandbox Code Playgroud)

现在的问题是:

  • 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.
}
Run Code Online (Sandbox Code Playgroud)

我之前使用udp时没有这样的问题.相同的套接字支持receive()send()方法.对于udp,共享地址很容易.

  • 当A连接B时,B会保存socketAddressA,
  • 当C连接B时,B将A的地址发送给C,C将连接到A.

Ser*_*sta 0

编辑:在 JAVA 中不起作用

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))
Run Code Online (Sandbox Code Playgroud)

另一个进程正在侦听端口 8090

不幸的是,在 Java setReuseAddrjavadoc 中明确指出(强调我的):

在使用bind(SocketAddress) 绑定套接字之前启用SO_REUSEADDR 允许绑定套接字,即使先前的连接处于超时状态也是如此

由于我无法猜测的原因,Java 在这里受到更多限制。看起来更奇怪的是,根据另一个问题,它曾经被允许在较旧的 JRE 版本(高达 JRE 7U5)上使用


原始(错误)帖子如下:

诀窍是在绑定之前设置 SO_REUSEADDR 选项。这意味着您需要对ServerSocketet都使用无参数构造函数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(...);
Run Code Online (Sandbox Code Playgroud)

这样您就可以从本地侦听端口进行连接,以便对等方知道在连接关闭后如何重新连接。

注意:未经测试...