сейчас эта проблема прослушивает меня какое-то время.Java 8 неблокирующее чтение имеет состояние гонки?
В рабочем приложении, над которым я работаю, я использую SocketChannel в неблокирующем режиме для связи со встроенными устройствами. Теперь я получаю спорадически поврежденные данные. На некоторых ПК этого не происходит, теперь это происходит на моем. Но когда я слишком сильно меняю программу, проблема исчезает.
Сколько может иметь влияние. Время, аппаратные средства сетевого интерфейса, win7, Java-версия, брандмауэр компании, ...
считывание данных сводятся к этому коду:
byteBuffer.compact();
socketChannel.read(byteBuffer); // <<< problem here ?
byteBuffer.flip();
if(byteBuffer.hasRemaining()){
handleData(byteBuffer);
}
Это выполняется в том же потоке, как запись, когда селектор просыпается и задан параметр op OP_READ.
Этот код является единственным местом, где указано byteBuffer. socketChannel используется только из одного потока при записи.
Я измерил код, поэтому я могу распечатать содержимое последних нескольких вызовов read(), когда произошла ошибка. В то же время я анализирую сетевой трафик на Wireshark. Я добавил много утверждений, чтобы проверить целостность байтов.
В Wireshark полученный поток выглядит хорошо. Нет DUP-ACK или что-то еще подозрительное. Последние вызовы read() точно совпадают с данными в Wireshark.
В Wireshark я вижу много мелких кадров TCP, получающих с 90 байтами данных полезной нагрузки в промежутках, таких как 10 мс. Обычно поток Java считывает данные, а также все 10 мс, когда он только что прибыл.
Когда дело доходит до проблемы, поток Java представляет собой битную задержку, так как чтение происходит после 300 мс, а чтение возвращается с размером ~ 3000 байт, что является правдоподобным. Но данные повреждены.
Данные выглядят, если они были скопированы в буфер, и одновременно полученные данные перезаписали первые данные.
Теперь я не знаю, как действовать. Я не могу создать небольшой пример, поскольку это редко случается, и я не знаю точное условие, которое необходимо.
Может кто-нибудь дать подсказку?
Как я могу доказать, что это Java-библиотека или нет?
Какие условия могут быть важны для просмотра?
благодаря Frank
29-июня-2015:
Теперь я смог построить пример для размножения.
Существует одна программа Sender и Receiver.
Отправитель использует блокировку ввода-вывода, сначала ожидая подключения, а затем отправляя 90 байтовых блоков каждые 2 мс. Первый 4-байтовый счетчик работает, остальные не установлены. Отправитель использует setNoTcpDelay (true).
Приемник использует неблокирующий IO. Сначала он подключается к отправителю, затем он читает канал всякий раз, когда для него готов ключ выбора. Когда-нибудь, цикл чтения выполняет Thread.sleep (300).
Если они работают на одном ПК через петлю, это работает для меня все время. Если я поставлю Sender на другой компьютер, напрямую подключенный через локальную сеть, он вызывает ошибку. Проверка с Wireshark, трафик и отправленные данные выглядят хорошо.
Чтобы запустить, сначала запустите Отправитель на одном ПК, затем (после редактирования hostaddress) запустите приемник.
Пока он работает, он печатает строку примерно каждые 2 секунды. Если он терпит неудачу, он печатает информацию о последних 5 вызовах read().
Что я нашел, чтобы быть триггером:
- Отправитель настроил setNoTcpDelay (истина)
- Приемник имеет иногда Thread.sleep (300) перед выполнением чтения().
благодаря Frank
Хотя возможно, что на Java есть ошибка, это крайне маловероятно ... учитывая огромное количество других программистов на Java 8, которые (по-видимому) не испытывают такого рода проблемы. Скорее всего, это ошибка в коде. Что делать? Хорошо, если ваш код слишком сложно свести к MCVE, и слишком большой, чтобы показать нам, тогда лучше всего собрать коллегу, чтобы помочь вам. –
Если код выполняется в одном потоке, это вряд ли будет «условием гонки» в обычном смысле. (Я не знаю, помогает ли это ...) –
Из того, что вы упомянули, может возникнуть проблема - в том, как вы потребляете данные. Впрочем, это всего лишь предположение. По какой-то причине не объясняется тем, что вы упомянули, - когда поток java, который считывает данные, получает запланированное после 300 мс, вы получаете так много пакетов с 90 байтами назад. Если вы предполагали читать их в кусках в 90 байт, явным образом читаю их в 90 байтах. А затем потребляйте больше - когда их больше 90 байтов. Я не знаю конкретных API, но может быть, это должно указывать в каком-то направлении? – gabhijit