c++ - 如何在 boost asio 中设置阻塞套接字的超时时间?

标签 c++ sockets boost boost-asio

有没有办法取消挂起的操作(不断开连接)或为 boost 库函数设置超时?

即我想在 boost asio 中设置阻塞套接字的超时时间?

socket.read_some(boost::asio::buffer(pData, maxSize), error_);

示例:我想从套接字中读取一些内容,但是如果 10 秒过去了,我想抛出一个错误。

最佳答案

TL;DR

socket.set_option(boost::asio::detail::socket_option::integer<SOL_SOCKET, SO_RCVTIMEO>{ 200 });

完整答案 这个问题多年来一直被反复问。到目前为止我看到的答案很差。我将在此问题的第一次出现中添加此信息。

如果作者只是为所有同步和异步 io 函数添加一个可选参数超时,每个尝试使用 ASIO 来简化他们的网络代码的人都会非常高兴。不幸的是,这不太可能发生(在我看来,只是出于意识形态的原因,毕竟 ASIO 中的 AS 是有原因的)。

所以这些是迄今为止可用的给这只可怜的猫剥皮的方法,它们都不是特别开胃的。假设我们需要 200 毫秒的超时时间。

1) 好(坏)旧的套接字 API:

const int timeout = 200;
::setsockopt(socket.native_handle(), SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout, sizeof timeout);//SO_SNDTIMEO for send ops

请注意这些特点: - 用于超时的 const int - 在 Windows 上,所需的类型实际上是 DWORD,但幸运的是当前的编译器集具有相同的类型,因此 const int 在 Win 和 Posix 世界中都可以使用。 - (const char*) 表示值。在 Windows 上 const char* 是必需的,Posix 需要 const void*,在 C++ 中 const char* 将默默地转换为 const void* 而反之则不然。

优点:可以工作并且可能会一直工作,因为套接字 API 是旧的和稳定的。很简单。快速地。 缺点:从技术上讲,setsockopt 和宏可能需要适当的头文件(在 Win 上不同,甚至在不同的 UNIX 风格上),但 ASIO 的当前实现无论如何都会污染全局命名空间。需要一个用于超时的变量。不是类型安全的。在 Windows 上,要求套接字处于重叠模式才能工作(当前的 ASIO 实现幸运地使用了这种模式,但它仍然是一个实现细节)。丑!

2) 自定义 ASIO 套接字选项:

typedef boost::asio::detail::socket_option::integer<SOL_SOCKET, SO_RCVTIMEO> rcv_timeout_option; //somewhere in your headers to be used everywhere you need it
//...
socket.set_option(rcv_timeout_option{ 200 });

优点:足够简单。快速地。漂亮(使用 typedef)。 缺点:取决于 ASIO 实现细节,这可能会改变(但 OTOH 最终一切都会改变,而且这样的细节不太可能改变,然后是受标准化的公共(public) API)。但万一发生这种情况,您必须根据 https://www.boost.org/doc/libs/1_68_0/doc/html/boost_asio/reference/SettableSocketOption.html 编写一个类。 (这当然是一个主要的 PITA,这要归功于 ASIO 的这部分明显的过度设计)或者更好的是恢复到 1。

3) 使用 C++ 异步/ future 工具。

#include <future>
#include <chrono>
//...
auto status = std::async(std::launch::async, [&] (){ /*your stream ops*/ })
    .wait_for(std::chrono::milliseconds{ 200 });
switch (status)
    {
    case std::future_status::deferred:
    //... should never happen with std::launch::async
        break;
    case std::future_status::ready:
    //...
        break;
    case std::future_status::timeout:
    //...
        break;
    }

优点:标准。 缺点:总是启动一个新线程(在实践中),相对较慢(可能对客户端来说已经足够了,但会导致服务器的 DoS 漏洞,因为线程和套接字是“昂贵的”资源)。不要尝试使用 std::launch::deferred 而不是 std::launch::async 来避免新线程启动,因为 wait_for 将始终返回 future_status::deferred 而不会尝试运行代码。

4) ASIO 规定的方法 - 仅使用异步操作(这并不是问题的真正答案)。

优点:如果不需要短事务的巨大可扩展性,对服务器也足够好。 缺点:相当罗嗦(所以我什至不会包含示例 - 请参阅 ASIO 示例)。需要对异步操作及其完成处理程序使用的所有对象进行非常仔细的生命周期管理,这实际上要求在异步操作中包含和使用此类数据的所有类都派生自 enable_shared_from_this,这需要在堆上分配所有此类类,这意味着(至少对于短操作而言)可伸缩性将在大约 16 个线程后开始逐渐减少,因为每个堆分配/释放都将使用内存屏障。

关于c++ - 如何在 boost asio 中设置阻塞套接字的超时时间?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/291871/

相关文章:

Java - 网络 - 最佳实践 - 混契约(Contract)步/异步命令

java - 网络线程和 Swing 线程出现问题

c++ - 我怎么能通过 boost 来制作像 QFutureWatcher 这样的东西

java - 与库一起使用的不同记录器

c++ - 我可以确定 const 引用在被另一个实体修改时会更新吗?

c++ - vector 、矩阵、代数类设计

c# - 如何使用套接字流正确分离数据包? C#

c++ - else 和 if 语句

c++ - boost 文件系统错误(temp_directory_path 返回 <Bad Ptr>)

c++ - 为什么 std::sub_match<T> 公开继承自 std::pair<T, T>?