Address reuse error when using fork () + excelp with boost :: asio on Linux

I have a program that listens on a TCP port for a specific line and launches the application by calling execlp . I am doing fork() to start the child process before this execlp call. After that, the parent start process will again start listening on the same port. I close the socket in the child process.

I wrote a wrapper on top of boost::asio::tcp_socket , where before setting the socket I set the addr_reuse parameter to true .

Now my problem is Linux. I get an address reuse error after several application launches. In my program, he constantly tries to accept connections (or, rather, tries to schedule a reception until boost::asio::io_service ) until it is bound and then accepts successful execution. Therefore, I get an error in this loop.

Oddly enough, if I close (or kill) the running executable, this error will stop appearing, which means bind . I am sure that in the running application the same port is not used anywhere. I use asynchronous socket operations. Any idea why I am getting this error?

Here's how I accept on a socket: (I also call reset in boost::asio::tcp_socket(_tcpSocket) generic pointer before starting a new accept.)

 boost::asio::ip::tcp::endpoint endPoint(boost::asio::ip::tcp::v4(), port); _acceptor.reset ( new boost::asio::ip::tcp::acceptor( *_ioService.get() ) ); _acceptor->open( endPoint.protocol() ); _acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); boost::system::error_code ec; _acceptor->bind(endPoint, ec); if ( ec.value() != boost::system::errc::success ) { ec.clear(); _acceptor->close(ec); close(); return false; } ec.clear(); _acceptor->listen(boost::asio::socket_base::max_connections, ec); if ( ec.value() != boost::system::errc::success ) { return false; } _acceptor->async_accept(*_tcpSocket, boost::bind(&TCPSocket::_handleAsyncAccept, this, boost::asio::placeholders::error) ); 

This is how I deploy:

 pid_t pid = fork(); switch (pid) { case 0: { /// close all sockets for child process. as it might cause addr reuse error in parent process _asyncNO->closeAll(); std::string binary = "<binaryName>"; std::string path = "<binaryPath>"; if ( execlp( path.c_str(), binary.c_str(), controllerIP.c_str(), (char *)0 ) == -1 ) { LOG_ERROR("System call failed !!") } } break; default: } 

I removed logging for simplicity.

+4
source share
1 answer

As @TannerSansbury noted in the comments, this is most likely because Boost.Asio needs to notify fork() :

A newer version of the forking documentation with Boost.Asio.

Relevant section reproduced here:

Boost.Asio supports programs that use the fork() system call. If the program calls io_service.notify_fork() at appropriate times, Boost.Asio will recreate any internal file descriptors (for example, the self-pipe trick descriptor used to wake up the reactor). Notification is usually done as follows:

 io_service_.notify_fork(boost::asio::io_service::fork_prepare); if (fork() == 0) { io_service_.notify_fork(boost::asio::io_service::fork_child); // ... } else { io_service_.notify_fork(boost::asio::io_service::fork_parent); // ... } 

Custom services can also be fork-aware by overriding the io_service::service::fork_service() virtual function.

Please note that any file descriptors accessible through the open Boost.Asio API (for example, the descriptors underlying basic_socket<> , posix::stream_descriptor , etc.) are not changed during the fork. The responsibility of the program is to manage them as needed.

0
source

Source: https://habr.com/ru/post/1493962/


All Articles