Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How java nio ServerSocketChannel accept works?

I can't get how NIO works under the hood. Here is a sample code:

// Create the server socket channel
ServerSocketChannel server = ServerSocketChannel.open();
// nonblocking I/O
server.configureBlocking(false);
// host-port 8000
server.socket().bind(new java.net.InetSocketAddress(host,8000));

// Create the selector
Selector selector = Selector.open();
// Recording server to selector (type OP_ACCEPT)
server.register(selector,SelectionKey.OP_ACCEPT);

while (true) {
      selector.select(); // blocking operation
      Iterator it = selector.selectedKeys().iterator();
      while (it.hasNext()) {
        SelectionKey selKey = (SelectionKey) it.next();

        // THE MOST INTRIGUING PART HERE!!!
        if (selKey.isAcceptable()) {
          ServerSocketChannel ssChannel = (ServerSocketChannel) selKey.channel();
          SocketChannel sc = ssChannel.accept();
        }
        it.remove();
     }
}

Here I have a couple of questions:

  1. selKey.channel() return a ServerSocketChannel is it exactly the same channel we created in the beggining with ServerSocketChannel.open()? If not, then what is it?
  2. More important question: in most other tutorials selKey.channel(); step is skipped and they simply use SocketChannel client = server.accept(); For example here: http://www.onjava.com/pub/a/onjava/2002/09/04/nio.html?page=2 and here: http://www.developer.com/java/article.php/10922_3837316_2/Non-Blocking-IO-Made-Possible-in-Java.htm So, how does server.accept() knows about current key we process?
  3. In http://www.developer.com/java/article.php/10922_3837316_2/Non-Blocking-IO-Made-Possible-in-Java.htm they even suggest to accept channel in new thread. I guess it is possible that the following situation may occur.

    key1 someClient1 acceptable
    key2 someClient2 not acceptable
    key3 someClient3 acceptable
    
    startThread1
    startThread3
    
    scheduler decides to give time to thread3 instead of thread1
    
    thread3 -> socket.accept() <- actually accepts client1
    thread1 -> socket.accept() <- actually accepts client3
    

So, could you please explain how selector works in pair with ServerSocketChannel and accept method? Because I don't understand in which order #accept accepts clients and how this order related to selectedKeys.

  1. Can I simply do the following:

    int availableClients = 0;
    while (it.hasNext()) {
        SelectionKey selKey = (SelectionKey) it.next();
    
        if (selKey.isAcceptable()) {
            ++availableClients;
        }
        it.remove();
    }
    for (int i = 0; i < availableClients; ++i) {
           SocketChannel sc = server.accept();
           doSomething(sc);
    }
    
like image 835
Vadim Kirilchuk Avatar asked Feb 10 '15 20:02

Vadim Kirilchuk


1 Answers

selKey.channel() return a ServerSocketChannel is it exactly the same channel we created in the beginning with ServerSocketChannel.open()?

Yes.

More important question: in most other tutorials selKey.channel(); step is skipped and they simply use SocketChannel client = server.accept(); For example here: http://www.onjava.com/pub/a/onjava/2002/09/04/nio.html?page=2 and here: http://www.developer.com/java/article.php/10922_3837316_2/Non-Blocking-IO-Made-Possible-in-Java.htm So, how does server.accept() knows about current key we process?

It doesn't. They're assuming that there is only one ServerSocketChannel. Your way is better: it's more general.

In http://www.developer.com/java/article.php/10922_3837316_2/Non-Blocking-IO-Made-Possible-in-Java.htm they even suggest to accept channel in new thread.

I have no idea why. It's a non-blocking call. It will return immediately. The suggestion is pointless. Ignore it. It's a very poor quality tutorial from six years ago, but there were better ones in existence thirteen years ago. Try the Oracle tutorial. The author of this one doesn't seem to understand the point of non-blocking mode at all. The suggestion to use a separate thread for every event is completely and utterly ludicrous. He also doesn't understand how to use OP_WRITE. He makes a false assertion about cancel(). I could go on. It's doubtful that he has ever executed this code: he certainly didn't investigate its behaviour in any way. How to write a non-scalable NIO server. Quite a feat.

I guess it is possible that the following situation may occur.

I don't even seen why you would be accepting in two threads at the same time, let alone how it could possibly matter to either thread which client it accepts. This is a difficulty invented where none exists.

So, could you please explain how selector works in pair with ServerSocketChannel and accept method? Because I don't understand in which order #accept accepts clients and how this order related to selectedKeys.

The accept() method returns the next socket in the backlog queue, and OP_ACCEPT fires whenever the backlog queue is non-empty. It's perfectly simple, no mystery. The order doesn't 'relate to selected keys' at all. The selected key is that of the ServerSocketChannel.

EDIT: It appears you have a major misunderstanding. Consider:

  1. You create and register a ServerSocketChannel for OP_ACCEPT.
  2. Two clients connect simultaneously.
  3. There is now exactly one SelectionKey in existence, and therefore exactly one in the selected-keys set: that of the ServerSocketChannel.
  4. You then process your isAcceptable() case on that key; accept one or both connections; register those channels for OP_READ.
  5. There are now three selection keys in existence, and none in the selected-key set, because you cleared it.
  6. Both the clients send some data.
  7. Now you have two selection keys ready to read in the selected-keys set.

OK?

Can I simply do the following:

Certainly, but why? There is no advantage to be had by making something complicated out of something simple. Do it the first way.

like image 147
user207421 Avatar answered Oct 28 '22 05:10

user207421