2

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

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

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

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

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

有人可以解释这里可能发生的事情吗?

4

1 回答 1

14

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

  1. 看到共享内存区域中有一些数据可用,是时候开始发送了!
  2. 使用 len=300000 调用 send()。send() 将尽可能多的数据吸收到内核的输出套接字数据缓冲区中(例如 131,072 字节),并返回 131072 以指示它已完成的操作。
  3. 现在你的服务器线程发现它还有 168928 字节的数据要发送,所以它再次调用 send() 并设置 len=168928。此时,内核的输出套接字数据缓冲区仍然完全充满(因为还没有机会发送任何数据包),所以 send() 返回 EWOULDBLOCK。

通常,只要您使用非阻塞套接字,EWOULDBLOCK 就是您的代码需要处理的东西。我通常处理它的方式是:

  1. 在 select() (或 poll() 等)中等待,直到套接字返回准备好写入
  2. 当 select()/poll() 指示套接字已准备好写入时,在套接字上调用 send() 直到您发送了所有可用数据,或者直到 send() 返回 EWOULDBLOCK(以先到者为准)。
  3. 如果您在第 2 步中获得了 EWOULDBLOCK,请转到第 1 步。

这样,您的发送线程将始终尽可能快地将传出数据提供给内核,但不会更快 - 即您不会浪费任何 CPU 进行忙循环,但您也不会浪费时间插入任何不必要的过程中的延误。

于 2013-04-29T15:36:16.200 回答