如何解决由HTTP持久连接造成的超时问题?

我一直在努力解决HTTP超时问题。 经过一个多月的调查,我很确定这是由HTTP持久连接错误造成的。 详情如下:

  1. 这是一个iOS应用程序。
  2. 大多数用户正在运行iOS 8。
  3. 我正在使用NSURLConnection
  4. iOS 8有一个已知的keep alive错误,但我的是一个不同的问题。 更具体地说,该错误导致NSURLErrorNetworkConnectionLost但我的错误是NSURLErrorTimedOut 。 但是,我不确定我的问题是否是由iOS 8的另一个bug引起的。
  5. 我的问题的行为:经过一段时间的使用 – 在一些HTTP请求成功发送并收到相应的响应之后 – 一个请求会导致NSURLErrorTimedOut ,以及所有跟随(不能太远离最后一个重用持久连接)请求会导致NSURLErrorTimedOut
  6. 一些工作的解决方法:
    1. 杀死并重新启动应用程序。
    2. 在iPhone上closuresWiFi连接强制使用3G / 4G。
    3. 打开空气模式,然后closures。
  7. 我的分析:从行为来看,这个问题似乎是由一个不好的持续连接引起的。 所有后续的请求继续使用这个持久连接,所以所有的失败与NSURLErrorTimedOut 。 从解决方法,我们可以看到所有这些工作,因为他们导致不良的持久连接被删除,并创build一个新的持久连接。

我的问题:

  1. 有没有其他人遇到过这个问题?
  2. 这是一个已知的iOS 8的错误吗?
  3. 这是由服务器的一些非常规configuration引起的吗? 我不控制服务器,但我知道他们使用nginx 1.6.1,他们的工程师正在与我一起调查这个问题。 我应该问他们什么信息?
  4. 有没有办法强制NSURLConnection不重用当前的持续连接,但创build一个新的,所以我可以解决这个问题后,我检测到我的代码?

更新:

我通过使用CFNetwork并直接控制Connection标题成功缓解了iOS 8上的这个问题。 但是,iOS9上这个问题似乎变得更糟了。

因为我希望苹果能够在iOS 9上修复它,所以我终于开了一个雷达: http : //www.openradar.me/22770738 。

如果你也遇到这个问题,请复制我的雷达,或者更好的是,如果你有一个更可靠的重现样本,你可以自己发射雷达。

经过2周的研究,我可以回答问题3和4:

  1. nginx的持久连接超时在服务器上设置为5秒,这不应该是原因。 服务器工程师发现这些超时请求实际上是正常接收和响应的。 所以这更可能是一个客户端问题。 由于我有一个最小的可重复的代码来排除我的代码作为原因,原因应该是在iOS中。
  2. 我发现的唯一方法是使用CFNetwork 。 系统会覆盖更高级别的API,如NSURLConnectionNSURLSessionConnection头。

同样的问题在这里,iOS只是试图在服务器放弃它之后重新使用连接。

为什么CFNetwork不够用

大约两年前,我转而使用CFNetwork来解决这个问题,但是最近我发现用CFNetwork API实现SSL固定是不可能的。 所以现在我正在考虑回到NSURLSession。

解决方法

经过一番挖掘,我发现系统不会重用NSURLSession的连接,所以在一段时间内创建新会话应该可以解决问题。

但是我也发现(至少在macOS上):NSURLSession所做的每个连接持续时间可以持续180秒,并且该连接没有被释放或重置会话关闭,所以您可能需要实现一些缓存机制以避免创建大量的连接在同一时间。

这是我目前使用的简单机制:

 @interface HTTPSession : NSObject @property (nonatomic, strong) NSURLSession * urlSession; @property (nonatomic, assign) CFTimeInterval flushTime; @end + (NSURLSession *)reuseOrCreateSession { static NSMutableArray<HTTPSession *> * sessions = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sessions = [NSMutableArray<HTTPSession *> array]; }); const CFTimeInterval serverTimeoutSeconds = 10; const CFTimeInterval systemTimeoutSeconds = 40; CFTimeInterval now = CFAbsoluteTimeGetCurrent(); HTTPSession * resultSession = nil; for (HTTPSession * session in sessions) { CFTimeInterval lifeTime = now - session.flushTime; if (lifeTime < serverTimeoutSeconds) { resultSession = session; break; } if (lifeTime > systemTimeoutSeconds) { resultSession = session; resultSession.flushTime = now; break; } } if (!resultSession) { resultSession = [HTTPSession new]; NSURLSession * session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; // setup session resultSession.urlSession = session; resultSession.flushTime = now; [sessions addObject:resultSession]; } return resultSession.urlSession; } 

如果您将时间戳添加到所有请求URL? 我认为这将使每个请求独特,也许iOS会建立新的连接,每次你发送请求(我不知道,需要尝试)