зачем мне получать EADDRINUSE не из bind(), а из listen()?

В приложении C++ для Linux я вызываю socket(), bind() и listen(), чтобы создать серверный сокет. Обычно, если приложение запускается дважды (с одним и тем же портом сервера), во втором процессе bind() завершится ошибкой EADDRINUSE. Однако теперь у меня есть случай, когда bind(), по-видимому, завершился успешно, но последующий вызов listen() вызвал ошибку EADDRINUSE...

Это, вероятно, редкое состояние гонки, но мне все же было бы интересно, в каких случаях может случиться, что второй bind() преуспевает, а второй listen() - нет. Кто-нибудь знает больше о таком случае?

Это на 32-битной версии RHEL 5.3.


person oliver    schedule 15.07.2011    source источник


Ответы (2)


Не уверен насчет Linux, но в Windows, если при вызове bind() указан подстановочный IP-адрес (INADDR_ANY и т. д.), базовая привязка может быть отложена до вызова listen() или connect(), так как ОС имеет больше шансов решить, какой именно лучше всего использовать сетевой интерфейс. bind() не сообщит об ошибке в этой ситуации.

person Remy Lebeau    schedule 16.07.2011
comment
Спасибо, кажется, это объяснение. По-видимому, правила таковы: listen() фактически зарезервирует порт в ядре и вызовет EADDRINUSE, если другой процесс вызвал listen() для этого порта; bind() не резервирует порт, но возбуждает EADDRINUSE, если другой процесс вызвал listen() для порта - person oliver; 18.07.2011

setsockopt(.... SOL_SOCKET, SO_REUSEADDR, ...) должен решить вашу проблему.

См. setsockopt(2) и сокет(7)

(что касается того, почему второй bind на самом деле преуспевает, понятия не имею... на самом деле это тоже должно потерпеть неудачу)

person Damon    schedule 15.07.2011
comment
@Ben Voigt: Конечно, хотя я думаю, что не только listen должен потерпеть неудачу, но уже bind, прежде чем вы доберетесь до listen. Хотя теория Реми Лебо об отложенном связывании, вероятно, является причиной, в любом случае она имеет смысл. - person Damon; 16.07.2011
comment
Рад, что мы согласны. Но тогда SO_REUSEADDR не исправляет проблему, а скрывает ее. - person Ben Voigt; 16.07.2011
comment
Я в чем-то нет, в чем-то да. Поскольку проблема в том, что две программы не могут одновременно использовать один и тот же порт, решения на самом деле нет. Давая SO_REUSEADDR, вы, по сути, сообщаете операционной системе, что если кто-то все еще использует этот порт, я обещаю, что все в порядке... никакая другая программа действительно не использует этот порт. Это чем-то похоже на приведение типов в C/C++, когда вы в основном говорите компилятору, что все в порядке, я обещаю, что знаю, что делаю. - person Damon; 16.07.2011