【问题标题】:Behavior of the poll() system call and receiving or sending data afterwardspoll() 系统调用和之后接收或发送数据的行为
【发布时间】:2016-04-04 22:32:01
【问题描述】:

让我们考虑下面的代码

pollfd file_descriptors[1];
file_descriptors[0].fd = sock_fd;
file_descriptors[0].events = POLLIN;

int return_value = poll(file_descriptors, 1, 0);

if (return_value == -1) { cerr << strerror(errno); }
else if (return_value == 0) { cerr << "No data available to be read"; }
else { 
    if (file_descriptors[0].revents & POLLIN) {
        recv(sock_fd, buff, 1024, 0); 
    }
}

现在我对上面的代码有两个问题。

  1. 如果对 poll() 的调用既不返回 -1 也不返回 0,并在位图 revents 中为 file_descriptors 数组中的第一个条目设置了 POLLIN 标志,那么对 @987654328 的调用将@ 堵塞?如果不是,那么数据会被瞬时读取吗?
  2. 假设对poll() 的调用与上述方式相同。将读入多少数据?是否与定期拨打recv() 相同?即在上述情况下,任意(对程序员)数量小于或等于1024。那么如果我想在再次阅读之前poll(),我是否只是从第一次调用poll() 开始重复直到所有数据都被完全读入(即在客户端服务器场景中,这将对应于正在完成的请求)?

谢谢!

【问题讨论】:

  • 对于 3,请参阅 stackoverflow.com/questions/589928/…。本质上,如果一个套接字端发送正常数据和然后 OOB 数据,客户端会尽快获得OOB 数据,可能 其他数据之前。 [嗯,这取决于客户如何调用recv,以及其他一些事情,这还不是全部......请参阅链接]
  • 非常感谢!我会调查的:)
  • @deviantfan 从我在该问题的答案中读到的内容。我将忽略当前用例中的所有 OOB 数据。不过,我有一个后续问题。如果确实有一些 OOB 数据已经发送,而我忽略它,即只是定期调用 recv() 。这些数据会被忽略吗?还是会被注入到我正在读取的当前字节流中(或 recv()-ing)?
  • 视情况而定。您可以使用MSB_OOB in recv 来查询OOB 数据而没有这样的选项来查询普通数据,或者您可以使用setsockoptSO_OOBINLINE 来接收没有特殊recv 选项的所有内容。
  • @deviantfan 谢谢!因此,我将忽略 OOB 数据,因为它并不完全存在。我想我很快就会删除问题的第三部分。我会稍等片刻,以确保其他人没有在输入内容。

标签: c++ sockets c++11 networking tcp


【解决方案1】:

如果poll() 返回一个严格的正值,这意味着至少有一个描述符有一些数据要返回。

recv() 因此不应该被阻塞:因为有数据它应该返回一些东西。在任何情况下,都不能保证调用会返回所有 1024 个字节。

因此,您必须检查recv() 返回的字节数。最终,您必须循环轮询/接收,直到获得所有预期数据。

您可以通过使用标志参数MSG_DONTWAIT 调用或以更持久的方式通过fcntl() 来控制阻塞/非阻塞行为。

编辑:请注意,如果套接字 (for example a loss of connection) 出现问题,则投票承诺的可用数据可能会丢失,并阻止 recv() 出乎所有人的意料。因此,显式使用非阻塞标志更安全(并考虑recv() 可能返回错误代码而不是读取的字节数)。

【讨论】:

  • 谢谢!您能否分享一些参考资料作为您的答案?
  • 另外,当您在致电recv() 之前进行轮询时,拥有MSG_DONTWAIT 有什么意义?
  • @Curious 最后这句话是一般信息,因为 OP 似乎担心阻塞与否。如上所述,poll() 成功后不会有阻塞。
  • @Christophe 这不能保证。 poll 函数报告当前状态,无法预测未来。假设来自poll 的读取命中可确保未来的读取操作不会阻塞,这是一个错误,过去曾导致严重的安全隐患。如果您不想阻塞,则必须将套接字设置为非阻塞或使用带有请求它们不阻塞的标志的读/写操作。
  • 或者,如果您使用 UDP 并且数据报被丢弃,如果这导致安全关键进程挂起,这可能是一场真正的灾难。 (是的,这确实发生了。)
【解决方案2】:

如果对 poll() 的调用既不返回 -1 也不返回 0 并且在位图中为 file_descriptors 数组中的第一个条目设置 POLLIN 标志,那么对 recv() 的调用会阻塞吗?如果不是,那么数据会被瞬时读取吗?

