假设一个点对点程序使用epoll
来执行异步TCP读取和写入多个对等点。当然,这意味着每个文件描述符都设置为非阻塞,以允许调用epoll_wait
并检查多个套接字。
但是,存在潜在的问题。假设有两个对等体:A和B.A尝试向B写入消息,但B是拥塞或其他东西,因此对write
的调用返回-1,errno
设置为EAGAIN
。此时,A在打电话给epoll_wait
时入睡。
但请注意,B已经停止了对epoll_wait
的调用。如果B从未收到有关A未能尝试向其发送消息的通知,则B将永远不会唤醒并尝试在A的套接字上执行读取,并且整个事情将会死锁。所以我的问题是,B保证会被通知A正试图向它发送消息,即使A放弃原来的write
呼叫并进入睡眠状态?
即使上面的答案是“是”,由于应用层失步,这样的系统是否有可能无限期地死锁?即A试图写入B但失败,所以它进入睡眠状态。然后B醒来并试图从A读取,但因为A进入睡眠而失败。等等
任何具有可能状态的协议都允许双方在读取之前等待另一方读取,这将是一个根本破坏的协议。对于对等协议,通常禁止每一端因为无法写入而延迟读取。
在实现方面,通常每次调用epoll_wait
(或发现就绪I / O的等效方式)都会检查程序正在使用的所有描述符的输入。除非应用程序具有已读取的未处理数据,否则读取永远不会延迟,并且只要处理完数据就会停止延迟。在阅读之前等待网络活动通常是一个非常糟糕的主意。
这就是典型的协议中立TCP代理使用两个进程或两个线程的原因。你不能只是从A读取然后对B进行阻塞写入,因为你不知道B在写入之前是否需要读取。
这也是为什么用recv
调用MSG_WAITALL
几乎总是一个坏主意。另一端可能正在等待您在发送之前接收已发送的字节。没有协议可以允许一方在读取任何字节之前等待所有字节发送,如果它还允许另一方等待直到发送其余字节之前已经读取了一些字节!