我可以使用 Boost.Asio 在多线程 I/O 服务上同步读取套接字并设置超时吗?

2023-11-23

我有一个使用 Boost.Asio 进行 TCP 和 UDP 套接字通信的应用程序。我知道“Asio”中的“A”代表异步,因此该库致力于鼓励您尽可能使用异步 I/O。我有一些情况,同步套接字读取更可取。但与此同时,我想为所述接收调用设置超时,因此不可能无限期地阻塞读取。

这似乎是 Boost.Asio 用户中非常常见的问题,过去 Stack Overflow 曾就该主题提出过以下问题:

  • C++ Boost ASIO:如何超时读/写?
  • asio::读取超时
  • 提高asio超时
  • 如何在 boost asio 中设置阻塞套接字的超时?

甚至可能还有更多。甚至还有文档中的示例了解如何实现带超时的同步操作。它们归结为将同步操作转换为异步操作,然后与异步操作并行启动它asio::deadline_timer。如果超时到期,计时器的到期处理程序可以取消异步读取。这看起来像这样(摘自上面链接示例的片段):

std::size_t receive(const boost::asio::mutable_buffer& buffer,
      boost::posix_time::time_duration timeout, boost::system::error_code& ec)
  {
    // Set a deadline for the asynchronous operation.
    deadline_.expires_from_now(timeout);

    // Set up the variables that receive the result of the asynchronous
    // operation. The error code is set to would_block to signal that the
    // operation is incomplete. Asio guarantees that its asynchronous
    // operations will never fail with would_block, so any other value in
    // ec indicates completion.
    ec = boost::asio::error::would_block;
    std::size_t length = 0;

    // Start the asynchronous operation itself. The handle_receive function
    // used as a callback will update the ec and length variables.
    socket_.async_receive(boost::asio::buffer(buffer),
        boost::bind(&client::handle_receive, _1, _2, &ec, &length));

    // Block until the asynchronous operation has completed.
    do io_service_.run_one(); while (ec == boost::asio::error::would_block);

    return length;
  }

这实际上是一个相对干净的解决方案:启动异步操作,然后手动轮询asio::io_service一次执行一个异步处理程序,直到async_receive()完成或计时器到期。

然而,如果套接字的底层 I/O 服务已经在一个或多个后台线程中运行,情况会怎样呢?在这种情况下,无法保证异步操作的处理程序将由上面代码片段中的前台线程运行,因此run_one()直到稍后某个可能不相关的处理程序执行后才会返回。这将使套接字读取相当无响应。

asio::io_service has a poll_one()函数将在不阻塞的情况下检查服务的队列,但我没有找到阻止前台线程(模拟同步调用行为)直到处理程序执行的好方法,除了没有正在执行的后台线程的情况之外asio::io_service::run()已经。

我看到了两种可能的解决方案,但我都不喜欢:

  1. 使用条件变量或类似的构造使前台线程在启动异步操作后阻塞。在处理程序中async_receive()调用,向条件变量发出信号以解除线程阻塞。这会导致每次读取时出现一些锁定,我希望避免这种情况,因为我希望在 UDP 套接字读取上实现最大可能的吞吐量。否则,它是可行的,并且可能是我会做的,除非出现更好的方法。

  2. 确保套接字有自己的asio::io_service没有由任何后台线程运行。这使得在需要的情况下使用套接字的异步 I/O 变得更加困难。

有什么其他方法可以安全地实现这一目标吗?


Aside:对于之前的 SO 问题,有一些答案提倡使用SO_RCVTIMEO套接字选项来实现套接字读取超时。这在理论上听起来很棒,但它似乎至少在我的平台上不起作用(Ubuntu 12.04,Boost v1.55)。我可以设置套接字超时,但它不会给 Asio 带来预期的效果。相关代码在/boost/asio/detail/impl/socket_ops.ipp:

