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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1511129/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 20:13:38  来源:igfitidea点击:

boost::asio::ip::tcp::socket is connected?

c++socketsboost-asio

提问by coelhudo

I want to verify the connection status before performing read/write operations.

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

Is there a way to make an isConnect() method?

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

I saw this, but it seems "ugly".

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

I have tested is_open()function as well, but it doesn't have the expected behavior.

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

回答by joshperry

TCP is meant to be robust in the face of a harsh network; even though TCP provides what looks like a persistent end-to-end connection, it's all just a lie, each packet is really just a unique, unreliable datagram.

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

The connections are really just virtual conduits created with a little state tracked at each end of the connection (Source and destination ports and addresses, and local socket). The network stack uses this state to know which process to give each incoming packet to and what state to put in the header of each outgoing packet.

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

Virtual TCP Conduit

虚拟 TCP 管道

Because of the underlying — inherently connectionless and unreliable — nature of the network, the stack will only report a severed connection when the remote end sends a FIN packet to close the connection, or if it doesn't receive an ACK response to a sent packet (after a timeout and a couple retries).

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

Because of the asynchronous nature of asio, the easiest way to be notified of a graceful disconnection is to have an outstanding async_readwhich will return error::eofimmediately when the connection is closed. But this alone still leaves the possibility of other issues like half-open connections and network issues going undetected.

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

The most effectively way to work around unexpected connection interruption is to use some sort of keep-alive or ping. This occasional attempt to transfer data over the connection will allow expedient detection of an unintentionally severed connection.

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

The TCP protocol actually has a built-in keep-alive mechanismwhich can be configured in asio using asio::tcp::socket::keep_alive. The nice thing about TCP keep-alive is that it's transparent to the user-mode application, and only the peers interested in keep-alive need configure it. The downside is that you need OS level access/knowledge to configure the timeout parameters, they're unfortunately not exposed via a simple socket option and usually have default timeout values that are quite large (7200 seconds on Linux).

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

Probably the most common method of keep-alive is to implement it at the application layer, where the application has a special noop or ping message and does nothing but respond when tickled. This method gives you the most flexibility in implementing a keep-alive strategy.

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

回答by Max Lybbert

TCP promises to watch for dropped packets -- retrying as appropriate -- to give you a reliable connection, for some definition of reliable. Of course TCP can't handle cases where the server crashes, or your Ethernet cable falls out or something similar occurs. Additionally, knowing that your TCP connection is up doesn't necessarily mean that a protocol that will go over the TCP connection is ready (eg., your HTTP webserver or your FTP server may be in some broken state).

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

If you know the protocol being sent over TCP then there is probably a way in that protocol to tell you if things are in good shape (for HTTP it would be a HEAD request)

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

回答by Peter Jankuliak

If you are sure that the remote socket has not sent anything (e.g. because you haven't sent a request to it yet), then you can set your local socket to a non blockingmode and try to read one or more bytes from it.

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

Given that the server hasn't sent anything, you'll either get a asio::error::would_blockor some other error. If former, your local socket has not yet detected a disconnection. If latter, your socket has been closed.

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

Here is an example code:

这是一个示例代码:

#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();
}

And the output:

和输出:

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

Tested on:

测试:

Ubuntu: 16.04
Kernel: 4.15.0-36-generic
Boost: 1.67

Ubuntu:16.04
内核:4.15.0-36-generic
Boost:1.67

Though, I don't know whether or not this behavior depends on any of those versions.

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

回答by vehomzzz

you can send a dummy byte on a socket and see if it will return an error.

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