使用 NSURLSessionDownloadTask 设置并发下载次数

Posted

技术标签:

【中文标题】使用 NSURLSessionDownloadTask 设置并发下载次数【英文标题】:Set number of concurrent downloads with NSURLSessionDownloadTask 【发布时间】:2014-01-02 17:58:14 【问题描述】:

我正在使用新的 NSURLSession API 并允许用户下载文件。我想尝试告诉我的 NSURLSession 要同时运行多少次下载,但我看不到这样做的方法。我想尽量避免自己管理下载任务,如果我可以告诉系统允许多少个下载任务会更好 - 当我的应用程序未运行时,这对于排队后台下载也更好。有没有办法做到这一点?

【问题讨论】:

好问题!是时候赏金了? 【参考方案1】:

您可以使用HTTPMaximumConnectionsPerHost 属性在NSURLSessionConfiguration 对象中设置它。

【讨论】:

虽然这确实有效,但系统仍然不会像我想要的那样排队下载。如果我将最大值设置为 3 并选择 5 次下载开始,最后 2 次最终会在前两次运行时超时。有没有办法让系统排队下载? 在 NSURLSessionConfiguration 你可以设置 两个 超时:一个是指定你接受等待整个资源完成的最大持续时间:timeoutIntervalForResource - 另一个是相关的到网络丢失:timeoutIntervalForRequest,仅当底层网络在此期间没有收到更多 new 数据时才会触发。您必须将timeoutIntervalForResource 设置为适当的持续时间,其中包括下载上述资源所需的时间。 这是一个愚蠢的设计实现。 timeoutIntervalForResource 不应包括先前资源下载的时间。如果不知道前面有多少资源怎么办!? HTTPMaximumConnectionsPerHost 不会只限制每个主机的最大连接数,而不是整体? @Tommy 没错,HTTPMaximumConnectionsPerHost 限制了某个主机的最大同时连接数。如果您想将每个应用程序的连接限制为 any 主机,也有一些方法可以实现这一点。一种解决方案是使用NSOperationQueue - 这需要您将您的URL 任务封装到NSOperation 中(这很详细)。其他解决方案可能涉及实用程序库,您可以在其中将 异步任务(带有完成处理程序的闭包,或返回未来的闭包)排入可以并行执行一组任务的特殊队列中。 【参考方案2】:

我找到了解决此超时的方法。

尝试在设备上下载具有慢速连接模拟的文件(设置 -> 开发人员 -> 网络链接调节器 -> 选择配置文件 -> 3G -> 启用)。

这是我的示例代码:

- (void) methodForNSURLSession
  NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
  _tasksArray = [[NSMutableArray alloc] init];
  sessionConfig.HTTPMaximumConnectionsPerHost = 3;
  sessionConfig.timeoutIntervalForResource = 120;
  sessionConfig.timeoutIntervalForRequest = 120;
  NSURLSession* session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil];

  // data tasks
  [self createDownloadTasksWithSession:session];



- (void) createDownloadTasksWithSession:(NSURLSession *)session
  for (int i = 0; i < 100; i++) 
    NSURLSessionDownloadTask *sessionDownloadTask = [session downloadTaskWithURL: [NSURL URLWithString:@"https://discussions.apple.com/servlet/JiveServlet/showImage/2-20930244-204399/iPhone%2B5%2BProblem2.jpg"]];
    [_tasksArray addObject:sessionDownloadTask];
    [sessionDownloadTask addObserver:self forKeyPath:@"countOfBytesReceived" options:NSKeyValueObservingOptionOld context:nil];
    [sessionDownloadTask resume];
  


- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
  if([[change objectForKey:@"old"] integerValue] == 0)
    NSLog(@"task %d: started", [_tasksArray indexOfObject: object]);
  


- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
    if (!error) 
      NSLog(@"task %d: finished!", [_tasksArray indexOfObject:task]);
     else if (error.code == NSURLErrorTimedOut) 
      NSLog(@"task %d: timed out!", [_tasksArray indexOfObject:task]);
    

我的输出:

2014-01-10 10:38:48.769 TestApplication[2442:1803] task 1: started
2014-01-10 10:38:49.517 TestApplication[2442:1803] task 2: started
2014-01-10 10:38:50.273 TestApplication[2442:4b03] task 0: started
2014-01-10 10:40:11.794 TestApplication[2442:5003] task 2: finished!
2014-01-10 10:40:13.924 TestApplication[2442:1803] task 3: started
2014-01-10 10:40:26.221 TestApplication[2442:1d0f] task 1: finished!
2014-01-10 10:40:28.487 TestApplication[2442:1d0f] task 4: started
2014-01-10 10:40:43.007 TestApplication[2442:440f] task 5: timed out!
2014-01-10 10:40:43.009 TestApplication[2442:440f] task 6: timed out!
2014-01-10 10:40:43.011 TestApplication[2442:440f] task 7: timed out!
...

如您所见,任务在 2 分钟后开始超时

我使用了timeoutIntervalForResource 和 timeoutIntervalForRequest 参数,如果我们将两者都设置为 0,它将下载而不会超时。但我认为这不是一个好主意,因为电池耗电。我认为 10 分钟或类似的时间将是一个很好的价值。但是您必须将两个参数设置为相同的值。

苹果文档:

timeoutIntervalForRequest - 等待时使用的超时间隔 获取更多数据。 timeoutIntervalForResource - 最大数量 应允许资源请求占用的时间。(超时 所有任务到一个资源)

注意到奇怪的事情:如果我们设置timeoutIntervalForResource = 60timeoutIntervalForRequest = 30,任务将在30 秒后超时!但他们中的大多数甚至都不会开始!

看起来timeoutIntervalForRequest 的计时器在任务恢复时启动。在这种情况下,我们同时恢复了所有任务,并且每个任务的超时都必须作为资源超时。

另外,我可以向wwdc13 705 session 提供有关下载任务的后台会话的精彩演示。

【讨论】:

我刚刚在我的 UI 中注意到它,每个单元格都有一个下载进度指示器,服务器的连接限制为 3。如果我将其设置为 3 以匹配并点击下载 5 个项目,第一个三个运行,最后两个最终超时。我更担心应用程序何时未运行,我知道我可以排队下载系统以在后台运行,我担心它会尝试全部运行并超时,类似于它的方式在应用程序运行时。不确定测试该部分的最佳方法。 文档说每次接收到数据时都会重置 timeoutIntervalForRequest ——所以只要继续滴入数据,该连接就会保持活动状态。但是, timeoutIntervalForResource 是请求的总超时。 DOCS:“资源计时器在请求启动时启动并计数,直到请求完成或达到此超时间隔,以先到者为准。”请求的默认值为 60 秒,资源的默认值为 7 天。

以上是关于使用 NSURLSessionDownloadTask 设置并发下载次数的主要内容,如果未能解决你的问题,请参考以下文章

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇

Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)

MySQL db 在按日期排序时使用“使用位置;使用临时;使用文件排序”

使用“使用严格”作为“使用强”的备份

Kettle java脚本组件的使用说明(简单使用升级使用)