Maciej
Maciej

Reputation: 127

Java NIO nonblocking: how to refuse incoming connections?

I'm trying to use server side code based on java NIO(non blocking) from 'The Rox Java NIO Tutorial'. There are lot of incoming socket connections and I would like to accept only 100. So if there are 100 active connections then new ones should be rejected/refused. But how to do that? There is only method ServerSocketChannel.accept() which returns SocketChannel object. Using that object I can call socketChannel.socket().close(), but connection is already open. Here is part of the code:

@Override
public void run() {
    while (true) {
        try {
            // Wait for an event one of the registered channels
            this.selector.select();

            // Iterate over the set of keys for which events are available
            Iterator selectedKeys = this.selector.selectedKeys().iterator();
            while (selectedKeys.hasNext()) {
                SelectionKey key = (SelectionKey) selectedKeys.next();
                selectedKeys.remove();

                if (!key.isValid()) {
                    continue;
                }

                // Check what event is available and deal with it
                if (key.isAcceptable()) {
                    this.accept(key);
                } else if (key.isReadable()) {
                    this.read(key);
                } else if (key.isWritable()) {
                    this.write(key);
                }
            }
        } catch (Exception e) {
            logger.warn("Reading data", e);
        }
    }
}

and accept() mehod:

 private void accept(SelectionKey key) throws IOException {
    // For an accept to be pending the channel must be a server socket channel.
    ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();

    // Accept the connection and make it non-blocking        
    if (noOfConnections < MAX_CONNECTIONS) {
        SocketChannel socketChannel = serverSocketChannel.accept();
        Socket socket = socketChannel.socket();
        socket.setKeepAlive(true);
        socketChannel.configureBlocking(false);
        // Register the new SocketChannel with our Selector, indicating
        // we'd like to be notified when there's data waiting to be read
        socketChannel.register(this.selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);//listener for incoming data: READ from client, WRITE to client
        noOfConnections++;
        logger.info("Accepted: " + socket.getRemoteSocketAddress().toString());
    } else {

        // REJECT INCOMING CONNECTION, but how?

        logger.warn("Server is full: " + noOfConnections + " / " + MAX_CONNECTIONS);
    }
}

If connection is not accepted then accept() method is being called over and over.

Thanks for help!

Upvotes: 5

Views: 4266

Answers (3)

LifeOnNet
LifeOnNet

Reputation: 107

Well, I managed this problem next way: Pending-state connections on socket are in kind of "middle_state", that mean you cannot control/reject them. Backlog socket parameter may be used/ignored/treated in different way by specific VM. That mean you have to accept particular connection to receive associated object and operate it.

Use one thread to accept connection, pass accepted connection to second thread for processing. Create some variable for number of active connections. Now, while number of active connections is less than wished maximum, accept connection, rise the number by 1, and pass to second thread for processing. Otherwise, accept connection and close that immediately.

Also, in connection process thread, than finished, decrease the number of active connections by 1 to point there is one more free channel available.

EDT: Just made the "stub" for server machanism for Java.Net NIO. May be adapted for OP needs:

package servertest;

import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;


public class Servertest extends Thread {
    final int MAXIMUM_CONNECTIONS = 3;
    int connectionnumber = 0;


    /**
     * @param args the command line arguments
     * @throws java.io.IOException
     */
    public static void main(String[] args){

        new Servertest().start();
    }

    @Override
    public void run() {
        try {
            ServerSocket sc = new ServerSocket(33000, 50, InetAddress.getLoopbackAddress());
          while (sc.isBound()) {
            Socket connection = sc.accept();
            if(connectionnumber<=MAXIMUM_CONNECTIONS){
                new ClientConnection(connection).start();
                connectionnumber++;
            } else {
                //Optionally write some error response to client
                connection.close();
            }
          }
        } catch (IOException ex) {
            Logger.getLogger(Servertest.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    private class ClientConnection extends Thread{
        private Socket connection;
        public ClientConnection(Socket connection) {
            this.connection=connection;
        }

        @Override
        public void run() {
            try {
                //make user interaction

                connection.close();


            } catch (IOException ex) {
                Logger.getLogger(Servertest.class.getName()).log(Level.SEVERE, null, ex);
            }

            connectionnumber--;

        }


    }

}

Upvotes: 0

user207421
user207421

Reputation: 311052

There is no way to accomplish that, but I doubt that that's what you really want, or at least what you really should do.

If you want to stop accepting connections, change the interestOps in the server socket channel's selection key to zero, and change it back to OP_ACCEPT when you are ready to accept again. In the interim, isAcceptable() will never be true, so the problem you describe won't occur.

However that won't cause further connections to be refused: it will just leave them on the backlog queue where in my opinion and that of the designers of TCP they belong. There will be another failure behaviour if the backlog queue fills up: its effect in the client is system-dependent: connection refusals and/or timeouts.

Upvotes: 2

Serge Bogatyrev
Serge Bogatyrev

Reputation: 816

I think any tuning of a backlog queue hardly ever would be a good solution. But probably, you can just stop listening.

Upvotes: 0

Related Questions