【问题标题】:Can a socket become writeable after an EWOULDBLOCK but before an epoll_wait?一个套接字可以在 EWOULDBLOCK 之后但在 epoll_wait 之前变为可写的吗?
【发布时间】:2016-08-16 00:03:37
【问题描述】:

我有一个基于反应器的设计,其中在epoll_wait 中轮询了许多套接字文件描述符。

为了优化延迟而不是吞吐量,当写入套接字时,我们会尝试立即直接写入文件描述符。

如果出现EWOULDBLOCK,我们会缓冲未写入的数据,然后等待套接字再次变为可写。

下次我们的epoll_wait 将我们的文件描述符返回为可写,然后我们从缓冲的数据中发送。

问题是,套接字能否在我们收到EWOULDBLOCK之前我们调用epoll_wait 之后变为可写?

也就是说,如果我们执行 2 个 write 调用,其中第一个 write 被部分写入,其余部分被缓冲,然后执行第二个 write,尝试立即写入,套接字是否可以在第一次和第二次写入调用之间变为可写,而我们最终会在远端接收到乱序数据?

【问题讨论】:

  • 我没有得到你的关注。假设您没有尝试发送已经发送的数据并且没有做一些延迟的事情,例如尝试在没有任何同步的情况下在线程之间共享套接字,这将如何导致数据乱序?
  • @cHao 不,没有什么比非同步多线程更迟钝的了!请参阅EJP 我对以下答案的评论 - 我想是内核线程清空了我担心的套接字发送缓冲区。

标签: linux sockets epoll


【解决方案1】:

问题是,套接字能否在我们收到EWOULDBLOCK之前我们调用epoll_wait 之后变为可写?

是的,可以。在EWOULDBLOCK 时,套接字缓冲区已满。但是一旦对等方确认了一些数据并且套接字缓冲区中的空间被清除,套接字就会再次变为可写的。这发生在代码之外的后台,因此套接字可以在任何时间变为可写。

也就是说,如果我们执行 2 次 write 调用,其中第一个 write 被部分写入,其余部分被缓冲,然后执行第二个 write,尝试立即写入,套接字是否可以在第一次和第二次 write 调用,我们最终会在远端收到乱序数据?

是的,可以。这将是您的代码逻辑中的一个错误,因为您在完成写入任何仍在等待发送的 缓冲数据 之前尝试将 新数据 写入套接字。

当您检测到EWOULDBLOCK 并开始缓冲数据时,停止为任何新数据调用write(),直到您的缓冲区被完全清除。如果您需要发送新数据,并且缓冲区中已经有数据,只需将新数据附加到缓冲区的末尾并继续前进,甚至根本不要调用write()。每当套接字报告为可写时,检查您的缓冲区中是否有任何待处理的数据,如果是,则从缓冲区的前面尽可能多地发送,同时删除成功发送的数据,直到缓冲区被清除或EWOULDBLOCK又被举报了。

【讨论】:

    【解决方案2】:

    一个套接字可以在 EWOULDBLOCK 之后但在 epoll_wait 之前变为可写的吗?

    是的。一旦套接字发送缓冲区中有空间,它就可以变为可写的,只要其中现有数据的任何前缀被对等方确认,与您的应用程序异步。

    为了优化延迟而不是吞吐量,当写入套接字时,我们会尝试立即直接写入文件描述符。

    很好。

    如果发生 EWOULDBLOCK,我们会缓冲未写入的数据,然后等待套接字再次变为可写。下次我们的 epoll_wait 将我们的文件描述符返回为可写时,我们就从缓冲的数据中发送。

    再次VG。

    问题是,在我们收到 EWOULDBLOCK 之后但在调用 epoll_wait 之前,socket 是否可以变为可写的?

    是的,见上文。

    也就是说,如果我们进行 2 次写入调用,其中第一次写入被部分写入,其余部分被缓冲,然后进行第二次写入,尝试立即写入,套接字是否可以在第一次和第二次写入调用之间变为可写

    是的,见上文。

    我们最终会在远端收到乱序数据?

    我不知道如何,除非您从多个线程写入套接字。

    【讨论】:

    • 不,它是单线程的。我想我想知道一个内核线程在EWOULDBLOCK 之后但在第二次写入之前在套接字发送缓冲区中腾出空间,它将尝试立即写入 fd。第二次写入是否会因为内核在套接字发送缓冲区中腾出空间而成功?
    • 可以,但这怎么会改变你发送数据的顺序呢?
    • @cHao 鉴于它是基于反应器的设计,我可以尝试在重新进入反应器循环之前对同一个套接字进行 2 次写入,重新输入 epoll_wait 调用,让套接字返回可写,然后从我未发送的缓冲区写入。如果第一次写入部分发送,那么一半写入套接字发送缓冲区,另一半在用户空间缓冲以备后用,当epoll_wait再次返回可写时,内核线程在套接字发送缓冲区中腾出一些空间,然后第二次写入尝试立即发送,并且成功,并且只有在重新进入反应器循环并调用epoll_wait
    • @SteveLorimer:如果你真的在写,你的缓冲区是空的,对吧?否则,您将在缓冲区中排队数据。
    • @SteveLorimer 如果您缓冲了第一次写入的剩余部分,直到轮询显示它是可写的,然后成功写入其他内容,您将得到乱序数据。解决方案是,如果已经有缓冲数据,则不要尝试第二次写入:在第一次写入之后,还要缓冲第二次写入。
    猜你喜欢
    • 2017-02-25
    • 2018-11-19
    • 1970-01-01
    • 2016-12-09
    • 1970-01-01
    • 2016-05-08
    • 1970-01-01
    • 1970-01-01
    • 2023-02-14
    相关资源
    最近更新 更多