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

Boost:: asio чистое разъединение

Иногда boost:: asio, кажется, отключается, прежде чем я захочу, то есть до того, как сервер правильно обработает отключить. Я не уверен, как это возможно, потому что клиент, кажется, считает, что он полностью отправил сообщение, но когда сервер испускает ошибку, он даже не читает заголовок сообщения... Во время тестирования это происходит только в 1 раз в 5 раз, сервер получает сообщение об отключении клиента и отключает клиента.

Ошибка: "существующее соединение было принудительно закрыто удаленным хостом"

Клиент отключается:

void disconnect()
{
    boost::system::error_code error;
    //just creates a simple buffer with a shutdown header
    boost::uint8_t *packet = createPacket(PC_SHUTDOWN,0);
    //sends it
    if(!sendBlocking(socket,packet,&error))
    {
        //didnt get here in my tests, so its not that the write failed...
        logWrite(LOG_ERROR,"server",
            std::string("Error sending shutdown message.\n")
            + boost::system::system_error(error).what());
    }

    //actaully disconnect
    socket.close();
    ioService.stop();
}
bool sendBlocking(boost::asio::ip::tcp::socket &socket,
    boost::uint8_t *data, boost::system::error_code* error)
{
    //get the length section from the message
    boost::uint16_t len = *(boost::uint16_t*)(data - 3);
    //send it
    asio::write(socket, asio::buffer(data-3,len+3),
        asio::transfer_all(), *error);
    deletePacket(data);
    return !(*error);
}

Сервер:

void Client::clientShutdown()
{
    //not getting here in problem cases
    disconnect();
}
void Client::packetHandler(boost::uint8_t type, boost::uint8_t *data,
    boost::uint16_t len, const boost::system::error_code& error)
{
    if(error)
    {
        //error handled here
        delete[] data;
        std::stringstream ss;
        ss << "Error recieving packet.\n";
        ss << logInfo() << "\n";
        ss << "Error: " << boost::system::system_error(error).what();
        logWrite(LOG_ERROR,"Client",ss.str());

        disconnect();
    }
    else
    {
        //call handlers based on type, most will then call startRead when
        //done to get the next packet. Note however, that clientShutdown
        //does not
        ...
    }
}



void startRead(boost::asio::ip::tcp::socket &socket, PacketHandler handler)
{
    boost::uint8_t *header = new boost::uint8_t[3];
    boost::asio::async_read(socket,boost::asio::buffer(header,3),
        boost::bind(&handleReadHeader,&socket,handler,header, 
        boost::asio::placeholders::bytes_transferred,boost::asio::placeholders::error));
}
void handleReadHeader(boost::asio::ip::tcp::socket *socket, PacketHandler handler,
    boost::uint8_t *header, size_t len, const boost::system::error_code& error)
{
    if(error)
    {
        //error "thrown" here, len always = 0 in problem cases...
        delete[] header;
        handler(0,0,0,error);
    }
    else
    {
        assert(len == 3);
        boost::uint16_t payLoadLen  = *((boost::uint16_t*)(header + 0));
        boost::uint8_t  type        = *((boost::uint8_t*) (header + 2));
        delete[] header;
        boost::uint8_t *payLoad = new boost::uint8_t[payLoadLen];

        boost::asio::async_read(*socket,boost::asio::buffer(payLoad,payLoadLen),
            boost::bind(&handleReadBody,socket,handler,
            type,payLoad,payLoadLen,
            boost::asio::placeholders::bytes_transferred,boost::asio::placeholders::error));
    }
}
void handleReadBody(ip::tcp::socket *socket, PacketHandler handler,
    boost::uint8_t type, boost::uint8_t *payLoad, boost::uint16_t len,
    size_t readLen, const boost::system::error_code& error)
{
    if(error)
    {
        delete[] payLoad;
        handler(0,0,0,error);
    }
    else
    {
        assert(len == readLen);
        handler(type,payLoad,len,error);
        //delete[] payLoad;
    }
}
4b9b3361

Ответ 1

Я думаю, что вы должны, вероятно, позвонить socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec) там до вызова socket.close().

Документация boost:: asio для basic_stream_socket:: закрыть:

Для портативного поведения в отношении изящного закрытия подключенного сокета вызовите shutdown() перед закрытием сокета.

Это должно гарантировать, что любые ожидающие действия в сокете должным образом отменены и все буферы будут очищены до вызова socket.close.

Ответ 2

Я попытался сделать это с помощью метода close() и метода shutdown()

socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec)

Метод останова - лучший из двух. Тем не менее, я считаю, что использование деструктора ASIO-сокета - это чистый способ сделать это, поскольку ASIO заботится обо всем этом для вас. Таким образом, ваша цель - просто отключить сокет. Теперь вы можете сделать это легко с помощью shared_ptr и сброса shared_ptr в новый сокет или null. это вызовет деструктор сокета ASIO, и жизнь будет хорошей.

Ответ 3

Возможно, это то, что происходит:

  • Клиент отправляет пакет разъединения
  • Клиент отключает сокет.
  • Обработчик чтения сервера вызывается, но есть ошибка, связанная с пакетом shutdown, потому что сокет уже закрыт.

Я вижу в обработчиках чтения, если есть ошибка, вы никогда не проверяете, есть ли ваш пакет выключения. Может быть это. В основном, я говорю, может быть, ваш клиент иногда может отправлять как закрытие, так и пакет завершения, прежде чем сервер будет иметь возможность обрабатывать их отдельно.

Ответ 4

Используйте async_write() и поместите socket.close() внутри обработчика записи. Это позволит убедиться, что пакет обрабатывается boost asio и не игнорируется в середине обработки (из-за вызовов close()).

Ответ 5

У меня очень похожая проблема. Я считаю, что это связано с подключением к переработке Windows. Есть ли знакомые?

  • Вы получаете эту ошибку сразу после запуска программы, но не после установления соединения?
  • Ошибка не возникает, если вы ждете более 4 минут до перезапуска приложения?

В спецификациях tcp указывается, что по умолчанию для окончательного подтверждения, когда tcp-соединение закрыто, он должен ждать четыре минуты. Вы можете видеть эти соединения в состоянии FIN_WAIT, используя netstat. ОС Windows обнаруживает, когда вы пытаетесь подключиться к той же самой системе, и берет эти частично закрытые соединения и перерабатывает их. Второй вызов программы получает "закрытое" соединение, оставшееся после первого запуска. Он получает следующее подтверждение, а затем действительно закрывается.