【问题标题】:boost::asio::ip::tcp::socket is connected?boost::asio::ip::tcp::socket 已连接?
【发布时间】:2009-10-02 18:31:22
【问题描述】:

我想在执行读/写操作之前验证连接状态。

有没有办法制作 isConnect() 方法?

我看到this,但看起来“丑”。

我也测试了is_open() 函数,但它没有预期的行为。

【问题讨论】:

标签: c++ sockets boost-asio


【解决方案1】:

TCP 的目的是在严苛的网络面前保持稳健;尽管 TCP 提供了看似持久的端到端连接,但这一切都是谎言,每个数据包实际上只是一个唯一的、不可靠的数据报。

连接实际上只是在连接的每一端(源和目标端口和地址,以及本地套接字)跟踪的一些状态创建的虚拟管道。网络堆栈使用此状态来了解将每个传入数据包分配给哪个进程以及将哪个状态放入每个传出数据包的标头中。

由于网络的底层——本质上是无连接和不可靠的——性质,堆栈只会在远程端发送 FIN 数据包以关闭连接时报告断开连接,或者如果它没有收到 ACK 响应发送的数据包(在超时和几次重试之后)。

由于 asio 的异步特性,通知正常断开连接的最简单方法是有一个未完成的async_read,它将在连接关闭时立即返回error::eof。但仅此一点仍然可能导致其他问题,如连接半开和网络问题未被检测到。

解决意外连接中断的最有效方法是使用某种保持活动状态或 ping。这种通过连接传输数据的偶尔尝试可以方便地检测到意外断开的连接。

TCP协议实际上有一个内置的keep-alive mechanism,可以在asio中使用asio::tcp::socket::keep_alive进行配置。 TCP keep-alive 的好处是它对用户模式应用程序是透明的,只有对 keep-alive 感兴趣的对等方才需要配置它。缺点是您需要操作系统级别的访问/知识来配置超时参数,遗憾的是它们没有通过简单的套接字选项公开,并且通常具有非常大的默认超时值(Linux 上为 7200 秒)。

可能最常见的保持活动状态的方法是在应用程序层实现它,其中应用程序有一个特殊的 noop 或 ping 消息,除了在痒痒时响应之外什么都不做。此方法为您实施保活策略提供了最大的灵活性。

【讨论】:

  • keep-alive 方法存在一个问题:我无法访问应用程序将运行的服务器,因此我无法更改 keep-alive 超时(此处为 7200 秒)。但我喜欢这个响应,所以如果我再次需要它,我将启用 TCP keel-alive。谢谢
  • 我忘了说我做了什么,我只是尝试读/写,检查错误并根据这个错误做出决定。
  • 图像不再出现,您能解决这个问题吗?我想看插图。谢谢。
  • @joshperry 默认超时值是否必须在客户端和服务器端都进行修改?
【解决方案2】:

TCP 承诺监视丢弃的数据包——在适当的时候重试——给你一个可靠的连接,对于可靠的一些定义。当然,TCP 无法处理服务器崩溃、以太网电缆脱落或发生类似情况的情况。此外,知道您的 TCP 连接已启动并不一定意味着将通过 TCP 连接的协议已准备就绪(例如,您的 HTTP 网络服务器或您的 FTP 服务器可能处于某种损坏状态)。

如果您知道通过 TCP 发送的协议,那么该协议中可能有一种方法可以告诉您情况是否良好(对于 HTTP,它将是 HEAD request

【讨论】:

    【解决方案3】:

    如果您确定远程套接字没有发送任何内容(例如,因为您还没有向它发送请求),那么您可以将本地套接字设置为 非阻塞 模式并尝试从中读取一个或多个字节。

    鉴于服务器尚未发送任何内容,您将收到asio::error::would_block 或其他一些错误。如果是前者,则您的本地套接字尚未检测到断开连接。如果是后者,则您的套接字已关闭。

    这是一个示例代码:

    #include <iostream>
    #include <boost/asio.hpp>
    #include <boost/asio/spawn.hpp>
    #include <boost/asio/steady_timer.hpp>
    
    using namespace std;
    using namespace boost;
    using tcp = asio::ip::tcp;
    
    template<class Duration>
    void async_sleep(asio::io_service& ios, Duration d, asio::yield_context yield)
    {
      auto timer = asio::steady_timer(ios);
      timer.expires_from_now(d);
      timer.async_wait(yield);
    }
    
    int main()
    {
      asio::io_service ios;
      tcp::acceptor acceptor(ios, tcp::endpoint(tcp::v4(), 0));
    
      boost::asio::spawn(ios, [&](boost::asio::yield_context yield) {
        tcp::socket s(ios);
        acceptor.async_accept(s, yield);
        // Keep the socket from going out of scope for 5 seconds.
        async_sleep(ios, chrono::seconds(5), yield);
      });
    
      boost::asio::spawn(ios, [&](boost::asio::yield_context yield) {
        tcp::socket s(ios);
        s.async_connect(acceptor.local_endpoint(), yield);
    
        // This is essential to make the `read_some` function not block.
        s.non_blocking(true);
    
        while (true) {
          system::error_code ec;
          char c;
          // Unfortunately, this only works when the buffer has non
          // zero size (tested on Ubuntu 16.04).
          s.read_some(asio::mutable_buffer(&c, 1), ec);
          if (ec && ec != asio::error::would_block) break;
          cerr << "Socket is still connected" << endl;
          async_sleep(ios, chrono::seconds(1), yield);
        }
    
        cerr << "Socket is closed" << endl;
      });
    
      ios.run();
    }
    

    还有输出:

    Socket is still connected
    Socket is still connected
    Socket is still connected
    Socket is still connected
    Socket is still connected
    Socket is closed
    

    测试日期:

    Ubuntu:16.04
    内核:4.15.0-36-generic
    提升:1.67

    不过,我不知道这种行为是否取决于这些版本中的任何一个。

    【讨论】:

      【解决方案4】:

      您可以在套接字上发送一个虚拟字节,看看它是否会返回错误。

      【讨论】:

      • 但是,如果您已连接,另一端收到此虚拟字节时会发生什么情况。这不是一个好的解决方案。
      • 通常这类应用程序都有心跳周期性发送的概念。此外,在 windows (win32) 上没有办法可靠地检查连接,并且 boost::asio::ip::tcp::socket 将在其之上实现。 @jmucchiello - 如果您曾经在分布式系统上工作过,您就会知道始终通过心跳检查连接,这通常是一个字节。希望这对 OP 有所帮助。
      • 有一个问题,我不知道服务器端的行为。我找到的解决方案是在操作之前调用连接并检查错误情况。
      • 您需要知道服务器的协议(查看文档或询问供应商);尝试连接可能会非常昂贵。
      • Andrei:他的套接字可能是 HTTP/1.1 Keep Alive 连接的客户端。该协议是否有“发送一个字节”的心跳?我只使用分布式系统 20 多年,并不是所有的系统都有心跳。其中一些,如 HTTP/1.1,使用“错误重新连接”方法。您的回答说“发送一个虚拟字节”,而不包括在您的 cmets 中发现的任何警告。这不是正确的答案。您的评论说“您需要了解协议”。请将此作为您的实际答案,因为当前的答案有缺陷。
      猜你喜欢
      • 2018-06-08
      • 1970-01-01
      • 2012-05-25
      • 1970-01-01
      • 2017-04-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多