1
votes

I currently have a very weird problem. Sometimes my ::acceptor (using async_accept) doesn't accept a socket (there is no call of the accept function specified in async_accept) but the connector returns true on the connect function (both boost::asio:: classes). Sometimes all works but sometimes I don't get an acceptor socket.... I really don't know why anymore. I tested everything under Win10 64bit with different listen_ports.
My server structure is the following:
io_service:

_io_thread = std::make_unique<std::thread>([this]() {
            while (1)
            {
                try
                {
                    _io_service->run();
                    break;
                }
                catch (const boost::exception& e)
                {
                    spdlog::error("IO_SERVICE_EXCEPTION: ", boost::diagnostic_information(e));
                }
            }
        });

acceptor:

void Server::initialize(uint16_t listen_port)
    {
        // at this point the io_thread is running already

        auto endpoint = ip::tcp::endpoint(ip::tcp::v4(), listen_port);

        _acceptor = std::make_unique<boost::asio::ip::tcp::acceptor>(*_io_service, endpoint.protocol());
        _acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(false));
        _acceptor->bind(endpoint);
        _acceptor->listen();

        _listen_port = listen_port;

        __accept();
    }

__accept():

void Server::__accept()
    {
        // create socket for next connection
        _acceptor_socket = std::make_unique<ip::tcp::socket>(*_io_service);

        _acceptor->async_accept(*_acceptor_socket, [this](const boost::system::error_code& err)
            {
                spdlog::info("Accept new socket..."); // this sometimes doesn't get called :(

                if (err.failed())
                {
                    // acceptor closed
                    if (err == boost::asio::error::operation_aborted)
                    {
                        spdlog::info("network server stopped accepting sockets");
                        return;
                    }

                    // unknown error
                    spdlog::error("network server accepting socket failed {} message {} num {}", err.failed(), err.message(), err.value());

                    // accept next connection
                    __accept();
                    return;
                }

                // accept new socket
                _new_connections_mutex.lock();

                auto con = __create_new_connection();
                con->initialize(++_connection_id_counter, std::move(_acceptor_socket));
                con->set_handler(_input_handler);
                con->goto_phase(_initial_phase);
                spdlog::info("new connection from {} with id {}", con->get_host_name(), con->get_unique_id());

                _new_connections[con->get_unique_id()] = std::move(con);

                _new_connections_mutex.unlock();

                // wait for next connection
                __accept();
            }
        );
    }



My client connector is simple like that:

        auto socket = std::make_unique<boost::asio::ip::tcp::socket>(*_io_service);

        spdlog::info("try to connect to {}:{}", endpoint.address().to_string(), endpoint.port());
        try
        {
            socket->connect(endpoint);
        }
        catch (const boost::system::system_error & e)
        {
            spdlog::error("cannot connect to {}:{}", endpoint.address().to_string(), endpoint.port());
            return false;
        }

        // this succeeds everytime...
        [...]

So... shouldn't everytime when the connector socket succeeds to connect the acceptor socket be created as well? I hope somebody knows what's going wrong here :/

1
Wait, why do you put reuse_address to false? I am not entirely sure what it actually does (it is always a hidden mystery) but it might result it, say, being unable to reopen socket from the same address for a short while.ALX23z
Identifiers with leading __ are reserved in all scopes; Using them is technically UB stackoverflow.com/questions/228783/…sehe
There are several log statements in the code. Which of them do you see when you hit the problematic behaviour?sehe
If an exception occurs between mutex .lock() and .unlock() you will have soft or deadlock. It is rarely necessary to use these, instead use std::lock_guard, std::unique_lock or std::shared_lock which are exception safesehe
@sehe: Up to now I used __ for private and protected member functions, _ for member variables. What do you recommend? I read the glibc page and yeah __ is reserved for global C functions... the point with std::lock_guard is actually a good point. I will change that. The connector socket ("try to connect to ...") is successfully created (actually after that is another log that verifys that) but the log "Accept new socket..." sometimes appear and sometimes don't (if it don't I have to restart the server until it can accepts sockets...)Crispy

1 Answers

1
votes

Okay I found the answer.... the io_service::run function doesn't work as I expected. I thought it will just block forever if io_service::stop is not called. Seems like that is wrong. Because I break after one ::run call out of my while loop and then the thread is finished the io_service sometimes didn't run anymore when the connector socket connected. Have changed the io_service::run -> run_one() and removed the break. Will now add a loop-cancel variable so it's no infinite loop...
Thanks for your answers !