首页 文章

boost :: asio干净地断开连接

提问于
浏览
24

有时boost :: asio似乎在我想要之前断开连接,即在服务器正确处理断开连接之前 . 我不确定这是怎么可能的,因为客户端似乎认为它完全发送了消息,但是当服务器发出错误时它甚至没有读取消息头...在测试期间,这种情况可能只发生在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;
    }
}

5 回答

  • 10

    我想你应该在调用 socket.close() 之前调用 socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec) .

    boost::asio documentation for basic_stream_socket::close州:

    对于关于正常关闭连接套接字的可移植行为,请在关闭套接字之前调用shutdown() .

    这应确保正确取消套接字上的任何挂起操作,并在调用socket.close之前刷新任何缓冲区 .

  • 5

    我试图用close()方法和shutdown()方法做到这一点

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

    关机方法是两者中最好的 . 但是,我发现使用ASIO套接字的析构函数是干净的方法,因为ASIO会为您完成所有操作 . 所以你的目标是让套接字超出范围 . 现在,您可以使用shared_ptr轻松完成此操作,并将shared_ptr重置为新的套接字或null . 这将调用ASIO套接字的析构函数,生活是美好的 .

  • 4

    也许这就是发生的事情:

    • 客户端发送断开连接数据包

    • 客户端关闭套接字

    • 调用服务器读取处理程序,但是由于套接字已关闭,因此关闭数据包存在错误 .

    我在你的读处理程序中看到,如果有错误,你永远不会检查你的关闭数据包是否存在 . 也许是 . 基本上我所说的可能是你的客户端有时能够在服务器有机会单独处理它们之前发送关闭和关闭数据包 .

  • 21

    使用async_write()并将socket.close()放在write handler中 . 这将确保数据包由boost asio处理,而不是在处理过程中忽略(因为close()调用) .

  • 3

    我有一个非常相似的问题 . 我相信它与Windows回收连接有关 . 以下是熟悉的吗?

    • 您在启动程序时立即收到此错误,但在 Build 连接后却没有?

    • 如果在重新启动应用程序之前等待超过4分钟,则不会发生错误?

    tcp规范指定默认情况下,当tcp连接关闭时,它应该等待四分钟以进行最终确认 . 您可以使用netstat在FIN_WAIT状态中查看这些连接 . 当您尝试连接到完全相同的系统时,Windows操作系统会检测到这些部分关闭的连接并对其进行回收 . 第二次调用程序会在第一次运行时留下“关闭”连接 . 它获得了下一个确认,然后真正关闭 .

相关问题