Search code examples
javasocketsnio

Why my non blocking Java server refuses client connections?


Good day everybody! I'm developing NIO based server and I'm trying to test it with simple client programm.

Before posting code I would like to briefly describe problem: in the test case where server does his job immediately everything is OK. But when I'm trying to add some real life behavior such as short delay on servicing I'm getting "java.net.ConnectException: Connection refused" exceptions. More precisely, part of 100 client threads get this connection refused exception.

I use the following code:

Client

public class TCPClient implements Runnable{

private String name;

public TCPClient(String name)
{
  this.name = name;
}

public static void main(String[] args)
{

   for(int i=0;i<100;i++)
   {
        Thread t = new Thread(new TCPClient("thread # "+Integer.toString(i)));
        t.start();
   }
}

@Override
public void run()
{
   Socket socket = null;
   OutputStream out = null;
   int counter = 0;
   try
   {
        socket = new Socket();
        socket.connect(new InetSocketAddress("192.168.3.109",2345), 0); 
        out =  socket.getOutputStream();

        byte[] bytes;
        while(counter<100)
        {
            counter++;
            bytes = (name+ ", message # "+Integer.toString(counter)+System.lineSeparator()).getBytes();
            out.write(bytes); 
            out.flush();
            Thread.sleep(200);
        }
   }
   catch(Exception ex)
   {
            System.out.println(name+" "+Integer.toString(counter));
            ex.printStackTrace(new PrintStream(System.out));
            System.out.println();
   }
    finally
    {
        if(socket!=null && out!=null)
        {
            try
            {
                socket.close();
                out.close();
            }
            catch(Exception ex)
            {
                System.out.println("client close error");
            }
        }
    }
}

}

Server

public class TCPServer  {

private Selector selector;
private boolean isRunning;
private ServerSocketChannel server;
private int counter;
private PrintWriter times;
private PrintWriter logger;
private Charset charset;
private CharsetDecoder decoder;
ByteBuffer bb;
long serviceTime,curTime;
Random random;
public TCPServer(int port)
{

    counter = 0;
    isRunning = false; 
    serviceTime = 0;
    random = new Random();
    random.setSeed(System.currentTimeMillis());
    bb =  ByteBuffer.allocate(2048);

try
{
    selector = Selector.open(); 
server = ServerSocketChannel.open(); 
server.socket().bind(new InetSocketAddress(port)); 
server.configureBlocking(false); 
server.register(selector, SelectionKey.OP_ACCEPT);


}
catch(Exception ex)
{
    System.out.println("initialization error "+ex.getMessage());

}

}


public void startServer() {
    isRunning = true;
    int acc = 0;
    boolean error = false;
    while (isRunning) {
       try 
       {


         selector.select(); 

         Set keys =  selector.selectedKeys();
         Iterator it = keys.iterator(); 
         while(it.hasNext())
         {
             SelectionKey key = (SelectionKey)it.next();

             if (key.isConnectable())
             { 
        ((SocketChannel)key.channel()).finishConnect(); 
     } 

             if (key.isAcceptable())
             { 
        //logger.println("socket accepted");
                    //logger.flush();
                    acc++;
                    System.out.println("accepted sockets count = "+acc);
                    SocketChannel client = server.accept(); 
                    client.configureBlocking(false); 
                    client.socket().setTcpNoDelay(true); 
                    client.register(selector, SelectionKey.OP_READ); 
     } 

             if (key.isReadable())
             { 

                   curTime = System.currentTimeMillis();
                   SocketChannel sc = (SocketChannel) key.channel();
                   bb.clear();
                   int x =  sc.read(bb); 

                   if(x==-1)
                   {
                       key.cancel();
                       continue;
                   }

                   counter++;

                  // Thread.sleep(2);
                   int sum=0;
                   for(int dummy=0;dummy<4000000;dummy++) // without this delay client works fine
                   {
                       sum+=random.nextInt();
                       sum%=1005;
                   }

                   serviceTime+= System.currentTimeMillis() - curTime;
                   if(counter>=10000)
                   {
                        System.out.println("recieved messages count = "+counter);
                        System.out.println("service time = "+serviceTime+" milliseconds");
                   }


     } 
         }
         keys.clear();
  } 
  catch (Exception ex) 
  {     

      System.out.println("error in recieving messages "+ex.getMessage());

  }

 }
}

public static void main(String[] args)
{

   TCPServer deviceServer = new TCPServer(2345);
   deviceServer.startServer();

}
}

The problem is in for(dummy...) loop - it's just simulation of service delay - time needed to parse incoming messages, write something to DB and so on. When delay is small code works fine, all of 10000 messages come to server (100 client threads X 100 messages from each client) but when dummy loop makes over 3.000.000 iterations some of client threads fail to connect to server. One more strange thing here is ignoring infinite timeout property by client socket. I mean socket.connect(InetAddress,timeout) with timeout equal to zero means infinite timeout - in other words service delay doesn't make sense at least I expect such behavior.


Solution

  • It looks like the server socket has a maximum number of pending connections it will allow. The JavaDoc for ServerSocket says:

    The maximum queue length for incoming connection indications (a request to connect) is set to 50. If a connection indication arrives when the queue is full, the connection is refused.

    Right now, I can't find the same information for ServerSocketChannel, but I'm sure it must exist.

    ServerSocketChannel.bind allows configuration of the number of pending connections allowed.