当服务器运行缓慢时,写入客户端返回EWOULDBLOCK

时间:2013-04-29 14:40:17

标签: c++ sockets tcp

我试图在特定时间间隔后在套接字中写入一些数据。我有两个线程,一个线程维护TCP连接,另一个线程生成数据。

生成therad的数据生成数据,并将其写入共享内存。服务器从共享内存中读取数据并将其发送到客户端。

但是,当数据生成线程变慢时,当涉及大量计算时,服务器线程在尝试写入客户端时会收到EWOULDBLOCK错误。但是,令人惊讶的是,从客户端来看,没有这样的错误。

如果我没有错,当服务器比客户端快时,会返回EWOULDBLOCK错误,并且在再次写入之前未完全读取套接字缓冲区。但是,这种情况完全相反。

可能是因为服务器therad一直处于休眠状态,直到数据生成线程完成(数据线程具有更高的优先级)。

有人可以解释这里可能会发生什么吗?

1 个答案:

答案 0 :(得分:12)

当您使用非阻塞套接字时,将返回EWOULDBLOCK,并且内核的传出数据缓冲区中没有足够的空间来容纳该套接字以保存您要发送的任何数据。如果客户端读取速度太慢,可能会发生这种情况,但如果服务器尝试同时发送大量数据,也可能发生(即使使用快速客户端);例如,如果您的计算线程花费很长时间计算数据,然后在计算结束时突然一次超过300,000字节的数据,您的服务器线程可能会执行以下操作:

  1. 查看共享内存区域中的某些数据是否可用,是时候开始发送了它!
  2. 使用len = 300000调用send()。 send()吸收尽可能多的数据,因为它可以放入内核的传出套接字数据缓冲区(例如131,072字节),并返回131072以指示它已完成的工作。
  3. 现在你的服务器线程看到它还有168928字节的数据要发送,所以它再次使用len = 168928调用send()。此时,内核的传出套接字数据缓冲区仍然完全填满(因为还没有机会发送任何数据包),所以send()返回EWOULDBLOCK。
  4. 通常,只要您使用非阻塞套接字,EWOULDBLOCK就是您的代码需要处理的内容。我通常处理它的方式是:

    1. 在select()(或poll()等)中等待,直到套接字返回ready-for-write
    2. 当select()/ poll()指示套接字已准备好写入时,请在套接字上调用send(),直到您发送了所有可用数据,或者直到send()返回EWOULDBLOCK(以先到者为准。)
    3. 如果您在步骤2中获得了EWOULDBLOCK,请转到1。
    4. 这样你的发送线程总是尽可能快地将传出数据提供给内核,但不会更快 - 即你不要浪费任何CPU做忙碌的循环,但你也不要这样做。通过在流程中插入任何不必要的延迟来浪费时间。