【问题标题】:Handling short reads using epoll()使用 epoll() 处理短读
【发布时间】:2011-06-24 01:51:26
【问题描述】:

假设客户端发送了 100 个字节的数据,但不知何故服务器只收到了 90 个字节。我该如何处理这种情况?如果服务器在 while 循环中调用“读取”函数检查接收到的总数据,那么服务器将永远等待最后 10 个字节的数据包..

此外,客户端可能会在数据传输过程中断开连接。在这种情况下,服务器也将永远等待,直到它接收到所有不会到达的数据..

我正在使用 tcp 但在现实世界的网络环境中,这种情况可能会发生。提前谢谢...

【问题讨论】:

    标签: linux networking tcp epoll


    【解决方案1】:

    在收到所需的字节数之前,不要在循环中调用read() 函数。相反,您将套接字设置为非阻塞并在循环中调用read() 函数,直到它返回0(表示流结束)或错误。

    在正常情况下,循环将在 read() 返回 -1 时终止,errno 设置为 EAGAIN。这表明连接尚未关闭,但当前没有更多可用数据。此时,如果您还没有来自客户端的足够数据,您只需保存拥有的数据以备后用,然后返回主epoll() 循环。

    如果剩余数据到达时,套接字将被epoll()返回为可读,您将read()剩余数据,检索保存的数据并全部处理。

    这意味着您需要在每个套接字数据结构中留出空间来存储已读取但尚未处理的数据。

    【讨论】:

      【解决方案2】:

      您必须仔细检查read 的返回值。它可以返回任何三个的东西:

      一个正数,表示读取了一些字节。

      零,表示另一端已经优雅地关闭了连接。

      -1,表示发生错误。 (如果套接字是非阻塞的,那么错误 EAGAIN 或 EWOULDBLOCK 表示连接仍然​​打开,但现在没有数据准备好,所以你需要等到epoll 说有更多数据给你。)

      如果您的代码没有检查这三件事中的每一个并以不同的方式处理它们,那么它几乎肯定会被破坏。

      这些涵盖了您询问的所有情况,例如客户端发送 90 个字节然后关闭或粗鲁地断开连接(因为对于这些情况, read() 将返回 0 或 -1)。

      如果您担心客户端可能会发送 90 个字节然后不再发送,并且永远不会关闭连接,那么您必须实现自己的超时。为此,您最好的选择是非阻塞套接字并在 select() / poll() / epoll() 上设置超时,如果连接空闲时间过长,则放弃连接。

      【讨论】:

        【解决方案3】:

        TCP 连接是分层在基于数据包的网络之上的双向流。只读取对方发送的部分内容是很常见的。你必须循环阅读,追加直到你有完整的消息。为此,您需要在 TCP 之上使用的应用程序级协议(消息的类型、结构和语义)(FTP、HTTP、SMTP 等就是此类协议)。

        要回答问题的具体第二部分 - 将 EPOLLRDHUP 添加到 epoll(7) 事件集以在连接断开时收到通知。

        【讨论】:

          【解决方案4】:

          除了 caf 所说的,我建议只订阅 EPOLLRDHUP,因为这是确定连接是否关闭的唯一安全方法(read() == 0 不可靠,因为 caf 也提到了这一点,在错误的情况下可能是真的)。 EPOLLERR 总是订阅,即使您没有特别要求。正确的行为是在 EPOLLRDHUP 的情况下使用 close() 关闭连接,甚至可能在设置了 EPOLLERR 时。

          有关更多信息,我在这里给出了类似的答案:epoll_wait() receives socket closed twice (read()/recv() returns 0)

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2015-03-12
            • 2011-08-16
            • 2023-03-04
            • 1970-01-01
            • 2012-04-04
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多