你应该如何处理NSStream堵塞的可能性?

时间:2012-01-06 15:47:56

标签: objective-c nsstream blocked

Per Apple的“轮询与运行循环调度”:

  

[hasSpace/BytesAvailable]可能意味着有可用的字节或空间,找出的唯一方法是尝试读取或写入操作(这可能导致瞬间块)。

doc没有明确声明hasSpace / BytesAvailable 事件的行为方式相同,只是模糊地说它们具有“相同的语义”。

我是否可以得出结论,写入/读取streamError或字节读/写返回值小于预期量可能是由于“瞬间阻塞”?

如果是这样,我应该再次尝试传输吗?我应该使用某种计时器机制来阻止机会清除吗?这将是很多工作要实施,所以我宁愿不要,如果它不太可能帮助。

(在这种情况下启动一个有限的轮询循环很有吸引力,比如说10次尝试的while循环,但是我不知道在运行中调度流的同时执行此操作是否安全循环,我无法测试它。)

2 个答案:

答案 0 :(得分:1)

这是套接字的好包装:https://github.com/robbiehanson/CocoaAsyncSocket

如果连接不可用,它将对读取进行排队。你没有提到你是使用UDP还是TCP,但是我怀疑你正在使用TCP,在这种情况下它会自己处理任何中断 - 只要连接没有被拆除。

答案 1 :(得分:1)

这是一个漫长的过程。以下是对此问题的一些跟进:

早期,我提出了维护和检查剩余缓存的想法,因为只有当进一步的反射表明输入流也会被阻塞时,这只会对输出流有效。

相反,我设置了空闲的while循环:

- (void) stream:(NSStream *)theStream handleEvent:(NSStreamEvent)eventCode {

    switch (eventCode) 
        // RECEIVING
    case NSStreamEventHasBytesAvailable: {
        if (self.receiveStage == kNothingToReceive)
            return;
        // Get the data from the stream. (This method returns NO if bytesRead < 1.)
        if (![self receiveDataViaStream:(NSInputStream *)theStream]) {
            // If nothing was actually read, consider the stream to be idling.
            self.bStreamIn_isIdling = YES;
            // Repeatedly retry read, until (1) the read is successful, or (2) stopNetwork is called, which will clear the idler.
            // (Just in case, add nil stream property as a loop breaker.) 
            while (self.bStreamIn_isIdling && self.streamIn) {
                if ([self receiveDataViaStream:(NSInputStream *)theStream]) {
                    self.bStreamIn_isIdling = NO;
                    // The stream will have started up again; prepare for next event call.
                    [self assessTransmissionStage_uponReadSuccess];
                }
            }
        }
        else
            // Prepare for what happens next.
            [self assessTransmissionStage_uponReadSuccess];
        break;
        // SENDING
    case NSStreamEventHasSpaceAvailable: 
        if (self.sendStage == kNothingToSend)
            return;
        if (![self sendDataViaStream:(NSOutputStream *)theStream]) {
            self.bStreamOut_isIdling = YES;
            while (self.bStreamOut_isIdling && self.streamOut) {
                if ([self sendDataViaStream:(NSOutputStream *)theStream]) {
                    self.bStreamOut_isIdling = NO;
                    [self assessTransmissionStage_uponWriteSuccess];
                }
            }
        }
        else
            [self assessTransmissionStage_uponWriteSuccess]; 
        break;
    // other event cases…

然后是时候通过“取消”按钮测试用户发起的取消。在同步的中途,Cocoa方面有一个暂停,等待用户输入。如果用户此时取消,Cocoa应用程序将关闭流并将其从runloop中删除,因此我预计连接的其他侧的流将生成NSStreamEventEndEncountered个事件,或许NSStreamEventErrorOccurred。但是,不,只有一个事件发生了,NSStreamEventHasBytesAvailable!去图。

当然,实际上没有任何“字节可用”,因为流已经在Cocoa端关闭,而不是写入 - 所以iOS端的流处理程序进入无限循环。不太好。

接下来,我测试了其中一个设备进入睡眠状态会发生什么。在用户输入暂停期间,我让iPhone通过自动锁定*进入睡眠状态,然后在Cocoa端提供用户输入。再次惊喜:Cocoa应用程序继续没有扰动到同步结束,当我醒来iPhone时,iOS应用程序也证明已经完成了同步的一面。

我的闲置循环修复了iPhone方面的打嗝吗?我投入了一个停止网络例程来检查:

if (![self receiveDataViaStream:(NSInputStream *)theStream])
    [self stopNetwork]; // closes the streams, etc.

同步仍然完成。没有打嗝。

最后,我测试了如果 Mac (Cocoa端)在暂停输入期间进入睡眠状态时发生了什么。这产生了一种向后冲击:在 Mac 侧收到了两个NSStreamEventErrorOccurred事件,之后不再可能写入输出流。在iPhone方面没有收到任何事件,但如果我测试了iPhone的流状态,它将返回5,NSStreamStatusAtEnd。

结论&amp;计划:

  • “临时区块”是独角兽。网络运行平稳或完全断开连接。
  • 如果确实存在临时块这样的事情,则无法将其与完全断开连接区分开来。对临时块而言似乎合乎逻辑的唯一流状态常量是NSStreamStatusAtEndNSStreamStatusError。但根据上述实验,这些表明断开连接。
  • 因此,我正在丢弃while循环,并且仅通过检查bytesRead / Written&lt;检测断开连接。 1。

*如果它从属于Xcode,iPhone将无法入睡。你必须直接从iPhone上运行它。