如何计算 iOS 中网络连接测试的有效负载大小和超时长度?
Posted
技术标签:
【中文标题】如何计算 iOS 中网络连接测试的有效负载大小和超时长度?【英文标题】:How to calculate payload size and timeout length for network connectivity test in iOS? 【发布时间】:2018-08-05 19:13:26 【问题描述】:我的应用程序提供了从我们的服务器下载 3430 张高分辨率图像的选项,每张图像的大小为 50k - 600k 字节。
最初的方法是只下载所有这些 - 但我们意识到这会产生很多 NSURLErrorTimedOut 错误并使我们的程序崩溃。我们现在已经实现了它,以便我们下载所有图像,但一次分批下载 100 个图像。
- (void)batchDownloadImagesFromServer:(BOOL)downloadHiResImages
[UIApplication sharedApplication].idleTimerDisabled = YES;
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
[self generateImageURLList:YES];
[leafletImageLoaderQueue removeAllObjects];
numberOfThumbnailLeft = [uncachedThumbnailArray count];
numberOfHiResImageLeft = [uncachedHiResImageArray count];
NSLog(@"DEBUG: In batchDownloadImagesFromServer numberOfThumbnailLeft %ul , numberOfHiResImageLeft %ul ", numberOfThumbnailLeft, numberOfHiResImageLeft);
numberOfImagesToDownload = numberOfThumbnailLeft;
if (downloadHiResImages)
numberOfImagesToDownload += numberOfHiResImageLeft;
if (numberTotalToDownload < 0)
numberTotalToDownload = numberOfHiResImageLeft;
int midBatchCt = 0;
// start where we stopped
NSArray *subArray;
NSRange batchRange;
batchRange.location = 0;//uncachedHiResIndex;
NSInteger uncachedNumber = [uncachedHiResImageArray count];
NSLog(@"uncachedHiResIndex and numberTotalToDownload: %d %d", uncachedHiResIndex, numberTotalToDownload);
if (uncachedHiResIndex >= numberTotalToDownload || batchRange.location >= uncachedNumber)
// we have reached the end of the uncached hires images
NSLog(@" END of download total to download=%ld , uncachedNumber=%ld, # not downloaded is %ld", (long)numberTotalToDownload, uncachedNumber, (long)numberFailedToDownload);
return;
if (batchRange.location+100 > uncachedNumber)
NSInteger imagesUntilEnd = uncachedNumber -1;
batchRange.length = imagesUntilEnd;
NSLog(@"this is uncached number: %d this is uncachedhiresindex:%d and this images until end:%d ", uncachedNumber, uncachedHiResIndex, imagesUntilEnd);
else
batchRange.length = 100;
NSLog(@" NEW RANGE is from %lul to %lul ", (unsigned long)batchRange.location, batchRange.length);
subArray = [uncachedHiResImageArray subarrayWithRange:batchRange];
if (downloadHiResImages)
for ( LeafletURL* aLeafletURL in subArray )
LeafletImageLoader* hiResImageLoader = [[LeafletImageLoader alloc] initWithDelegate:self];
[leafletImageLoaderQueue addObject:hiResImageLoader]; // do this before making connection!! //
[hiResImageLoader loadImage:aLeafletURL isThumbnail:NO isBatchDownload:YES];
//// Adding object to array already retains it, so it's safe to release it here. ////
[hiResImageLoader release];
midBatchCt++;
uncachedHiResIndex++;
if (midBatchCt == 10)
NSLog(@" Waiting for queued images to download...");
NSLog(@" REMOVED from QUEUE %lul , UncachedIndex %lul", numberRemovedFromQueue, uncachedHiResIndex);
break;
if ( [leafletImageLoaderQueue count] == 0 && numberRemovedFromQueue == numberTotalToDownload)
if([delegate respondsToSelector:@selector(requestDidBatchDownloadImages:)])
[delegate requestDidBatchDownloadImages:self];
这解决了我们的大部分问题。但是,我们甚至想在开始批量下载之前测试网络连接。我找到了一个low level ping library,它给出了准确的往返计时结果。使用来自 GBPing 的演示代码作为参考,在调用 batchDownloadImagesFromServer
之前,我编写了自己的代码来 ping 我们的服务器。
- (IBAction)preloadAll:(id)sender
self.ping = [GBPing new];
self.ping.host = kDefaultDataServer;
self.ping.delegate = self;
self.ping.timeout = 1;
self.ping.pingPeriod = 0.9;
// setup the ping object (this resolves addresses etc)
[self.ping setupWithBlock:^(BOOL success, NSError *error)
if (success)
// start pinging
[self.ping startPinging];
// stop it after 5 seconds
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^
NSLog(@"stop it");
[self.ping stop];
self.ping = nil;
);
else
UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"Internet Connection"
message:@"Not strong enough internet connection"
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* OKButton = [UIAlertAction
actionWithTitle:@"Ok"
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action)
[downloadManager batchDownloadImagesFromServer:YES];
];
[alert addAction:OKButton];
[self presentViewController:alert animated:NO completion:nil];
];
我对网络完全陌生。考虑到批量大小和图像大小,如何确定测试的有效负载大小和超时长度?
【问题讨论】:
【参考方案1】:超时长度是每个请求。这只是网络代码在放弃之前等待回复的时间。这不应该太短,但是对于大多数系统 API,超时长度大约是一分钟或更长,这可能太长了。
另外,请注意,如果连接不良,您仍然会收到超时错误,因此需要修复导致崩溃的任何原因。您必须能够从超时中恢复。
你没有提供太多关于你的崩溃的信息(它是什么类型的崩溃?你得到了什么回溯?),但我可以看到可能发生的三个明显的事情:
您在没有@autoreleasepool
块的情况下以紧密的循环进行下载。这意味着您下载的所有文件数据都会累积在 RAM 中并超出应用程序的内存限制。请务必将自动释放池置于长时间运行的循环中。
您在主线程上进行这些下载。主线程用于 UI 和短操作。如果您的主线程代码执行的时间超过几秒钟,UIApplication
将无法处理来自用户(和其他事件)的触摸事件,并且操作系统会将其视为无响应而将其关闭。将较长的操作卸载到调度队列(或使用其他方式将操作移出主线程,例如使用异步 API)。
您正在用请求淹没您的服务器,并且它内部的一些 DDoS 保护决定在几分钟内忽略您的请求,作为一种自我保护的形式。许多服务器对在给定时间段内从客户端接受的请求数量或客户端可以同时打开的连接数有限制。
我认为显示执行实际下载的代码会为您提供更好的服务。您不应该需要获得准确的 ping 时间信息来下载一堆图像文件。
假设上述一种或多种可能性为真,我建议您像这样实现您的下载代码:
创建需要下载的所有文件 URL 的列表。
编写代码,以便按顺序下载这些 URL。 IE。不要让它开始下载文件,直到前一个文件完成(或失败并且您决定暂时跳过它)。
使用NSURLSession
支持将单个文件下载到文件夹,不要使用代码获取 NSData 并自行保存文件。这样一来,您的应用程序就无需在下载完成时运行。
确保您可以判断文件是否已经下载,以防下载中断或手机在下载过程中重新启动。你可以例如通过比较它们的名称(如果它们足够独特),或者将注释保存到 plist 中,以便您将下载的文件与其来源的 URL 相匹配,或者在您的情况下构成识别特征的任何内容来做到这一点。
在启动时,检查是否所有文件都在那里。如果没有,把缺少的放到上面的下载列表中,依次下载,如#2。
在开始下载任何内容之前(包括在上一次下载完成或失败后下载下一个文件),请使用 Apple 的 SystemConfiguration.framework
中的可达性 API 进行可达性检查。这将告诉您用户是否有连接,以及您使用的是 WiFi 还是蜂窝网络(通常,您不希望通过蜂窝网络、大多数蜂窝网络连接下载大量文件计量)。
如果您的图像存储在单独的服务器上,或者它们相对较小,并且设置连接的开销比实际下载数据的开销更大,您可以修改代码以一次下载多个图像,但通常如果您是同时从服务器下载超过 4 张图片,您可能不会看到性能优势,因为每增加一张图片只会减少其他图片的可用带宽。
【讨论】:
您对如何按顺序下载它们有什么建议吗?我在目标 c 中找不到这方面的教程 有一个NSMutableArray<NSURL*>
,您可以在其中添加图像 URL。创建一个从数组中删除第一个 URL 并使用它开始下载的方法。在下载完成块中,如果数组尚未为空,则让完成块再次调用该方法以下载下一个。这样它就会下载东西,直到什么都没有,但一次只剩下一个。
另外,我认为您可以指定 dispatch_queue_t
或 NSOperationQueue
,NSURLSession
将使用它来进行下载。创建一个串行(非并行)队列,并将其传递给您的NSURLSession
,将所有 URL 排入队列,并且在前一个成功或失败之前不会再次尝试下载。
在这两种情况下,您可能希望添加代码,如果发生错误,将失败的 URL 添加到另一个数组并在延迟后重试,或者执行其他任何适当的操作(在某处的用户界面?)。以上是关于如何计算 iOS 中网络连接测试的有效负载大小和超时长度?的主要内容,如果未能解决你的问题,请参考以下文章