【问题标题】:Detect closed TCP connection during write with boost::asio immediately立即使用 boost::asio 在写入期间检测关闭的 TCP 连接
【发布时间】:2017-10-07 21:16:46
【问题描述】:

我有一个带有多个客户端/会话的 TCP 服务器。每个会话都有自己的线程用于从客户端接收数据,但只有一个线程(“writeThread”)来响应所有客户端。

现在有一个问题,如果客户端在“writeThread”正在写入此套接字期间关闭连接,则写入操作需要几秒钟才能注意到连接已远程关闭。有时它根本不会注意到,就在我手动为已安装的 sighandler 发送 signal 时,应用程序会检测到它并中断写操作。

时间在Logger::trace("start write");Logger::trace("remote term, closed socket "); 之间测量

尽管这可能不是最好的设计,但是否有可能立即检测到关闭的连接,还是我真的必须重新设计?

bool myWrite(UINT8 *pu8_buffer, UINT32 u32_size)
{
    bool b_success = false;
    try
    {
        Logger::trace("start write");
        b_success = (u32_size == boost::asio::write(_x_socket, boost::asio::buffer(pu8_buffer, u32_size)))
    }
    catch (boost::system::system_error &er)
    {
        if (er.code() == boost::asio::error::eof || 
            er.code() == boost::asio::error::connection_reset)
        {
            boost::system::error_code x_er;
            _x_socket.close(x_er);
            if (!x_er)
            {
                Logger::trace("remote term, closed socket ");
            }
            else
            {
                Logger::err("remote term, closed socket failed");
            }
        }
    }
    catch(std::exception &ex)
    {
        Logger::err("write exception\n\t",ex.what());
    }
    catch(...)
    {
        Logger::err("write unknown exception",(uint32_t)this);
    }
    return b_success;
}

【问题讨论】:

  • TCP/IP 中有一个选项可以减少超时时间(一端注意到另一端不再存在所需的时间)。然而,由于 TCP/IP 是一种可靠且健壮的连接协议(并使用此时间重试尚未确认的数据包),因此减少超时可能会引入其他问题(例如虚假断开连接)。如果“另一端”正确关闭套接字,您将立即收到通知,但如果另一端停止响应(或存在其他连接问题),则会发生此超时。
  • @RichardCritten 你确定这是因为“太大”的 tcp 窗口大小吗?
  • 不,不是没有数据包跟踪 - 它只是一个低挂选项。获取 wireshark 或类似的工具,查看网络上的数据包,看看是否有干净的关闭或只是超时。
  • @RichardCritten 你是对的,只要线路上有一个 RST-ACK,写入功能就会立即中止。所以我的问题/情况的一部分在另一边——谢谢。你能告诉我更多关于“低挂”选项的信息吗?可以从应用程序设置此超时还是 TCP 内部?也许你有一些有用的链接。
  • 你没有指定平台,所以这里是微软的答案(在其他平台上类似)你可能想要SO_SNDTIMEO 选项:msdn.microsoft.com/en-us/library/windows/desktop/…

标签: c++ multithreading sockets tcp boost-asio


【解决方案1】:

如果您使用异步写入操作,则可以在同一个线程上多路复用写入,而不会阻塞另一个。您甚至可以对读取执行相同的操作。

【讨论】:

  • 好主意,但使用async_write 我有一个设计问题,因为我的调用线程总是分配缓冲区并在写入后释放它。当async_write 立即返回时,缓冲区可能会在复制到内核缓冲区之前被释放。如果我将缓冲区复制到mySocket 中的本地发送缓冲区,该缓冲区将在下一次mySocket::myWrite() 调用中被覆盖。你知道如何解决这个问题吗?
  • 在写回调中释放缓冲区
  • 没错,但是有没有可能通过缓冲区指针(用于回调)来知道要释放哪个缓冲区?
  • std::function 是你的朋友。您可以使用 std::bind 或 lamda 在函数对象中包含指针。
  • 完全正确,这应该可以解决。我明天试试。非常感谢。
【解决方案2】:

为了结束这个问题,我将总结“Richard Critten”的 cmets。

问题是连接到我的服务器的客户端没有正常断开连接。如果有适当的断开连接,写入功能将立即中断。为了避免写入操作的长期或无限阻塞,可以在报告错误之前配置写入操作可能需要多长时间的超时。可以使用SO_SNDTIMEO 套接字选项设置此超时。 http://man7.org/linux/man-pages/man7/socket.7.html

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-14
    相关资源
    最近更新 更多