Как я могу проверить, завершен ли SocketChannel.read() для неблокирующего канала?

Я использую функцию для чтения байтов из неблокирующего SocketChannel (сокет из accept()) и из блокирующего SocketChannel (на стороне клиента). Я реализую сервер, используя селектор для обработки нескольких клиентов, и я использую петлевой адрес, чтобы использовать только свой ноутбук. я написал это

while((r = socketChannel.read(ackBuf)) != -1) {
        System.out.println(name3d+" r: "+r);
    }

и я ожидал, что когда будет достигнут конец контента в канале, read() вернет -1, но это не то, что будет успешным. read(), в неблокирующей конфигурации возвращает 0 также, если в данный момент ничего не готово для чтения, но это будет скоро (если я хорошо понимаю), поэтому, если я изменю код на

while((r = socketChannel.read(ackBuf)) > 0) {
        System.out.println(name3d+" r: "+r);
    }

Не буду ничего читать и если что-то будет готово через мгновение. Как я могу отличить, получил ли я 0, потому что не готов или потому что он закончился? В следующем фрагменте я могу проверить чтение во второй раз после сна, но я уверен, что это не надежный способ сделать то, что я хочу.

int times = 0;
while((r = socketChannel.read(ackBuf)) != -1 && times<2) {
    if (r == 0)
        try {
            Thread.sleep(500);
            times++;
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    System.out.println(name3d+" r: "+r);
}

person Falkon    schedule 28.02.2020    source источник


Ответы (2)


Неблокирующий SocketChannel используется немного по-другому.

  1. Сначала вы ждете, пока клавиша выбора сообщит вам, что есть данные, и
  2. затем вы читаете эти данные с канала.

См. этот черновик кода:

  Selector selector = Selector.open();
  SocketChannel sc = SocketChannel.open();
  sc.configureBlocking(false);
  sc.connect(addr);
  sc.register(selector, SelectionKey.OP_READ);
  while (true) {
    // select() can block!
    if (selector.select() == 0) {
      continue;
    }
    Iterator iterator = selector.selectedKeys().iterator();
    while (iterator.hasNext()) {
     SelectionKey key = (SelectionKey) iterator.next();
     iterator.remove();
     if (key.isReadable()) {
       SocketChannel sc = (SocketChannel) key.channel();
       ByteBuffer bb = ByteBuffer.allocate(1024);
       sc.read(bb);
       System.out.println("Message received!");
     }
}
person s.fuhrm    schedule 28.02.2020
comment
Так что я никогда не получу 0, если выбран ключ socketChannel! Это все объясняет. Спасибо :) - person Falkon; 28.02.2020
comment
Обиженно говорить не обязательно. Но у вас есть хороший шанс получить больше 0 в большинстве случаев. :-) - person s.fuhrm; 29.02.2020

"Если я получил 0, потому что не готов или потому что он закончился?" Вы имеете в виду сообщение или тотальность коммуникации?

Для сообщения вы должны использовать протокол связи (например, json или http) для связи, я думаю, вы должны получить SocketException... Вы бы сделали это, если бы использовали блокировку, а человек на другом конце закрыл соединение... ( Я писал многим людям на SO о том, что SocketException — ваш друг)

--- редактировать ---

Просматривая документацию для канала, похоже, что вы должны получить какое-то исключение IOException (SocketException является подклассом IOException), если/когда канал закрыт

person ControlAltDel    schedule 28.02.2020
comment
Я говорил о сообщении. Я использую свой собственный протокол, в котором после того, как клиент установил связь TCP с сервером, клиент отправляет несколько байтов через сокет-канал, причем первый байт используется для идентификации конкретного запроса, чтобы сервер понимал, какой тип запроса запрашивает клиент. аналогичным образом сервер отправляет байты клиенту - person Falkon; 28.02.2020