How to solve timeout issues caused by bad HTTP persistent connection?

筅森魡賤 提交于 2019-12-03 03:22:36

After 2 weeks of research, I can give answers to question 3 and 4:

  1. nginx's persistent connection timeout is set to 5s on server, which should not be the cause. Server engineers found those timed-out requests are actually normally received and responded. So it is more likely a client side issue. Since I have a minimal reproducible code to rule out my code as the cause, the cause should be in iOS.
  2. The only way I found is to use CFNetwork. Higher level API such as NSURLConnection or NSURLSession's Connection header will be overwritten by system.

Same issue here, iOS just try to reuse the connection after server drops it.

Why CFNetwork is NOT Enough

About two years ago, I switched to CFNetwork to workaround this issue, but recently I found it's not possible to implement SSL pinning with CFNetwork API. So now I'm considering go back to NSURLSession.

The Workaround

After some dig around, I found system will NOT reuse connections across NSURLSessions, so creating new sessions within period of time, should solve the issue.

But I also found (at least on macOS): each connection made by NSURLSession can persistence last by 180 seconds, and that connection didn't close by release or reset the session, so you may need to implement some caching mechanism to avoid creating a lots of connections at the same time.

Here is the simple mechanism I'm currently use:

@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;
}

What if you add timestamp to all your request URL? I think this will make each request unique and maybe iOS will establish new connection every time you send request ( I'm not sure. Need trying )

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!