Francesc Lordan
Francesc Lordan

Reputation: 529

Java NIO: A serverSocketChannel accepts a socket request and client receives the acceptance but the server does not log it

I'm trying to develop my own communication library based on non-blocking NIO messages. I've been reading 1000 tutorials and book chapters about it and I think that at the end I have something that works with few simultaneous connections. But I'm having some issues when I have many connections coexisting on the server-side.

I have the typical selector implementation with the 4 private methods: accept, finishConnect, read and write. My problem lies on the first two ones: Accept and finishConnect.

When the client opens a new socket, and an acceptable key wakes the selector up, the following code is executed.

private void accept(SelectionKey key) {
    try {
        ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
        SocketChannel sc = ssc.accept();
        sc.configureBlocking(false);
        LOGGER.debug("Socket " + sc.hashCode() + "-" + sc.socket().toString() + " connexion completed");
        changeInterest(sc, SelectionKey.OP_READ);
        eventManager.addEvent(new ConnectionEstablished(sc));
    } catch (Throwable e) {
        NIOException ne = new NIOException(NIOException.ErrorType.ACCEPTING_CONNECTION, e);
        eventManager.addEvent(new ErrorEvent(null, ne));
    }
}

On the client side, I have this implementation for the connect method that will be invoked once the server processes its acceptable key for the socket.

private void finishConnect(SelectionKey key) {
    SocketChannel sc = (SocketChannel) key.channel();
    try {
        if (sc.finishConnect()) {
            eventManager.addEvent(new ConnectionEstablished(sc));
            LOGGER.debug("Socket " + sc.hashCode() + "-" + sc.socket().toString() + " connection finished");
        } else {
            LOGGER.debug("REFUSED " + sc + " - " + sc.socket().toString());
            refusedConnection(sc, null);
            key.cancel();
        }
    } catch (Exception e) {
        refusedConnection(sc, e);
        key.cancel();
    }
}

The thing is that when I create some connections are accepted, the client executes the finishConnect message (and I can see the socket connection established with the ports used). But I can't find this connection acceptance on the server side, there is no connection completed log message using those ports!!

I suspected that an exception could arise between the ssc.accept() and the log invocation so I added some extra log messages to check which instruction was blowing everything up. The sequence was completed for all the keys that got into the accept method.

How is that possible if I can't even see any Error message on the log ?

EDIT: I made some tests on the number of sockets that are open at a time. When the client starts running, there's just one openSocket on the server: the server socket. After that it has up to 200 simultaneous open sockets, and at the end of the client execution the servers goes back to 1 open socket. I guess that they are never counted

By now, I've made a workaround that monitors the amount of coexisting connections on the node and delays new connections acceptance until that number is reduced to a given threshold. However I would like to understand what's going wrong on.

Thanks for your help.

Upvotes: 1

Views: 1367

Answers (2)

Francesc Lordan
Francesc Lordan

Reputation: 529

As EJP suggested the problem was on the backlog queue. I was binding the ServerSocketChannel using the bind(SocketAddress local) method.

When a socket request arrives to the JVM it is enqueued to the backlog queue and waits there until the Listener triggers the process of the corresponding key to be accepted. The actual problem lies on the size of this queue, using the bind method, it stores up to 50 connections.

When a peak of connection requests happens, there's an overflow on the queue and some of them are lost. To avoid this happening, the method bind(SocketAddress local, int backlog), allows to change the capacity of the queue and increase it.

On the other side, when working in non-blocking mode, the selector on the client node does not need the connection to be accepted to process a OP_CONNECT key. The reception of the SYN-ACK TCP message will trigger the corresponding key in the selector.

Upvotes: 1

user207421
user207421

Reputation: 310884

Because of the backlog queue, it's perfectly in order for a large number of client connections to complete before accept() is executed. So there is no actual problem here to solve.

But are you ever executing the accept() method? This is the bug you need to investigate.

Upvotes: 1

Related Questions