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

Har*_*sad 2 c++ sockets tcp

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

生成therad的数据生成数据,并将其写入共享内存.服务器从共享内存中线程化数据,并将其发送到客户端.

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

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

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

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

Jer*_*ner 13

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

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

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

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

这样你的发送线程将始终尽快将输出数据提供给内核,但不会更快 - 即你不浪费任何CPU做繁忙循环,但你也不要浪费时间插入任何不必要的延迟进入过程.