本次话题来自apple邮件列表《Must you read all you can on kCFStreamEventHasBytesAvailable?》
CFSocket和CFStream作为CFNetwork的底层支持,也是我们讨论的范围。
我们知道非block的CFStream一般有两种实现方式,一种是runloop,另一种是polling。在前者中,依靠注册的事件发生时,由runloop调度callback执行相应的工作。对于kCFStreamEventHasBytesAvailable事件,目前有两种实现,一种是尽力读取所有available数据,一种是每次设定一个一定长度的buffer,然后向其中写入数据,再取出来做后续处理,而不管此次available数据是否读完。没有读取完的可能因为buffer太小,只有部分数据被处理。别的可能原因,暂时想不到。即使在苹果的示例代码里,对于这两种实现也有分歧。在《CFNetwork Programming Guide》中采用后者;而CFFTPSample采用前者。根据邮件的回复,鼓励使用后者,说前者会引入一些问题。如果你在callback中运行runloop或者stream在多个runloop中被调度,就会在stream发出有available数据信号和第二次调用CFReadStreamRead()之间产生竞争,可能导致callback声称有available数据,而实际却没有的情况。最佳实践是在每次callback中只使用一次CFReadStreamRead(),并尽可能榨干所有available数据,这就要求一个大小适当的buffer。这个尺寸因应用而异。一般,对于繁忙的TCP Socket,以socket的内核buffer的大小(32K)为宜。
为方便理解,下面是引用的原文:
You are correct; it is not necessary to read all available bytes. Aslong as you read at least one byte, you will get anotherHasBytesAvailable callback if there are still bytes remaining. Infact, calling more than once can potentially introduce problems. Inparticular, if you run the run loop from within your callback, or thestream is scheduled on multiple run loops, you will have introduced arace between when the stream signals any remaining (or newly arrived)bytes and when you call CFReadStreamRead() for the second time. Theresult is you may get a callback claiming bytes are available when infact there are none.
Best practices and best performance is to call CFReadStreamRead() onlyonce per kCFStreamEventHasBytesAvailable callback, and provide abuffer large enough to be likely to drain all available bytes. Thatsize varies widely from application to application; you'll have toexperiment to find the best value for you. However, for busy TCPsockets, the size of the socket's kernel buffer (32k by default) is agood choice.
Hope that helps, REW
On Apr 13, 2006, at 6:26 PM, James W. Walker wrote:
If you're using a CFReadStream in the non-blocking mode, and yourcallback receives kCFStreamEventHasBytesAvailable, what happens ifyou don't consume all the bytes that are available? In the best ofall possible worlds, you'd get anotherkCFStreamEventHasBytesAvailable message. The sample code in theCFNetwork Programming Guide seems to suggest that is so, because itjust reads what fits in one buffer onkCFStreamEventHasBytesAvailable. In contrast, CFFTPSample takespains to keep reading until either CFReadStreamHasBytesAvailablereturns false or CFReadStreamRead reads 0 bytes.