繁体   English   中英

boost::asio::ip::tcp::socket 已连接?

[英]boost::asio::ip::tcp::socket is connected?

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

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

我看到了这个,但它似乎“丑陋”。

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

TCP 旨在应对恶劣的网络; 尽管 TCP 提供了看似持久的端到端连接,但这一切都只是谎言,每个数据包实际上只是一个独特的、不可靠的数据报。

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

虚拟 TCP 管道

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

由于 asio 的异步特性,收到正常断开连接通知的最简单方法是使用未完成的async_read ,它会在连接关闭时立即返回error::eof 但仅此一项仍然存在其他问题(如半开连接和网络问题)未被发现的可能性。

解决意外连接中断的最有效方法是使用某种 keep-alive 或 ping。 这种偶尔尝试通过连接传输数据将允许方便地检测意外断开的连接。

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

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

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

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

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

鉴于服务器尚未发送任何内容,您将收到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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM