Проблема тайм-аута Java NIO TCP

Я использую один SocketChannel в 2 потоках, один поток для отправки данных и другой для получения данных.

SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress(ip,port));
socketChannel.configureBlocking(false);

Поток 1: использует вышеуказанный канал сокета для записи данных

Тема 2: использует тот же сокетканал для чтения данных

Я не использую никаких селекторов с socketchannel, так как мне нужно, чтобы запись и чтение были асинхронными (используя 2 разных потока)

ПРОБЛЕМА: при потере соединения операции socketchannel.write() и socketchannel.read() не вызывают никаких ошибок. Он просто блокирует операцию.

Мне нужно обнаружить потерю соединения.

Я попытался использовать метод сердцебиения в потоке 2, но поскольку операция чтения просто блокируется, этот метод не сработал. Есть ли другой способ обнаружить потерю соединения без использования сердцебиения в новом потоке?

Можно ли выдать ошибку при записи/чтении, если есть потеря соединения?

Заранее спасибо.

РЕДАКТИРОВАТЬ:

Тема 1:

public void run() {
  socketChannel = SendAndReceivePacketUtil.createConnection(ip, port);
  socketChannel.configureBlocking(false);

  RecTask task = new RecTask(socketChannel);
  Thread recThread = new Thread(task);
  recThread.start();

  while(true)
  {
     byte[] data= getDataFromQueue(ip);
     if(data!= null) {
         //print(new String(data));
         sendPacket(data, socketChannel);
     }
   }
}

Тема 2: (RecTask)

public void run() {
  while(true) {
    byte[] data = receivePacket(socketChannel);
    //print(new String(data));
  }
}

Оба потока 1 и 2 имеют блоки try-catch-finally. окончательно закрывает socketchannel.

отправить пакет:

int dataSent = 0;
while (dataSent < data.length) {
    long n = socketChannel.write(buf);
        if (n < 0) {
            throw new Exception();
        }
        dataSent += (int) n;
 }

получить пакет:

int dataRec = 0;
byte[] data = new byte[length];
ByteBuffer buffer = ByteBuffer.wrap(data);

while (dataRec < length) {
    long n = socketChannel.read(buffer);
    if (n < 0) {
        throw new Exception();
    }
    dataRec += (int) n;
}       
return data;

Я постоянно отправляю и получаю данные. Но как только соединение теряется, ничего не печатается и код просто зависает. Это прямое приложение для Android Wi-Fi. Для сценария потери соединения я просто отключаю модуль Wi-Fi.


person armor--    schedule 01.05.2015    source источник


Ответы (2)


Я не использую никаких селекторов с socketchannel, так как мне нужно, чтобы запись и чтение были асинхронными (используя 2 разных потока)

Это не причина избегать Selector. На самом деле довольно сложно написать правильный неблокирующий код NIO без Selector.

ПРОБЛЕМА: при потере соединения операции socketchannel.write() и socketchannel.read() не вызывают никаких ошибок. Он просто блокирует операцию.

Нет, это не так. Вы находитесь в неблокирующем режиме. Он либо возвращает положительное целое число, либо ноль, либо выдает исключение. Что он?

Я попытался использовать метод сердцебиения в потоке 2, но поскольку операция чтения просто блокируется, этот метод не сработал.

Операция чтения не блокируется в неблокирующем режиме.

Есть ли другой способ обнаружить потерю соединения без использования сердцебиения в новом потоке?

Единственный надежный способ обнаружить потерю соединения в TCP — записать в соединение. В конце концов это выдаст IOException: connection reset. Но это не произойдет в первый раз после потери соединения из-за буферизации, повторных попыток и т. д.

Можно ли выдать ошибку при записи/чтении, если есть потеря соединения?

Вот что происходит.

С этим вопросом что-то серьезно не так. Либо опубликованный вами код не является реальным кодом, либо он ведет себя не так, как вы описали. Вам нужно опубликовать больше, например. ваш код чтения и записи.

person user207421    schedule 01.05.2015
comment
Я обновил вопрос с требуемым кодом. Хотя я нахожусь в неблокирующем режиме, я все еще не могу ничего печатать при потере соединения. Также код не входит в блок catch/finally, поэтому я предполагаю, что код ожидает операции чтения/записи. - person armor--; 01.05.2015
comment
@Pulkit Очевидно, что и ваши чтения, и ваши записи возвращают ноль, что вызывает бесконечные циклы. Вот почему вам нужно select(). Написание неблокирующих циклов без select() - это просто плохая техника и пустая трата времени процессора. - person user207421; 01.05.2015
comment
Можно ли сделать чтение и запись асинхронными с помощью select? Кроме того, если я изменю socketChannel.configureBloacking() на true, проблема должна быть решена, верно? - person armor--; 01.05.2015
comment
Правильный. В этом случае я бы использовал режим блокировки, поскольку очевидно, что потокам нечего делать, кроме чтения или записи, иначе вы бы не писали эти циклы. - person user207421; 01.05.2015
comment
Но в случае блокирующего режима запись и чтение также будут зависеть друг от друга. Как будто данные записываются в socketchannel, в то же время я не могу прочитать данные из socketchannel. Не так ли? - person armor--; 01.05.2015
comment
Стоит ли использовать NIO для асинхронной передачи файлов по сравнению со старомодным IO? Не могли бы вы рассказать мне, как использовать select() в этом сценарии. Спасибо :) - person armor--; 01.05.2015
comment
@Pulkit Нет. Вы можете одновременно читать и писать как в блокирующем, так и в неблокирующем режиме. Я действительно не понимаю, почему вы вообще должны использовать здесь NIIO, не говоря уже о неблокирующем режиме. Я бы сначала заставил его работать с java.net.Socket, а затем посмотрел, есть ли у вас проблема. - person user207421; 01.05.2015

Вы можете найти включение опции TCP-KEEP в сокете. При бездействующем соединении отправляется keep-alive сообщений, и ожидается подтверждение ACK для тех, кто находится на уровне TCP.

Если TCP-KEEP не работает, ваша следующая операция чтения/записи приведет к ошибке (ECONNRESET), которую можно использовать как признак потери соединения.

person Prabhu    schedule 01.05.2015
comment
Можем ли мы не устанавливать параметры сокета с меньшими значениями времени ожидания для поддержки активности? - person Prabhu; 01.05.2015
comment
Насколько я знаю, это зависит от ОС. Вам придется изменить его на уровне ОС - person armor--; 01.05.2015
comment
@Prahbu На некоторых платформах да, но не через Java-код. На других платформах вы должны установить его на уровне конфигурации операционной системы, что требует прав администратора. - person user207421; 01.05.2015
comment
@EJP. Ах! Я понимаю. Не знал об ограничении Java. Спасибо. - person Prabhu; 01.05.2015