Адрес уже используется с помощью boost asio acceptor

Я написал сервер, который прослушивает входящие TCP-соединения и подключающиеся к нему клиенты. Когда я выключаю сервер и перезапускаю его на том же порту, я иногда получаю сообщение об ошибке EADDRINUSE при вызове bind (...) (код ошибки: 98 в Linux). Это происходит, даже если я устанавливаю возможность повторного использования сокета.

Ошибка возникает не все время, но кажется, что это происходит чаще, когда клиенты подключены к серверу и отправляют данные, когда он завершает работу. Я предполагаю, что проблема в том, что все еще есть незавершенные соединения, пока сервер выключен (связанный раздел: https://stackoverflow.com/questions/41602/how-to-forcible-close-a-socket-in-time-wait).

На стороне сервера я использую boost :: asio :: ip :: tcp :: acceptor. Я инициализирую его опцией reuse_address (см. http://beta.boost.org/doc/libs/1_38_0/doc/html/boost_asio/reference/basic_socket_acceptor.html). Вот фрагмент кода:

using boost::asio::ip::tcp;
acceptor acceptor::acceptor(io_service);
endpoint ep(ip::tcp::v4(), port);
acceptor.open(ep.protocol());
acceptor.set_option(acceptor::reuse_address(true));
acceptor.bind(ep);
acceptor.listen();

Акцептор закрывается:

acceptor.close();

Я также пробовал использовать acceptor.cancel () до этого, но он имел тот же эффект. Когда возникла эта ошибка, я не могу перезапустить сервер на том же порту в течение некоторого времени. Перезагрузка сети помогает, но не является постоянным решением.

Что мне не хватает?

Любая помощь будет принята с благодарностью! :)


person Alexander    schedule 16.11.2010    source источник
comment
ваш сервер разветвляет дочерние процессы?   -  person Sam Miller    schedule 16.11.2010
comment
кроме того, вы уверены, что сокет находится в состоянии TIME_WAIT? Вы можете захотеть получить результат netstat -ap, когда это произойдет.   -  person Sam Miller    schedule 16.11.2010
comment
Сэм, большое спасибо за ваш вклад! Это помогло мне найти решение моей проблемы. Оказалось, что я установил опцию повторного использования адреса для приемника, но не для всех других подключений. Еще раз большое спасибо за вашу помощь!   -  person Alexander    schedule 17.11.2010
comment
Я добавил свои комментарии в качестве ответа.   -  person Sam Miller    schedule 17.11.2010
comment
@Alexander Я знаю, что приду на вечеринку с опозданием на 11 лет, но не могли бы вы объяснить, что вы имели в виду, когда установили опцию повторного использования адреса для приемника, но не для всех других подключений? Что это за другие связи? Эти сокеты были перемещены из блока приемника для асинхронной обработки?   -  person user3224083    schedule 17.03.2021


Ответы (2)


Изначально это был комментарий к вопросу.


ваш сервер разветвляет дочерние процессы? Кроме того, вы уверены, что сокет находится в состоянии TIME_WAIT? Вы можете захотеть получить результат netstat -ap, когда это произойдет

person Sam Miller    schedule 17.11.2010

Когда решаешь эти проблемы «силой», кажется, ты бросаешь проблемы на голову?

Есть причина, по которой поведение по умолчанию требует от вас подождать, иначе сеть может, например, спутать ACK от предыдущего соединения с ACK для нового соединения.

Я бы не позволил включать это «решение» в сборку релизов в моей команде.

Помните, когда вероятность ошибки очень мала, тестирование чрезвычайно сложно!

person Hrissan    schedule 10.05.2011
comment
Спасибо за ваш ответ. Я разделил свой ответ из-за нехватки места. Сначала я резюмирую ситуацию: сервер работал и прослушивал фиксированный порт на предмет входящих подключений. Затем клиенты подключились к нему через этот порт. В какой-то момент я остановил сервер, что привело к тому, что все клиенты закрыли свое соединение с сервером. (Я почти уверен, что правильно закрыл соединения как на стороне сервера, так и на стороне клиента.) - person Alexander; 11.05.2011
comment
Затем я снова запустил сервер. Однако серверу не удалось начать прослушивание того же порта, потому что в клиентских соединениях не было возможности повторного использования, а порт все еще был зарезервирован. Теперь возникает вопрос, как я могу повторно использовать адрес, который уже помечен как используемый операционной системой, даже если все соединения были закрыты правильно, без использования опции повторного использования? - person Alexander; 11.05.2011
comment
Простое ожидание (которое может занять довольно много времени) не является приемлемым решением для сервера. Я считал (и до сих пор верю), что в данном случае уместна установка опции повторного использования. Однако меня очень интересует альтернативное решение, и я был бы рад, если бы вы могли предоставить или намекнуть на него. (Примечание: предложенное выше решение работает очень хорошо. Мы активно используем сервер и не сталкивались с какими-либо проблемами, связанными с подключениями. Конечно, это не гарантирует, что оно на 100% безошибочно.) - person Alexander; 11.05.2011
comment
FIN_WAIT тайм-аут настраивается; это обычная практика, чтобы уменьшить его со 120 секунд по умолчанию до 5 секунд. Это не решит проблему полностью - вам все равно придется обработать адрес, который уже используется (поскольку для этого есть другие веские причины), - но устраняет необходимость в SO_REUSEADDR. - person ulidtko; 27.12.2018