Подтвердить что ты не робот

Как избежать исключения NoRouteToHostException?

Раскрытие: код, над которым я работаю, - это курсовая работа в университете.

Справочная информация. Задача, которую я пытаюсь завершить, - сообщить о влиянии различных методов потоковой обработки. Для этого я написал несколько классов, которые отвечают на запрос от клиента с использованием Java Sockets. Идея состоит в том, чтобы нагрузить сервер запросами и сообщить о том, как справляются с этим различные стратегии потоков. Каждый клиент будет делать 100 запросов, и на каждой итерации мы увеличиваем количество клиентов на 50, пока что-то не сломается.

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

Caused by: java.net.NoRouteToHostException: Cannot assign requested address
    at java.net.PlainSocketImpl.socketConnect(Native Method)
    at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:333)

Это происходит в нескольких сценариях, в том числе, когда клиент и сервер работают на локальном хосте. Соединения могут быть успешно выполнены некоторое время, и вскоре после попытки подключения 150 клиентов, которые выбрасывают исключение.

Моя первая мысль заключалась в том, что это ограничение Linux для дескрипторов открытых файлов (1024), но я так не думаю. Я также проверил, что все соединения между сокетами закрыты должным образом (т.е. В правильном блоке finally).

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

Кто-нибудь сталкивался с этим раньше? Как я могу избежать исключения NoRouteToHostException?


EDIT (дополнительные вопросы выделены курсивом)

До сих пор есть хорошие ответы, которые указывают либо на Ephemeral Port Range, либо на RFC 2780. Оба из них предполагают, что у меня слишком много соединений. Для обоих он показывает, что количество соединений, которые необходимо сделать для достижения этого предела, предполагает, что в какой-то момент я не закрываю соединения.

После отладки как клиента, так и сервера, оба наблюдали, как ударил вызов метода myJava-Net-SocketInstance.close(). Это предполагает, что соединения закрываются (по крайней мере, в не исключительном случае). Это правильное предложение?

Кроме того, существует ли уровень ОС, необходимый для возобновления доступа к портам? Было бы возможно запустить программу отдельно для каждого 50+ клиентов, если для выполнения следующей попытки просто потребуется короткий период (или оптимистично, запустив команду).


EDIT v2.0

Получив хорошие ответы, я изменил свой код, чтобы использовать метод setReuseAddress (true) с каждым соединением сокета, сделанным на клиенте. Это не имело желаемого эффекта, и я все еще ограничен 250-300 клиентами. После завершения программы выполнение команды netstat -a показывает, что в статусе TIME_WAIT имеется много соединений сокетов.

Мое предположение заключалось в том, что если сокет находился в состоянии TIME-WAIT и был установлен с опцией SO-REUSEADDR, любые новые сокеты, пытающиеся использовать этот порт, могли бы - однако, я все еще получаю NoRouteToHostException.

Это правильно? Есть ли что-нибудь еще, что можно сделать для решения этой проблемы?

4b9b3361

Ответ 1

Вы пробовали настройку:

echo "1" >/proc/sys/net/ipv4/tcp_tw_reuse

и/или

echo "1" >/proc/sys/net/ipv4/tcp_tw_recycle

Эти настройки могут заставить Linux повторно использовать сокеты TIME_WAIT. К сожалению, я не могу найти окончательную документацию.

Ответ 2

Это может помочь:

Эфемерный диапазон портов

Еще одно важное эфемерный диапазон портов заключается в том, что он ограничивает максимальное количество соединений из одна машина для конкретной службы на удаленный машина! Протокол TCP/IP использует подключение 4-кортежа к различать соединения, поэтому, если эфемерный диапазон портов составляет всего 4000 портов, что означает, что только 4000 уникальных подключений от клиентской машине на удаленную один раз.

Так что, возможно, у вас закончились доступные порты. Чтобы получить количество доступных портов, см.

$ cat /proc/sys/net/ipv4/ip_local_port_range 
32768   61000

Выход из моей системы Ubuntu, где у меня было бы 28,232 портов для клиентских подключений. Следовательно, ваш тест завершится неудачно, как только у вас будет 280 + клиентов.

Ответ 3

Невозможно назначить запрошенный адрес - это строка ошибки для ошибки EADDRNOTAVAIL.

Я подозреваю, что у вас закончились исходные порты. В динамическом диапазоне доступно 16 383 сокета, доступных для использования в качестве исходного порта (см. RFC 2780). 150 клиентов * 100 соединений = 15 000 портов - поэтому вы, вероятно, нажмете этот предел.

Ответ 4

Если у вас закончились исходные порты, но на самом деле они не поддерживают много открытых подключений, установите опцию SO_REUSEADDR. Это позволит вам повторно использовать локальные порты, которые все еще находятся в состоянии TIME_WAIT.

Ответ 5

Если вы закрываете 500 соединений в секунду, у вас не будет сокетов. Если вы подключаетесь к тем же местоположениям (веб-серверам), которые используют keepalive, вы можете реализовать пулы подключений, поэтому вы не закрываете и не открываете сокеты.

Это также спасет процессор.

Использование tcp_tw_recycle и tcp_tw_reuse может привести к тому, что пакеты поступят из предыдущего соединения, поэтому есть необходимость ждать 1 минуты для очистки пакетов.

Ответ 6

Для любых других пользователей Java, которые спотыкаются по этому вопросу, я бы рекомендовал использовать пул соединений, чтобы соединения были правильно использованы.