size_t sync_recvfrom(socket_type s, state_type state, buf* bufs,
    size_t count, int flags, socket_addr_type* addr,
    std::size_t* addrlen, boost::system::error_code& ec)
{
  if (s == invalid_socket)
  {
    ec = boost::asio::error::bad_descriptor;
    return 0;
  }

  // Read some data.
  for (;;)
  {
    // Try to complete the operation without blocking.
    signed_size_type bytes = socket_ops::recvfrom(
        s, bufs, count, flags, addr, addrlen, ec);

    // Check if operation succeeded.
    if (bytes >= 0)
      return bytes;

    // Operation failed.
    if ((state & user_set_non_blocking)
        || (ec != boost::asio::error::would_block
          && ec != boost::asio::error::try_again))
      return 0;

    // Wait for socket to become ready.
    if (socket_ops::poll_read(s, 0, ec) < 0)
      return 0;
  }
}

如果套接字读取超时,则调用recvfrom()上面会返回EAGAIN or EWOULDBLOCK,它被翻译成boost::asio::error::try_again or boost::asio::error::would_block。在这种情况下,上面的代码将调用poll_read()函数,对于我的平台来说是这样的:

int poll_read(socket_type s, state_type state, boost::system::error_code& ec)
{
  if (s == invalid_socket)
  {
    ec = boost::asio::error::bad_descriptor;
    return socket_error_retval;
  }

  pollfd fds;
  fds.fd = s;
  fds.events = POLLIN;
  fds.revents = 0;
  int timeout = (state & user_set_non_blocking) ? 0 : -1;
  clear_last_error();
  int result = error_wrapper(::poll(&fds, 1, timeout), ec);
  if (result == 0)
    ec = (state & user_set_non_blocking)
      ? boost::asio::error::would_block : boost::system::error_code();
  else if (result > 0)
    ec = boost::system::error_code();
  return result;
}

我剪掉了为其他平台有条件编译的代码。正如你所看到的,如果套接字不是非阻塞套接字,它最终会调用poll()具有无限超时,因此会阻塞,直到套接字有数据要读取(并在超时时阻止尝试)。就这样SO_RCVTIMEO选项无效。


Boost.Asio 的支持futures可能会提供一个优雅的解决方案。当提供异步操作时boost::asio::use_futurevalue 作为其完成处理程序,启动函数将返回一个std::future将接收操作结果的对象。此外,如果操作失败完成,error_code被转换成system_error并通过future.

在 Boost.Asio C++11 期货中日期时间客户端示例,一个专用线程运行io_service,主线程发起异步操作,然后同步等待操作完成,如下:

std::array<char, 128> recv_buf;
udp::endpoint sender_endpoint;
std::future<std::size_t> recv_length =
  socket.async_receive_from(
      boost::asio::buffer(recv_buf),
      sender_endpoint,
      boost::asio::use_future);

// Do other things here while the receive completes.

std::cout.write(
    recv_buf.data(),
    recv_length.get()); // Blocks until receive is complete.

使用时futures,实现带超时的同步读取的总体方法与之前相同。可以使用异步读取并异步等待计时器,而不是使用同步读取。唯一的小变化是,而不是阻塞io_service或者定期检查谓词,可以调用future::get()阻塞直到操作成功或失败(例如超时)完成。

如果C++11不可用,那么可以为Boost.Thread自定义异步操作的返回类型future,如所示this answer.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

