Is it possible to create multiple (SSL) ServerSocket on the same port? [closed]

馋奶兔 提交于 2020-01-24 00:56:07

问题


public class ServerThread implements Runnable 
{

    private static final int port = 10000;

    @Override
    public void run() {
        ServerSocket serverSocket = new ServerSocket(port);

        while (true) {
            Socket clientSocket = serverSocket.accept();
            ClientThread clientThread = new ClientThread(clientSocket);
            // handle the client request in a separate thread
        }
    }
}

Will this work If I have let's say 10 different threads running ServerThread.run()? Or should I use the same ServerSocket object for all threads?

The docs say:

The constructor for ServerSocket throws an exception if it can't listen on the specified port (for example, the port is already being used)

You may be wondering why I want to do this in the first place and not simply have a single thread running ServerSocket.accept(). Well, my assumption is (correct me if I'm wrong) that the accept() method may take some time to finish establishing a connection, especially if the ServerSocket is SSL (because of handshaking). So if two clients want to connect at the same time, one has to wait for the other. This would be very bad for a high traffic server.

Update: It seems that the accept() method will return as soon as a connection belonging to a queue is established. This means if there's a queue of clients waiting to connect, the server thread can handle the requests in the fastest way possible and only one thread is needed. (apart from the time it takes to create a new thread for each request and starting the thread, but that time is negligible when using a thread pool)

The ServerSocket also has a parameter called "backlog", where you can set the maximum number of connections in the queue. According to the book "Fundamental Networking in Java" 3.3.3

TCP itself can get ahead of a TCP server application in accepting connections. It maintains a ‘backlog queue’ of connections to a listening socket which TCP iself has completed but which have not yet been accepted by the application. This queue exists between the underlying TCP implementation and the server process which created the listening socket. The purpose of pre-completing connections is to speed up the connection phase, but the queue is limited in length so as not to pre-form too many connections to servers which are not accepting them at the same rate for any reason. When an incoming connection request is received and the backlog queue is not full, TCP completes the connection protocol and adds the connection to the backlog queue. At this point, the client application is fully connected, but the server application has not yet received the connection as a result value of ServerSocket.accept. When it does so, the entry is removed from the queue.

I'm still not sure though in the case of SSL, if the handshaking is also done in parallel by ServerSocket.accept() for simultaneous connections.

Update 2 The ServerSocket.accept() method itself doesn't do any real networking at all. It will return as soon as the operating system has established a new TCP connection. The operating system itself holds a queue of waiting TCP connections, which can be controlled by the "backlog" parameter in the ServerSocket constructor:

ServerSocket serverSocket = new ServerSocket(port, 50);
//this will create a server socket with a maximum of 50 connections in the queue

SSL handshaking is done after the client calls Socket.connect(). So one thread for ServerSocket.accept() is always enough.


回答1:


Here are a few thoughts regarding your problem:

You can't listen() on the same IP+port with several ServerSocket. If you could, to which one of the socket would the OS transfer the SYN packet?*

TCP indeed maintains a backlog of pre-accepted connections so a call to accept() will return (almost) immediately the first (oldest) socket in the backlog queue. It does so by sending the SYN-ACK packet automatically in reply to a SYN sent by the client, and waits for the reply-ACK (the 3-way handshake). But, as @zero298 suggests, accepting connections as fast as possible isn't usually the problem. The problem will be the load incurred by processing communication with all the sockets you'll have accepted, which may very well put your server down its knees (it's actually a DoS attack). In fact the backlog parameter is usually here so too many simultaneous connections waiting for too long in the backlog queue to be accept()ed will be dropped by TCP before reaching your application.

Instead of creating one thread per client socket, I would suggest you use an ExecutorService thread pool running some maximum number of threads, each handling communication with one client. That allows for a graceful degradation of system resources, instead of creating millions of threads which would in turn create thread starvation, memory issues, file descriptor limits, ... Coupled with a carefully-chosen backlog value you'll be able to get the maximum throughput you server can offer without crashing it. And if you're worried about DoS on SSL, the very first thing the run() method of your client-thread should do, is call startHandshake() on the newly-connected socket.

Regarding the SSL part, TCP itself cannot do any SSL pre-accept, as it need to perform encryption/decoding, talking to a keystore, etc. which are well beyond its specification. Note that you should also use an SSLServerSocket in that case.

To go around the use-case you gave (clients willingly delaying handshake to DoS your server) you'll be interested reading an Oracle forum post about it where EJP (again) answers:

The backlog queue is for connections that have been completed by the TCP stack but not yet accepted by the application. Nothing to do with SSL yet. JSSE doesn't do any negotiation until you do some I/O on the accepted socket, or call startHandshake() on it, both of which you would do in the thread dealing with the connection. I don't see how you can make a DOS vulnerability out of that, at least not an SSL-specific one. If you are experiencing DOS conditions, most probably you are doing I/O in the accept() thread that should be done in the connection-handling thread.

*: Though Linux >=3.9 does some kind of load-balancing, but for UDP only (so not SSLServerSocket) and with option SO_REUSEPORT, which is not available on all platforms anyway.



来源:https://stackoverflow.com/questions/57190490/is-it-possible-to-create-multiple-ssl-serversocket-on-the-same-port

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!