【问题标题】:CFReadStreamRead blocking forever under iOS 7CFReadStreamRead 在 iOS 7 下永远阻塞
【发布时间】:2013-09-24 15:42:33
【问题描述】:

我看到一个问题,其中CFReadStreamRead,作为流式文件上传的一部分,从不返回。

这种情况似乎只发生在 iOS7 上 — 在物理设备上进行调试时比在模拟器中更频繁 — 或者至少,在那里更为明显。

我们有一个文件的 HTTP(或 HTTPS,问题发生在任何一种方式,使用本地托管或远程服务器)POST,通过直线阻塞(非事件驱动)CFNetwork 调用。这是 C 代码调用这个处理程序的必要条件;没有回调的规定。

很好,网络调用发生在后台线程和/或通过异步调度。

有问题的网络代码归结为(为简洁起见,删除错误处理):

CFReadStreamRef upload = CFReadStreamCreateWithFile(
  kCFAllocatorDefault, upload_file_url);
CFRelease(upload_file_url);
CFReadStreamOpen(upload);

CFReadStreamRef myReadStream = CFReadStreamCreateForStreamedHTTPRequest(
  kCFAllocatorDefault, myRequest, upload);

CFReadStreamOpen(myReadStream);

CFIndex numBytesRead = CFReadStreamRead(myReadStream, buf, sizeof(buf));

// etc.

就其本身而言,这段代码想在 iOS7 下立即挂起。如果我在它之前添加一个对usleep 的调用的循环(沿途检查CFReadStreamHasBytesAvailable),它将几乎总是成功。每隔几百次尝试,它仍然会失败,永远不会返回。同样,主线程不受影响。

我希望 GM 能清除这种行为,但它仍然存在。

添加一个 runloop/callback 方法来监视字节可用事件没有任何效果 - 当调用挂起时,也看不到任何事件。

关于为什么会发生这种情况或如何防止这种情况的任何建议?还有其他人在 iOS 7 下看到不同的 CFReadStream 行为吗?

【问题讨论】:

  • 我有类似的问题:如果客户端在 iOS 7 下运行,服务器挂起并且客户端永远不会得到 HTTP 响应。iOS 3-4-5-6 很好。客户端使用 NSUrlConnection。任何人:请分享您的观察结果。
  • 函数阻塞的原因有据可查。所以,第一个问题:你确定文件没有被完全读取并且它在中间的某个地方阻塞吗?其次,你确定文件已经打开成功了吗?
  • 这两件事都是肯定的,并且完全期望该功能在需要时阻止。是的,(省略的)错误处理检查文件是否已正确打开。
  • 您可能会添加更多代码。我假设您使用CFReadStreamCreateForStreamedHTTPRequestCFReadStream 绑定到请求正文。我与 CFNetwork 合作的不多,但NSMutableURLRequest 需要一个未打开的NSInputStream(当设置为 HTTPBodyStream 时)。所以我想知道您的 CFReadStream 在使用CFReadStreamCreateForStreamedHTTPRequest() 设置之前是否必须打开。
  • 文件读取流(上传)在调用CFReadStreamCreateForStreamedHTTPRequest之前打开; myReadStream 直到之后才存在。请注意,这不是“嘿,我的代码怎么不起作用?”它是“除非我先睡一段不确定的时间,否则我以前快乐的(并且非常基于 Apple 的示例)代码现在怎么会挂起?”

标签: ios ios7 cfnetwork cfreadstream


【解决方案1】:

我尝试过这种讨厌的解决方法,它对我有用,问题是我正在从服务器请求增量值,所以如果出现问题,我只是获取新的增量值,一般情况下它不会工作(在日志中我看到有时会出现超时)。至少这可以防止形成永久线程阻塞,并有机会以某种方式处理这个问题:

NSInteger readStreamReadWorkaround(CFReadStreamRef readStrem, UInt8 *buffer, CFIndex bufferLength) {
  static dispatch_once_t onceToken;
  static BOOL isProblematicOs = YES;
  dispatch_once(&onceToken, ^{
    isProblematicOs = [[UIDevice currentDevice].systemName compare: @"7.0" options: NSNumericSearch]!=NSOrderedAscending;
  });

  NSInteger readBytesCount = -2;

  if (isProblematicOs) {
    CFStreamStatus sStatus = CFReadStreamGetStatus(readStrem);
    NSDate *date = [NSDate date];
    while (YES) {
      if(CFReadStreamHasBytesAvailable(readStrem)) {
        readBytesCount = CFReadStreamRead(readStrem, buffer, bufferLength);
        break;
      }
      sStatus = CFReadStreamGetStatus(readStrem);
      if (sStatus!=kCFStreamStatusOpen && sStatus !=kCFStreamStatusAtEnd
          || [date timeIntervalSinceNow]<-15.0) {
        break;
      }
      usleep(50000);
    }
  } else {
    readBytesCount = CFReadStreamRead(readStrem, buffer, sizeof(buffer));
  }
  return readBytesCount;
}

我不喜欢这个解决方案,但到目前为止我还没有看到替代方案。

【讨论】:

  • 这与问题中提到的我使用的解决方法基本相同。是的,它仍然不总是有效。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多