why SelectionKey.attachment() is null?

50 Views Asked by At

Here's the main part of server code:

Selector selector = Selector.open();
ServerSocketChannel serverSocket = ServerSocketChannel.open();
serverSocket.bind(new InetSocketAddress(InetAddress.getLocalHost(), port));
serverSocket.configureBlocking(false);
serverSocket.register(selector, SelectionKey.OP_ACCEPT);

while (true) {
    selector.select();
    Set<SelectionKey> keys = selector.selectedKeys();
    for (var iter = keys.iterator(); iter.hasNext();) {
       SelectionKey key = iter.next(); iter.remove();
          if (key.isValid()) {
              if (key.isAcceptable()) {
                  register(key);
              }
              else if (key.isReadable()) {
                  readBytes(key);
              }
              else if (key.isWritable()) {
                  sendResponse(key);
              }
          }
    }

}

private void register(SelectionKey key) throws IOException {
    var ssc = (ServerSocketChannel) key.channel();
    var client = ssc.accept();
    client.configureBlocking(false);
    client.register(key.selector(), SelectionKey.OP_READ);
    key.attach(new ClientData());
}
private void readBytes(SelectionKey key) throws IOException {
    SocketChannel client = (SocketChannel) key.channel();
    var data = (ClientData) key.attachment();
    client.read(data.buffer);
    client.register(key.selector(), SelectionKey.OP_WRITE);
}

ClientData:

public class ClientData {
    public final ByteBuffer buffer = ByteBuffer.allocate(1024);
}

I except from this that the server will read all the client’s bytes and put it in its attachment. But when readBytes() is called, an error occurs:

Exception in thread "main" java.lang.NullPointerException: Cannot read field "buffer" because "data" is null

UPD

private void register(SelectionKey key) throws IOException {
        var ssc = (ServerSocketChannel) key.channel();
        var client = ssc.accept();
        client.configureBlocking(false);
        SelectionKey clientKey = client.register(key.selector(), SelectionKey.OP_READ);
        clientKey.attach(new ClientData());
    }
private void readBytes(SelectionKey key) throws IOException {
        SocketChannel client = (SocketChannel) key.channel();
        SelectionKey clientKey = client.register(key.selector(), SelectionKey.OP_WRITE);
        var data = (ClientData) clientKey.attachment();
        client.read(data.buffer);
    }
1

There are 1 best solutions below

3
Gladitor On

The issue you're encountering stems from the register method. When you register the client channel with the selector for OP_READ operation, you attach an instance of ClientData to the SelectionKey that is returned by the register method. However, it seems like the register method is attaching ClientData to the wrong key.

The key passed to the register method is the server's SelectionKey (used for accepting connections), not the client's SelectionKey (used for read/write operations). When you call client.register(key.selector(), SelectionKey.OP_READ);, it actually returns a new SelectionKey associated with the client, to which you should attach the ClientData.

Here's a corrected version of your register method:

private void register(SelectionKey key) throws IOException {
    var ssc = (ServerSocketChannel) key.channel();
    var client = ssc.accept();
    client.configureBlocking(false);
    // Corrected: Attach ClientData to the client's key, not the server's key
    SelectionKey clientKey = client.register(key.selector(), SelectionKey.OP_READ);
    clientKey.attach(new ClientData());
}

This modification should ensure that each client's SelectionKey has its own ClientData attached. Now, when readBytes method is called, it should be able to retrieve the ClientData without throwing a NullPointerException.