在单独的线程上进行基于 CFStream 的手动连接时(用于带宽监控和限制等自定义操作),我使用 CFReadStreamScheduleWithRunLoop、CFRunLoopRunInMode 和 CFReadStreamSetClient 的组合。基本上我跑了 0.25 秒,然后检查流状态。客户端回调也会自己收到通知。这使我可以定期检查读取状态并执行一些自定义行为,但主要依赖于(流)事件。
static const CFOptionFlags kMyNetworkEvents =
kCFStreamEventOpenCompleted
| kCFStreamEventHasBytesAvailable
| kCFStreamEventEndEncountered
| kCFStreamEventErrorOccurred;
static void MyStreamCallBack(CFReadStreamRef readStream, CFStreamEventType type, void *clientCallBackInfo) {
[(id)clientCallBackInfo _handleNetworkEvent:type];
}
- (void)connect {
...
CFStreamClientContext streamContext = {0, self, NULL, NULL, NULL};
BOOL success = CFReadStreamSetClient(readStream_, kMyNetworkEvents, MyStreamCallBack, &streamContext);
CFReadStreamScheduleWithRunLoop(readStream_, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
if (!CFReadStreamOpen(readStream_)) {
// Notify error
}
while(!cancelled_ && !finished_) {
SInt32 result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.25, NO);
if (result == kCFRunLoopRunStopped || result == kCFRunLoopRunFinished) {
break;
}
if (([NSDate timeIntervalSinceReferenceDate] - lastRead_) > MyConnectionTimeout) {
// Call timed out
break;
}
// Also handle stream status CFStreamStatus status = CFReadStreamGetStatus(readStream_);
if (![self _handleStreamStatus:status]) break;
}
CFRunLoopStop(CFRunLoopGetCurrent());
CFReadStreamSetClient(readStream_, 0, NULL, NULL);
CFReadStreamUnscheduleFromRunLoop(readStream_, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
CFReadStreamClose(readStream_);
}
- (void)_handleNetworkEvent:(CFStreamEventType)type {
switch(type) {
case kCFStreamEventOpenCompleted:
// Notify connected
break;
case kCFStreamEventHasBytesAvailable:
[self _handleBytes];
break;
case kCFStreamEventErrorOccurred:
[self _handleError];
break;
case kCFStreamEventEndEncountered:
[self _handleBytes];
[self _handleEnd];
break;
default:
Debug(@"Received unexpected CFStream event (%d)", type);
break;
}
}