Селектор на Android-сокетах ведет себя странно - PullRequest
3 голосов
/ 17 сентября 2011

Предварительные условия: эмулятор Android 2.2.

У меня есть прекрасно работающий код Java, который также отлично скомпилирован для Android.Но наступает странная часть.В частности, кажется, что java.nio.Selector не работает вообще.

Первая проблема возникает при подключении.Следующий код работает на Java, но не работает на Android (подробности см. Ниже).

socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress(remoteAddr, getRemotePort()));

Selector selector = Selector.open();
socketChannel.register(selector, socketChannel.validOps());

// Wait for an event
int selRes = selector.select(timeout); 
if (selRes == 1)
{
    SelectionKey selKey = (SelectionKey)selector.selectedKeys().iterator().next();
    if (selKey.isValid() && selKey.isConnectable()) {
        // Get channel with connection request
        boolean success = socketChannel.finishConnect();
        if (!success) {
            selKey.cancel();
        }
    }
}                   

Я передаю тайм-аут 30000 (мс, что составляет 30 секунд), но select немедленно возвращает значение selres, равное 0 (в Desktop Java это 1).Переключение сокета в режим блокировки работает нормально (так что адреса, порты и другие вещи в порядке).

Хорошо, я оставил соединение для блокировки (пока).Но теперь мой Accept перестал работать - Selector не сообщает о входящих соединениях.Опять же, избавление от Selector с помощью блокирующего сокета работает.

Итак, вопрос в том, работает ли вообще Selector в Android, или код следует переписать, чтобы вообще избежать Selector и java.nio?

Ответы [ 2 ]

6 голосов
/ 17 сентября 2011

Следующий код работает на Java

Этот код имеет серьезные проблемы на любой платформе.

  1. Вы не очищаете selectedKeySet.Обычно это делается путем итерации по нему и вызова Iterator.remove(), но в этом случае вы должны вызывать selectedKeys().clear(), поскольку вы этого не делаете, хотя на самом деле это так: см. Ниже.

  2. Вы не должны регистрироваться с InterestOps = validOps ().Вы должны зарегистрироваться OP_CONNECT до тех пор, пока finishConnect() не вернет true, а затем либо OP_READ, либо OP_WRITE, в зависимости от того, что вы хотите сделать дальше.

  3. Если соединение не удается, finishConnect() выдает IOException, на котором вы должны закрыть канал.Вы этого не делаете.

  4. Если соединение еще не завершено, finishConnect() возвращает значение false, и в этом случае вам следует просто продолжить выбор.Нет смысла отменять клавишу в этот момент.

  5. Если selres > 1, то вы вообще не обрабатываете ни одну из выбранных клавиш.Тест должен быть if (selRes > 0), и он на самом деле не нужен, так как итерация по selectedKeySet просто повторяет ноль раз;однако selRes == 0 указывает, что select() истекло время ожидания, что может быть полезно, если вы хотите учесть время ожидания.

3 голосов
/ 17 сентября 2011

У проблемы есть странное решение, найденное в кажущемся не связанным отчете об ошибках в трекере ошибок Android. Эмулятор Android не поддерживает IPv6, и хотя я не претендую на запрос IPv6, похоже, что по умолчанию Selector пытается работать со стеком IPv6.

После добавления следующих строк мой код начинает работать правильно:

java.lang.System.setProperty("java.net.preferIPv4Stack", "true");
java.lang.System.setProperty("java.net.preferIPv6Addresses", "false");
...