poll 的调用对recv 的调用没有影响。 recv 是否阻塞取决于您调用 recv 时可用的数据以及套接字是处于阻塞模式还是非阻塞模式。

假设对 poll() 的调用与上面提到的方式相同。将读入多少数据?它是否与对 recv() 的常规调用相同?即在上述情况下,任意(对程序员)数量小于或等于 1024。然后,如果我想在再次读取之前进行 poll(),我是否只是从第一次调用 poll() 开始重复,直到所有数据都被完全读入(即在客户端服务器场景中,这将对应于正在完成的请求)?

假设您不想阻塞,您应该将套接字设置为非阻塞。你有两个基本的选择。您可以调用一次接收函数,然后再次调用poll。或者您可以继续呼叫接收,直到您收到“将阻止”指示。如果套接字是 TCP 连接,您可以继续调用接收函数,直到收到“将阻塞”指示或接收的字节数少于您要求的字节数。

您可以基于pollselect 使用三种常见的非阻塞I/O 策略。都要求将套接字设置为非阻塞。

1) 在执行 I/O 操作之前,您始终可以调用 pollselect。然后,仅当您从 pollselect 获得读取或写入命中时,您才尝试单个读取或写入操作。

2) 您可以先尝试readwrite 操作。如果它立即成功,那就太好了。如果没有,请等待pollselect 命中,然后重试操作。

3) 在执行 I/O 操作之前,您可以调用 pollselect。然后您尝试多个reads 或writes,直到您完成您需要做的所有事情或获得“将阻止”指示。当您收到“将阻止”指示时,请等到selectpoll 告诉您,然后再尝试在该套接字上朝该方向执行另一个操作。

方法 3 可能是最常见的读取方法。方法 2 可能是最常见的写操作。

请记住,poll 是一个状态报告功能,可以告诉您当前信息,这一点非常重要。它不附带任何未来的保证。假设来自poll 的读取指示意味着未来的读取操作不会阻塞,这是一个错误,并且它在过去造成了具有重大安全隐患的严重错误。保证套接字操作不会阻塞的唯一方法是设置套接字非阻塞。

【讨论】:

  • 非常感谢!但是你觉得我的逻辑看起来对吗? poll(),然后是recv(),然后是poll(),然后是recv(),依此类推……这是为了从网络中获得非阻塞读取。基本上我要问的是,如果在revents 结构中设置了 POLLIN 标志,那么紧随其后的recv() 不会阻塞正确吗?
  • @Curious 如果您想要非阻塞读取,则必须将套接字设置为非阻塞。然后您可以使用poll 知道何时执行读写操作。您可以在每次操作前使用poll,也可以调用poll,仅作为在收到“将阻止”指示时等待的一种方式。两种方法都有效。在读取之前先使用poll 是很常见的,但只有在写入获得“将阻止”指示时才先写入并回退到poll。但你可以随心所欲。
  • 好的,那么下面的步骤正确吗? 1. 设置套接字为非阻塞, 2. 调用poll(), 3. 如果poll() 表示有数据要读取,则调用recv(), 4. 如果所有需要的数据都在break中读取, 5 . 从 (2) 重复
  • 也是我上面提到的最有效的方法来实现这种非阻塞方案吗?如果不是,我可以改变什么?
  • 这是处理读取的常用方法。我将更新我的答案以解释三种不同的基本 I/O 策略。
【解决方案3】:

如果对poll() 的调用既不返回-1 也不返回0,并且在位图中为file_descriptors 数组中的第一个条目设置了POLLIN 标志,那么对recv() 的调用会阻塞吗?

不应该,除非另一个线程已经从套接字读取了当前可用的所有数据。

如果不是,那么数据会被瞬时读取吗?

是的。

假设对poll() 的调用与上述相同。将读取多少数据?

与已经到达的数据一样多。

这是否与定期拨打recv() 相同?即在上述情况下,任意(对程序员而言)数量小于或等于 1024。

是的。

那么如果我想在再次阅读之前poll(),我是否只是从第一次调用poll() 开始重复,直到所有数据都被完全读入(即在客户端服务器场景中,这将对应于正在完成的请求)?

是的。

【讨论】:

  • 感谢您的回答!你能分享一些对它的引用吗?这样当我有类似问题时,我就知道去哪里了
  • poll 函数是一个状态报告函数。它不附带任何未来的保证。假设来自pollselect 的指示将保证未来的读取或接收不会阻塞在任何情况下是一个错误,并且它在过去导致了具有安全隐患的错误.
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-14
  • 1970-01-01
  • 2011-08-06
  • 2020-02-09
相关资源
最近更新 更多