Nio uses threads to handle the writing problem of each read and write request

problem description

when writing the demo of java nio, I want to follow the React pattern so that every request that is ready is processed in a new thread. The specific code is shown below.

when a client disconnects after writing data, it will report an error:

java.nio.channels.ClosedChannelException

it is obvious that this way of writing is inappropriate or incorrect. See that the attach () method is used on the Internet

selectionKey.attach(new Acceptor(selector,selectionKey));

so:
question 1: what is the wrong writing method that caused the client to close the Times, and what is the cause of the error?
question 2: how do I use attach (), and why can this writing avoid turning off the client side to report an error?

related codes

use separate threads for select operations.

new Thread(() -> {
            try {
                while (selector.select() > 0) {
                    for (SelectionKey selectionKey : selector.selectedKeys()) {
                        try {
                            selector.selectedKeys().remove(selectionKey);
                            if (selectionKey.isAcceptable()) {
                                SocketChannel socketChannel = serverSocketChannel.accept();
                                socketChannel.configureBlocking(false);
                                socketChannel.register(selector, SelectionKey.OP_READ);
                            }
                            if (selectionKey.isReadable()) {
                                doRead(selectionKey);
                                
                            }
                        } catch (IOException e) {
                            selectionKey.cancel();
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();

when a readable event is encountered, it is processed in a new thread.

public void doRead(SelectionKey selectionKey) {
        new Thread(() -> {
            try {
                SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                String content = "";
                int c = 0;
                while ((c = socketChannel.read(byteBuffer)) > 0) {
                    byteBuffer.flip();
                    content += charset.decode(byteBuffer);
                }
                System.out.println(content);
                selectionKey.interestOps(SelectionKey.OP_READ);
                if (c == -1) {
                    selectionKey.cancel();
                    socketChannel.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
    }
Apr.02,2021

selector.selectedKeys().remove(selectionKey);

this is problematic and should be deleted with the iterator Iterator.

Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext())
            {
                SelectionKey sk = iterator.next();
                iterator.remove();
                
                .....
             }
             

you

  while ((c = socketChannel.read(byteBuffer)) > 0) {
  byteBuffer.flip();
  content += charset.decode(byteBuffer);
   }
      

byteBuffer should be clear (), otherwise it can only be read once, and the while loop is useless.
and the following:

selectionKey.interestOps(SelectionKey.OP_READ);
                if (c == -1) {
                    selectionKey.cancel();
                    socketChannel.close();
                }
       
        
  

Why use this function selectionKey.interestOps ()? Your reading event has been registered, there is no need to re-register.
as for the error caused by the disconnection of the client, I don't understand what you mean. Isn't your selectionKey.cancel (); socketChannel.close (); the connection code for the disconnection of the server?

the way you write it, you start a new thread as soon as it is readable, but maybe the next select () of the main thread will find it readable. Why? Because as long as you can't guarantee that your thread will read all the data before the mainthread select (), the next time select () will find that you have opened another thread, and it is very likely that you have several threads reading the incoming data together, and then one thread calls socketChannel.close (), to end the socket, so other threads reading with this thread must be abnormal.

if I use attach, I think it should be used this way. Of course, if I write it, I don't use attach to do it:

Acceptor acceptor = (Acceptor)selectionKey.attach();
if(acceptor == null)
selectionKey.attach(acceptor = new Acceptor(selectionKey));
else continue;
....

then before the end of the Acceptor thread, selectionKey.attach (null);
ensures that only one thread is used to read, of course, the above continue can not be written, just to make the presentation logic easier to understand.

Menu