Зачем подключаться () давать EADDRNOTAVAIL? - PullRequest
13 голосов
/ 08 октября 2010

У меня в приложении возникла ошибка, которая, похоже, не воспроизводима. У меня не удалось установить сокет TCP, и приложение попыталось восстановить его. Во втором вызове connect () при попытке переподключения я получил сообщение об ошибке с ошибкой errno == EADDRNOTAVAIL, о которой говорит man-страница connect (): «Указанный адрес недоступен с локальной машины».

Глядя на вызов connect (), второй аргумент представляется адресом, на который ссылается ошибка, но, насколько я понимаю, этот аргумент является адресом сокета TCP удаленного хоста, поэтому я запутался о man-странице, ссылающейся на локальную машину. Неужели этот адрес удаленного хоста сокета TCP недоступен с моей локальной машины? Если так, то почему? Он должен был успешно вызвать connect () в первый раз, прежде чем не удалось установить соединение, и он попытался восстановить соединение и получил эту ошибку. Аргументы для connect () были одинаковыми оба раза.

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

Ответы [ 5 ]

19 голосов
/ 08 октября 2010

Проверьте эту ссылку

http://www.toptip.ca/2010/02/linux-eaddrnotavail-address-not.html

РЕДАКТИРОВАТЬ : Да, я хотел добавить больше, но пришлось сократить его там из-за чрезвычайной ситуации

Вы закрыли розетку перед попыткой переподключения?Закрытие скажет системе, что пара сокетов (ip / port) теперь свободна.

Вот также дополнительные пункты, посмотрите на:

  • Если локальный порт уже подключен к заданномуудаленный IP и порт (т. е. уже есть идентичная пара сокетов), вы получите эту ошибку (см. ссылку на ошибку ниже).
  • Привязка адреса сокета, который не является локальным, приведет к этой ошибке.если IP-адреса компьютера - 127.0.0.1 и 1.2.3.4, и вы пытаетесь привязаться к 1.2.3.5, вы получите эту ошибку.
  • EADDRNOTAVAIL: указанный адрес недоступен наудаленный компьютер или поле адреса структуры имени - все нули.

Ссылка с ошибкой, аналогичной вашей (ответ близок к низу)

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4294599

Похоже, что ваш сокет в основном застрял в одном из внутренних состояний TCP и что добавление задержки для переподключения может решить вашу проблему, как они, похоже, сделали в этом отчете об ошибке.

3 голосов
/ 27 мая 2011

Это также может произойти, если указан неверный порт, например 0.

2 голосов
/ 07 февраля 2015

Если вы не хотите изменять количество доступных временных портов (как предложено Дэвидом), или вам нужно больше соединений, чем теоретический максимум, есть два других способа уменьшить количество используемых портов. Однако они в различной степени являются нарушением стандарта TCP, поэтому их следует использовать с осторожностью.

Первый - включить SO_LINGER с тайм-аутом в ноль секунд, заставляя стек TCP отправлять RST-пакет и сбрасывать состояние соединения. Однако есть одна тонкость: вы должны вызвать shutdown в дескрипторе файла сокета перед close, чтобы у вас была возможность отправить пакет FIN перед пакетом RST. Таким образом, код будет выглядеть примерно так:

shutdown(fd, SHUT_RDWR);
struct linger linger;
linger.l_onoff = 1;
linger.l_linger = 0;
// todo: test for error
setsockopt(fd, SOL_SOCKET, SO_LINGER,
           (char *) &linger, sizeof(linger));
close(fd);

Сервер должен видеть преждевременный сброс соединения, только если пакет FIN переупорядочен с пакетом RST.

См. Опция TCP SO_LINGER (ноль) - когда это требуется для получения более подробной информации. (Экспериментально, кажется, не имеет значения, где вы установили setsockopt.)

Второй - использовать SO_REUSEADDR и явный bind (даже если вы клиент), что позволит Linux повторно использовать временные порты при запуске, прежде чем они будут ждать. Обратите внимание, что необходимо использовать bind с INADDR_ANY и портом 0, в противном случае SO_REUSEADDR не соблюдается. Ваш код будет выглядеть примерно так:

int opts = 1;
// todo: test for error
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
         (char *) &opts, sizeof(int));

struct sockaddr_in listen_addr;
listen_addr.sin_family = AF_INET;
listen_addr.sin_port = 0;
listen_addr.sin_addr.s_addr = INADDR_ANY;
// todo: test for error
bind(fd, (struct sockaddr *) &listen_addr, sizeof(listen_addr));

// todo: test for addr
// saddr is the struct sockaddr_in you're connecting to
connect(fd, (struct sockaddr *) &saddr, sizeof(saddr));

Эта опция менее хороша, потому что вы все равно будете насыщать внутренние структуры данных ядра для соединений TCP согласно netstat -an | grep -e tcp -e udp | wc -l. Однако вы не начнете повторно использовать порты, пока это не произойдет.

1 голос
/ 09 мая 2019

Я получил эту проблему.Я получил разрешение, включив временную метку tcp.

Основная причина:

  1. После закрытия соединения Соединения некоторое время переходят в состояние TIME_WAIT.

  2. Во время этого состояния, если какие-либо новые соединения приходят с тем же IP и PORT, если SO_REUSEADDR не указан при создании сокета, то socket bind () завершится с ошибкой EADDRINUSE.

  3. Но даже если после предоставления SO_REUSEADDR sockect connect () может завершиться ошибкой EADDRNOTAVAIL, если временная метка tcp не активирована с обеих сторон.

Решение: Пожалуйста, включите tcp timestamp на стороне клиента и на сервере.

echo 1> / proc / sys / net / ipv4 / tcp_timestamps

Причина включения tcp_timestamp:

Когдамы включаем tcp_tw_reuse, сокеты в состоянии TIME_WAIT могут использоваться до истечения срока их действия, и ядро ​​будет пытаться убедиться, что нет никаких конфликтов в отношении порядковых номеров TCP.Если мы включим tcp_timestamps, это обеспечит невозможность таких коллизий.Однако нам нужно, чтобы временные метки TCP были активированы на обоих концах.См. Определение tcp_twsk_unique для подробной информации.

ссылка: https://serverfault.com/questions/342741/what-are-the-ramifications-of-setting-tcp-tw-recycle-reuse-to-1

0 голосов
/ 12 июля 2014

Еще одна вещь, которую нужно проверить, это то, что интерфейс работает.Недавно я запутался в этом, когда использовал сетевые пространства имен, так как создание нового сетевого пространства имен создает совершенно независимый петлевой интерфейс, но не вызывает его (по крайней мере, в версиях Debian wheezy).Это ускользнуло от меня на некоторое время, так как обычно никто не думает о петлеобразовании, когда он вообще не работает.

...