我可以使用 Boost.Asio 在多线程 I/O 服务上同步读取套接字并设置超时吗? 的相关文章

  • Rails 应用程序中的持久 TCP 连接

    我有一个在服务器上运行的第三方应用程序 它对客户端进行身份验证并返回响应 我的问题是 对于我收到 Rails 应用程序的每个请求 我都需要建立 TCP 连接并获取数据 是否可以有一个持久连接 以便我可以减少建立连接的开销 我希望你使用的是
  • 重构——套接字中的良好实践——简单的服务器-客户端 Swing 应用程序

    我使用单例和观察者模式编写了一个带有 Swing 接口的简单服务器 客户端程序 每个客户端都连接到服务器并可以发送消息 服务器将其收到的消息转发给其余的客户端 客户端使用 GUI 允许它们随时连接和断开与服务器的连接 该程序运行得很好 因为
  • 如何使用Python中字符串变量中存储的证书打开ssl套接字

    在Python中 ssl wrap socket可以从文件中读取证书 ssl wrap socket需要证书作为文件路径 如何使用从字符串变量读取的证书启动 SSL 连接 我的主机环境不允许写入文件 并且临时文件模块不起作用我正在使用Pyt
  • 如何“杀死”Pthread?

    我正在学习 Pthreads 并且想知道杀死这样一个对象的最佳方法是什么 在寻找类似的问题后 我无法找到 明确 的答案 但请随时向我指出任何相关问题 我正在使用一个小型客户端服务器应用程序 其中服务器主线程正在侦听套接字上的客户端连接 每次
  • C 套接字客户端/服务器滞后

    我正在编写一个 C C 客户端 服务器套接字应用程序 此时 客户端每 50 毫秒连接一次服务器并发送一条消息 一切似乎都正常 但数据流并不连续 突然 服务器不再收到任何消息 然后一次收到 5 条消息 有时一切正常 有人知道这种奇怪行为的起源
  • 如何在java中通过socket发送Image数据类型

    我真的很困惑如何通过套接字发送图像数据类型 请帮我 我已经搜索了如何将 Image 数据类型转换为 char 但结果是 0 Use ImageIO http docs oracle com javase 1 4 2 docs api jav
  • 提升 asio 和 endian

    我不知道 boost asio 是否处理字节序 Asio 确实会转换类似的东西port进入网络秩序 转换函数不作为官方接口的一部分公开 而是隐藏在detail名称空间代替 例如boost asio detail socket ops hos
  • PHP:如何检查 Guzzle 4 中的超时异常?

    如果请求期间发生错误 Guzzle 会引发异常 不幸的是 似乎没有特定于超时的错误 这对我来说很重要 因为我知道这些错误偶尔会发生 我想重试相应的请求 并且需要能够判断错误是否是由于超时而发生的 来自docs http docs guzzl
  • Java/Python 中的快速 IPC/Socket 通信

    我的应用程序中需要两个进程 Java 和 Python 进行通信 我注意到套接字通信占用了 93 的运行时间 为什么通讯这么慢 我应该寻找套接字通信的替代方案还是可以使其更快 更新 我发现了一个简单的修复方法 由于某些未知原因 缓冲输出流似
  • 从 sockaddr_storage 检索 ip 和端口

    我有一个sockaddr storage包含远程主机的 ipv4 地址和端口 我没见过这些struct但之前我不知道如何将它转换成struct我可以直接检索IP地址和端口号 我尝试过谷歌搜索struct但还没有发现任何东西 关于如何执行此操
  • 在Luasocket中,在什么条件下,即使在select告诉它可以安全读取之后,accept调用也可以阻塞?

    卢阿索基特select http w3 impa br diego software luasocket socket html select函数应该告诉何时可以在不阻塞的情况下读取套接字 它显然也可以用来告诉服务器套接字何时准备好接受新连
  • 使用 paramiko 运行 Sudo 命令

    我正在尝试执行sudo使用 python paramiko 在远程计算机上运行命令 我尝试了这段代码 import paramiko ssh paramiko SSHClient ssh set missing host key polic
  • perl6 IO::Socket::INET 无法从套接字接收数据:连接被对等方重置

    示例服务器 usr bin env perl6 my listen IO Socket INET new listen localhost
  • 当使用环回地址使用 TCP/IP 套接字进行 IPC 时,常见的网络堆栈是否会跳过将消息帧封装在较低级别的 PDU 中?

    在某些环境 例如 Java 中 很自然地使用 TCP IP 套接字通过 localhost 地址 IPv4 中的 127 0 0 1 或 IPv6 中的 1 在同一主机上的进程之间传递消息 因为Java倾向于不在其API中公开其他IPC机制
  • Linux TUN/TAP:无法从 TAP 设备读回数据

    问题是关于如何正确配置想要使用 Tun Tap 模块的 Linux 主机 My Goal 利用现有的路由软件 以下为APP1和APP2 但拦截并修改其发送和接收的所有消息 由Mediator完成 我的场景 Ubuntu 10 04 Mach
  • 中断连接套接字

    我有一个 GUI 其中包含要连接的服务器列表 如果用户单击服务器 则会连接到该服务器 如果用户单击第二个服务器 它将断开第一个服务器的连接并连接到第二个服务器 每个新连接都在一个新线程中运行 以便程序可以执行其他任务 但是 如果用户在第一个
  • Java套接字:在连接被拒绝异常时重试的最佳方法?

    现在我正在这样做 while true try SocketAddress sockaddr new InetSocketAddress ivDestIP ivDestPort downloadSock new Socket downloa
  • Java 服务器-客户端 readLine() 方法

    我有一个客户端类和一个服务器类 如果客户端向服务器发送消息 服务器会将响应发送回客户端 然后客户端将打印它收到的所有消息 例如 如果客户端向服务器发送 A 则服务器将向客户端发送响应 1111 所以我在客户端类中使用 readLine 从服
  • 链路范围 IPv6 多播数据包突然无法在 MacBook Pro 上路由?

    这是一个有点晦涩的问题 但我很困惑 我想也许有人对这个问题有更多的线索 我的同事已经在他的 MacBook Pro 上成功运行了一个使用 IPv6 多播的内部应用程序几个月了 但今天 Mac 决定停止路由多播数据包 特别是 该程序打印此错误
  • 如何在 Perl 中使用原始套接字?

    你怎样才能得到一个rawPerl 中的套接字 那么构建与其一起使用的数据包的最佳方法是什么 与在 C 中执行的操作相同 通过在创建套接字时设置套接字类型 在示例中CPAN http search cpan org rgarcia perl

