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);
}
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:
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.