Подтвердить что ты не робот

Как я могу повторно подключить boost:: socket после отключения?

В моем клиентском приложении используется boost::asio::ip::tcp::socket для подключения к удаленному серверу. Если приложение теряет соединение с этим сервером (например, из-за сбоя или завершения работы сервера), я хотел бы, чтобы он пытался повторно подключиться через равные промежутки времени, пока он не удастся.

Что мне нужно делать на стороне клиента, чтобы очистить отключение, убрать, а затем повторно пытаться переподключиться?

В настоящее время интересные фрагменты моего кода выглядят примерно так.

I connect следующим образом:

bool MyClient::myconnect()
{
    bool isConnected = false;

    // Attempt connection
    socket.connect(server_endpoint, errorcode);

    if (errorcode)
    {
        cerr << "Connection failed: " << errorcode.message() << endl;
        mydisconnect();
    }
    else
    {
        isConnected = true;

        // Connected so setup async read for an incoming message.
        startReadMessage();

        // And start the io_service_thread
        io_service_thread = new boost::thread(
            boost::bind(&MyClient::runIOService, this, boost::ref(io_service)));
    }
    return (isConnected)
}

Где метод runIOServer() справедлив:

void MyClient::runIOService(boost::asio::io_service& io_service)
{
    size_t executedCount = io_service.run();
    cout << "io_service: " << executedCount << " handlers executed." << endl;
    io_service.reset();
}

И если какой-либо из обработчиков чтения async возвращает ошибку, они просто называют этот метод disconnect:

void MyClient::mydisconnect(void)
{
    boost::system::error_code errorcode;

    if (socket.is_open())
    {
        // Boost documentation recommends calling shutdown first
        // for "graceful" closing of socket.
        socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, errorcode);
        if (errorcode)
        {
            cerr << "socket.shutdown error: " << errorcode.message() << endl;
        }

        socket.close(errorcode);
        if (errorcode)
        {
            cerr << "socket.close error: " << errorcode.message() << endl;
        }    

        // Notify the observer we have disconnected
        myObserver->disconnected();            
    }

.., который пытается изящно отключить и затем уведомить наблюдателя, который начнет вызывать connect() с интервалом в пять секунд, пока он не будет повторно подключен.

Есть ли что-нибудь еще, что мне нужно сделать?

В настоящее время это работает. Если я убью сервер, к которому он подключен, я получаю ожидаемую ошибку "End of file" у моих обработчиков чтения и mydisconnect() вызывается без каких-либо проблем.

Но когда он пытается снова подключиться и не работает, я вижу отчет "socket.shutdown error: Invalid argument". Это только потому, что я пытаюсь отключить сокет, который не читает/пишет в ожидании? Или это нечто большее?

4b9b3361

Ответ 1

Вам нужно создать новый boost::asio::ip::tcp::socket при каждом повторном подключении. Самый простой способ сделать это, вероятно, просто выделить сокет в куче, используя boost::shared_ptr (вы, возможно, также можете уйти с scoped_ptr, если ваш сокет полностью инкапсулирован в класс). Например:.

bool MyClient::myconnect()
{
    bool isConnected = false;

    // Attempt connection
    // socket is of type boost::shared_ptr<boost::asio::ip::tcp::socket>
    socket.reset(new boost::asio::ip::tcp::socket(...));
    socket->connect(server_endpoint, errorcode);
    // ...
}

Затем, когда вызывается mydisconnect, вы можете освободить сокет:

void MyClient::mydisconnect(void)
{
    // ...
    // deallocate socket.  will close any open descriptors
    socket.reset();
}

Ошибка, которую вы видите, вероятно, является результатом того, что ОС очистит дескриптор файла после того, как вы вызвали close. Когда вы вызываете close, а затем пытаетесь выполнить connect в том же сокете, вероятно, вы пытаетесь подключить неверный дескриптор файла. На этом этапе вы увидите сообщение об ошибке, начинающееся с "Connection failed:..." на основе вашей логики, но затем вы вызываете mydisconnect, который, вероятно, затем пытается вызвать shutdown в недопустимом дескрипторе файла. Порочный цикл!

Ответ 2

Для ясности здесь используется заключительный подход, который я использовал (но это основано на ответе bjlaub, поэтому, пожалуйста, дайте ему какие-либо варианты):

Я объявил элемент socket как scoped_ptr:

boost::scoped_ptr<boost::asio::ip::tcp::socket> socket;

Затем я изменил свой метод connect следующим образом:

bool MyClient::myconnect()
{
    bool isConnected = false;

    // Create new socket (old one is destroyed automatically)
    socket.reset(new boost::asio::ip::tcp::socket(io_service));

    // Attempt connection
    socket->connect(server_endpoint, errorcode);

    if (errorcode)
    {
        cerr << "Connection failed: " << errorcode.message() << endl;
        socket->close();
    }
    else
    {
        isConnected = true;

        // Connected so setup async read for an incoming message.
        startReadMessage();

        // And start the io_service_thread
        io_service_thread = new boost::thread(
            boost::bind(&MyClient::runIOService, this, boost::ref(io_service)));
    }
    return (isConnected)
}

Ответ 3

Я сделал что-то подобное, используя Boost.Asio в прошлом. Я использую асинхронные методы, поэтому повторное подключение обычно позволяет моему существующему объекту ip:: tcp:: socket выйти из области видимости, а затем создать новый для вызовов async_connect. Если async_connect терпит неудачу, я использую таймер, чтобы немного поспать, а затем повторить попытку.

Ответ 4

Я пробовал использовать метод close() и метод shutdown, и для меня это просто сложно. Close() может выдать ошибку, которую вам нужно поймать, и является грубым способом делать то, что вы хотите:) и shutdown(), кажется, лучше, но на многопоточном программном обеспечении, я считаю, что это может быть суетливо. Так что лучший способ, как сказал Сэм, позволить ему выйти из сферы действия. Если сокет является членом класса, вы можете: 1) перепроектировать так, чтобы класс использовал объект "соединение", чтобы обернуть сокет и позволить ему выйти из области видимости или 2) обернуть его в интеллектуальный указатель и reset умный указатель. Если вы используете boost, в том числе shared_ptr, это дешево и работает как шарм. Никогда не было проблемы с очисткой сокета, делающей это с помощью shared_ptr. Только мой опыт.