首页 文章

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

提问于
浏览
6

在评论正确使用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 回答

  • 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))
    

    与另一个进程侦听端口8090

    不幸的是,在Java setReuseAddr 中javadoc明确地说(强调我的):

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

    由于我无法猜测的原因,Java在这里更具限制性 . 看起来更奇怪的是,根据其他question它曾经允许在较旧的JRE版本上使用(最高为JRE 7U5)


    原始(和错误)帖子如下:

    诀窍是在绑定之前设置SO_REUSEADDR选项 . 这意味着您需要为 ServerSocket et 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(...);
    

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

    小心:未经测试......

  • 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.
    }
    

相关问题