Я пытаюсь создать простой сервер с использованием NIO с выбираемыми каналами и перенести все «тяжелые» логи c за пределы основного NIO l oop в отдельный поток. Но я не могу зарегистрировать SelectionKey из другого потока. Извините за длинное чтение.
Сервер запущен как обычно:
ServerSocketChannel serverChannel;
Selector selector;
try {
serverChannel = ServerSocketChannel.open();
ServerSocket ss = serverChannel.socket();
InetSocketAddress address = new InetSocketAddress(port);
ss.bind(address);
serverChannel.configureBlocking(false);
selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
} catch (IOException ex) {
ex.printStackTrace();
return;
}
Затем идет основной l oop, где на этапе принятия (key.isAcceptable ()) я выполняю принять ( Я предпочел бы принять соединение в отдельном потоке, но кажется, что без принятия в основном NIO l oop я не получу объект SocketChannel):
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel sChann = server.accept();
, а затем я передаю текущий SocketChannel и SelectionKey ко 2-му потоку, чтобы сделать некоторые проверки и решить, должен ли канал быть закрыт, или я могу читать данные из канала. Если все проверки успешно пройдены, я пытаюсь зарегистрировать флаг OP_READ для этого ключа и получить следующие проблемы:
В руководстве Java написано, что SelectionKey
является постоянным для канала. Однако, когда во 2-м потоке я пытаюсь выполнить
key.interestOps(SelectionKey.OP_READ);
, я получил следующее исключение:
Exception in thread "Thread-0" java.lang.IllegalArgumentException
at java.base/sun.nio.ch.SelectionKeyImpl.interestOps(SelectionKeyImpl.java:98)
at ConnectionAcceptor.run(ConnectionAcceptor.java:55)
at java.base/java.lang.Thread.run(Thread.java:834)
Я читал об этом исключении в руководстве
IllegalArgumentException - Если бит в наборе не соответствует операции, которая поддерживается каналом этого ключа, то есть, если (ops & ~ channel (). ValidOps ())! = 0
и добавил несколько проверок, чтобы проверить, не в моем ли это дело. Проверки во 2-м потоке:
System.out.println("ConnectionAcceptor: valid options " + ci.sockChan.validOps());
System.out.println("ConnectionAcceptor: OP_ACCEPT " + SelectionKey.OP_ACCEPT);
System.out.println("ConnectionAcceptor: OP_READ " + SelectionKey.OP_READ);
System.out.println("ConnectionAcceptor: OP_WRITE " + SelectionKey.OP_WRITE);
результат:
ConnectionAcceptor: valid options 13
ConnectionAcceptor: OP_ACCEPT 16
ConnectionAcceptor: OP_READ 1
ConnectionAcceptor: OP_WRITE 4
, поэтому правило из руководства не нарушено и исключение IllegalArgumentException не следует создавать.
Здесь Я нашел другой способ установить требуемый флаг:
sockChan.keyFor(selector).interestOps(SelectionKey.OP_READ);
, но, используя его во втором потоке, я получаю
Exception in thread "Thread-0" java.lang.NullPointerException
at ConnectionAcceptor.run(ConnectionAcceptor.java:59)
at java.base/java.lang.Thread.run(Thread.java:834)
В результате он Похоже, что в то время как объекты канала и ключа были переданы во второй поток, основной NIO l oop выполнил некоторые итерации, и SelectionKey канала стал недействительным. Пожалуйста, помогите мне найти способ регистрации флага селектора канала из 2-го потока.