Регистр Java SocketChannel () для нескольких кодов операций никогда не выбирается
Я построил систему на основе селектора в Java, которая может принимать несколько клиентов. Он имеет ServerSocketChannel, зарегистрированный под OP_ACCEPT, который принимает () входящее соединение и регистрирует результирующий SocketChannel с селектором снова. Вот этот кусочек:
ServerSocketChannel insock = ServerSocketChannel.open();
insock.configureBlocking(false);
insock.socket().bind(new InetSocketAddress(6789));
Selector sel = Selector.open();
SelectionKey joinchannel = insock.register(sel, SelectionKey.OP_ACCEPT);
System.out.println("Ready to accept incoming connections.");
while (true) {
int ready = sel.selectNow();
if (ready == 0)
continue;
Set<SelectionKey> selectedKeys = sel.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if(key.isAcceptable()){
SocketChannel newConnection = insock.accept();
System.out.println("New client "+newConnection+" connected.");
newConnection.configureBlocking(false);
newConnection.register(sel, SelectionKey.OP_READ).attach(new DGPlayer());
}
Если я регистрирую новый SocketChannel для OP_READ, это работает нормально. Проверка isReadable () завершается успешно, данные считываются. Вот этот кусочек:
else if(key.isReadable()){
ByteBuffer buf = ByteBuffer.allocate(1024);
int trans = ((SocketChannel)key.channel()).read(buf); buf.flip();
byte[] ba = new byte[buf.remaining()]; buf.get(ba);
String msg = new String(ba, 0, ba.length, Charset.forName("UTF-8"));
if(trans > 0){
DGPlayer client = (DGPlayer) key.attachment();
System.out.println(client.name+": "+msg.trim());
}
}
// else if(key.isWritable()){
// ByteBuffer buf = ByteBuffer.allocate(48);
// buf.clear();
// buf.put(((String)key.attachment()).getBytes());
// buf.flip();
//
// SocketChannel target = ((SocketChannel)key.channel());
// while(buf.hasRemaining()) {
// target.write(buf);
// }
//
// key.attach(null);
// }
Если я зарегистрирую SocketChannel для OP_READ|OP_WRITE, однако, ничего не происходит. Насколько я могу судить, селектор никогда не находит канал ни читаемым (), ни записываемым (). Единственное изменение-это регистрация канала для получения разрешения на запись.
Есть идеи, почему это может быть?
EDIT-для завершения, вот некоторые клиентские коды:
while (true) {
int readyChannels = sel.select();
if (readyChannels == 0)
continue;
Set<SelectionKey> selectedKeys = sel.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if (key.isReadable()) {
ByteBuffer buf = ByteBuffer.allocate(1024);
int trans = ((SocketChannel)key.channel()).read(buf); buf.flip();
byte[] ba = new byte[buf.remaining()]; buf.get(ba);
String msg = new String(ba, 0, ba.length, Charset.forName("UTF-8"));
if(msg.trim().length() != 0)
System.out.println("Response from server: "+msg.trim());
}
else if(key.isWritable() && messages.size() > 0){
String message = messages.remove();
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(message.getBytes(Charset.forName("UTF-8")));
buf.flip();
int written = outsock.write(buf);
while(buf.hasRemaining()){
outsock.write(buf);
}
System.out.println("Wrote '"+message+"' to server");
}
keyIterator.remove();
}
}
(клиент регистрирует сервер для OP_READ|OP_WRITE)
1 ответ:
Вообще это плохая идея, чтобы иметь канал, зарегистрированный для
OP_WRITE
, если у вас нет ничего, чтобы активно писать на него. Большинство каналов могут быть записаны в большую часть времени, поэтому каждый вызовSelector.select
будет возвращаться без блокировки и потреблять ресурсы. Лучше добавить флаг записи вSelectionKey.interestOps
Когда вы будете готовы написать что-то на канал, и снимите флаг, когда вы закончите.