iOS - 解锁后第一次网络请求失败

7
我有一个iOS应用程序,在applicationDidBecomeActive中从服务器拉取用户数据。
如果用户按下Home键,然后通过应用程序图标或多任务屏幕返回到应用程序,则功能正常运行。
如果用户在此期间锁定设备(无论是在应用程序中还是按Home键、然后锁定),则网络请求将失败。我正在使用Alamofire。响应对象为nilresult.data为空。服务器上也没有记录任何请求。失败会立即发生。
3个回答

7
Apple Tech Note TN2277指出,内核可以在应用程序毫不知情的情况下收回套接字。以下是相关部分:

测试套接字回收

如果你要编写处理内核回收套接字资源的代码,你必须想办法进行测试。系统可能回收套接字资源的确切情况没有被记录; 这使我们有了未来改进的灵活性。然而,在当前系统(iOS 4.0到iOS 4.3),您可以通过以下方式从您的应用程序中的套接字中获取系统回收资源:

  1. 将您的应用程序置于后台
  2. 确保应用程序已暂停
  3. 锁定屏幕

当您的应用程序恢复执行时,它会发现它的套接字已被回收。

为了解决这个问题,尝试在我的AppDelegate的applicationWillEnterForeground被调用时创建新的Alamofire.Manager。 希望这能帮到你。


但我没有在后台运行任何东西。这个网络请求是在applicationDidBecomeActive中发出的,所以在那个时候我的应用程序应该具有网络访问权限吧? - AndyRyan
我的猜测是,Alamofire.Manager 缓存的 NSURLSession 有一个或多个套接字保持打开状态(可能是为了避免重新协商 TLS 握手)。然后,当设备被锁定时,操作系统会回收这些套接字。对它们的下一个请求将导致系统调用返回 EBADF 错误号,该错误号被转换为带有本地化描述“连接已关闭”的 NSError。 - typedef
有任何想法我可以解决这个问题吗?我已经尝试寻找某种刷新函数。现在唯一的想法是重试请求。 - AndyRyan
我也遇到了同样的问题,有什么想法吗?Alamofire.Manager()不起作用。 - Wraithseeker
1
一个解决方案是每次应用程序从后台返回时都使用一个新的管理器,这对我有效。 - Roberto Frontado
在iOS 11.4和12中,套接字回收更加积极。当您的应用程序被挂起时,它会触发。 - Teffi

2
在AppDelegate的applicationWillEnterForeground方法中重新初始化您的网络管理器类,这将解决问题。出现此问题的原因是,当您锁定设备时,操作系统会锁定套接字,并在解锁手机时不释放它。因此,请重新初始化您的网络管理器类,它将解决该问题。
     func applicationWillEnterForeground(_ application: UIApplication) {
            WebServiceManager.sharedInstance.sessionManr = Alamofire.SessionManager.default
            WebServiceManager.sharedInstance.sessionManr.session.configuration.requestCachePolicy = .reloadIgnoringLocalAndRemoteCacheData

            WebServiceManager.sharedInstance.sessionManr.session.configuration.urlCache = nil
            WebServiceManager.sharedInstance.sessionManr.session.configuration.timeoutIntervalForRequest =  180 //40
            WebServiceManager.sharedInstance.sessionManr.session.configuration.httpShouldSetCookies = true
     }

0
我即使没有使用Almofire也遇到了类似的问题。有时候在解锁后的第一个请求中会出现错误-1005 NSURLErrorNetworkConnectionLost(有些用户是-1001 NSURLErrorTimedOut)。
在日志中我可以看到:
[connection] nw_read_request_report [C1] Receive failed with error "Software caused connection abort"

对我来说,解决方案是等待应用程序处于活动状态(applicationDidBecomeActive通知)。我之前是通过Timer启动请求,但计时器不在后台运行,而且暂停的计时器会在应用程序再次运行之后很快触发,甚至早于willEnterForeground通知。

推迟网络调用后,我仍然可以在日志中看到以下错误:

[connection] nw_flow_add_write_request [C1 A.B.C.D:443 failed parent-flow (satisfied (Path is satisfied), interface: en0[802.11], ipv4, dns)] cannot accept write requests
[connection] nw_write_request_report [C1] Send failed with error "Socket is not connected"

然而,那些并没有破坏我的代码。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接