随机推荐

  • 制作 USB 设备,用 Java 控制它

    I m thinking about making a physical controller device with knobs buttons and LEDs I d like to interact with it using Ja
  • 您可以在代码中配置 log4net 而不是使用配置文件吗?

    我明白为什么 log4net 使用app config用于设置日志记录的文件 因此您可以轻松更改信息的记录方式 而无需重新编译代码 但就我而言 我不想打包app config文件与我的可执行文件 而且我不想修改我的日志记录设置 有没有办法让
  • 如何从 Eclipse 导入包?

    在我的目录之一中 我拥有属于一个包 游戏 的所有 java 文件 现在我想创建一个不属于该包并导入 game 包的 java 文件 如果我创建一个新文件并写入import game 然后 Eclipse 抱怨它不知道 游戏 包的含义 有人可
  • Webstorm 的 Sass 文件观察器

    我正在尝试在 NodeJS 项目中使用 sass 这是我使用 nodejs 安装的库 https www npmjs org package node sass 我在这个项目中使用 webstorm 但无法让 Sass 文件观察器工作 即使
  • 从脚本与命令行在节点中分配全局变量

    我有以下脚本 script js var bar bar1 function foo console log this bar this bar console log global bar global bar foo Running n
  • 为什么在 Windows 中命名文件“con.txt”会使 Python 写入控制台而不是文件?

    我需要帮助调试 Python 中的一些奇怪的文件行为 采取以下脚本 write con py f open con txt w f write hi 在 Linux 中 这会创建一个名为con txt与内容hi 在 Windows 中这样写
  • 电路/方框图绘制

    我正在寻找用于 漂亮的 电路 框图绘制的算法或可视化工具 我也对这个问题的一般表述感兴趣 我所说的 电路绘图 是指利用 I O 端口及其连接 电线 探索框图 矩形 的位置和路线的能力 这些框图可以是分层的 即某些块可能具有一些嵌套的内部子结
  • 将 Word 文档文本转换为 HTML 的库 [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 目前不接受答案 是否有一个 Net开源库可以将word文档转换为HTML以在网页内显示 我知道有几种将 word 文档转换为 html 文件的工具 但我的要求是在
  • Rails 3.1 身份映射问题?

    有谁知道 Rails 3 1 IdentityMap 功能的关键问题导致默认情况下强制禁用该功能 我确信存在一些小具体问题 但是在为已构建的 Rails 3 1 应用程序启用它之前 是否有任何人应该注意的重大问题 来自代码中的注释 Acti
  • python-opencv AttributeError:“模块”对象没有属性“createBackgroundSubtractorGMG”

    我正在尝试遵循以下中给出的教程 https opencv python tutroals readthedocs org en latest py tutorials py video py bg subtraction py bg sub
  • 在 Mac OS 上设置 tkinter 图标

    我正在尝试更改 Mac OS 的 tk 应用程序上显示的图标 上次我检查此代码适用于 Windows 目标是使该解决方案能够跨所有平台工作 root tk Tk app Application master root app master
  • Clion 无法识别我的快速工具链路径

    我正在尝试为 swift 项目配置 clion 但它无法识别 swift 工具链路径 which swift 产生输出 usr bin swift bin swift 所以工具链就在那里 但 clion 只是不认识它 我该如何进行这项工作
  • jQuery 工具提示 onClick?

    我已经找了很长时间了 似乎找不到一个使用以下内容的 jQuery 工具提示插件 onClick 代替hover 使其像切换按钮一样工作 淡入淡出 使用工具提示的想法是 我有一些想要在其中显示内容的链接 虽然普通的工具提示 这可能是我出错的地
  • 如何在 Windows 上安装 pip?

    pip是替代品easy install 但我应该安装pip using easy install在 Windows 上 有没有更好的办法 Python 3 4 和 2 7 9 好消息 Python 3 4 2014 年 3 月发布 和Pyt
  • MySQL 表的主键是否应该公开?

    我有许多描述模型的 MySQL 表 例如 用户 业务 等 这些表的主键是否应该暴露给客户端 我主要是从安全角度来问 但是还有其他我没有想到的考虑因素吗 暴露您的主键 特别是如果它们是可预测的 是一个称为不安全直接对象引用的漏洞 通过使用如下
  • 如何设置 NSDate 上的时间?

    我想用我想要的小时 分钟 秒设置 NSDate 时间 目前我正在使用 NSDate 组件 但它没有给出期望的结果 comps setHour hours comps setMinute 0 comps setSecond 0 NSDate
  • 有人有 SourceForge 的 Xselerator XSL IDE 副本吗? [关闭]

    这个问题不太可能对任何未来的访客有帮助 它只与一个较小的地理区域 一个特定的时间点或一个非常狭窄的情况相关 通常不适用于全世界的互联网受众 为了帮助使这个问题更广泛地适用 访问帮助中心 我多年来一直使用 Marrowsoft Xselera
  • 使用 python pandas 比较两个 csv 文件

    我有两个 csv 文件 均由两列组成 第一个具有产品 ID 第二个具有序列号 我需要查找第一个 csv 中的所有序列号 并在第二个 csv 上查找匹配项 结果报告将在单独的列中包含匹配的序列号以及每个 csv 中相应的产品 ID 我确实修改
  • ASP.NET MVC 4 C# HttpPostedFileBase,如何存储文件

    Model public partial class Assignment public Assignment this CourseAvailables new HashSet
  • 我可以使用 Boost.Asio 在多线程 I/O 服务上同步读取套接字并设置超时吗?

    我有一个使用 Boost Asio 进行 TCP 和 UDP 套接字通信的应用程序 我知道 Asio 中的 A 代表异步 因此该库致力于鼓励您尽可能使用异步 I O 我有一些情况 同步套接字读取更可取 但与此同时 我想为所述接收调用设置超时