如何计算iOS中网络连通性测试的负载大小和超时长度?

How to calculate payload size and timeout length for network connectivity test in iOS?

我的应用程序提供了从我们的服务器下载 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];
        }

    }];
}

我对网络完全陌生。考虑到批量大小和图像大小,如何确定测试的负载大小和超时长度?

超时长度是每个请求。这正是网络代码在放弃之前等待回复的时间。这不应该太短,但对于大多数系统 API,超时长度大约为一分钟或更长时间,这可能太长了。

此外,请注意,如果连接不良,您仍然会收到超时错误,因此需要修复导致崩溃的任何原因。您必须能够从超时中恢复。

你没有提供太多关于崩溃的信息(这是什么类型的崩溃?你得到了什么回溯?),但我可以看到可能发生的三件明显的事情:

  1. 您在没有 @autoreleasepool {} 块的紧密循环中完成了下载。这意味着您下载的所有文件数据都在 RAM 中累积并超出了应用程序的内存限制。确保将自动释放池放在长 运行 循环中。

  2. 您是在主线程上进行这些下载的。主线程用于 UI 和短动作。如果您的主线程代码执行任何需要超过几秒的操作,UIApplication 将无法处理来自用户的触摸事件(以及其他事件),并且操作系统会认为它没有响应而将其关闭。将较长的操作卸载到调度队列(或使用其他方式将操作移出主线程,例如使用异步 API)。

  3. 您的服务器正在被请求淹没,而它内部的一些 DDoS 保护决定忽略您的请求几分钟作为一种自我保护形式。许多服务器对在给定时间段内从客户端接受多少请求,或者客户端可以同时拥有多少打开的连接有限制。

我认为显示执行实际下载的代码会更好。您不应该需要 获取准确的 ping 计时信息来下载一堆图像文件。

假设上述一种或多种可能性为真,我建议您像这样实现下载代码:

  1. 创建需要下载的所有文件 URL 的列表。

  2. 编写您的代码,使其按顺序下载这些 URL。 IE。在上一个文件完成之前不要让它开始下载文件(或者失败并且您决定暂时跳过它)。

  3. 使用NSURLSession支持将单个文件下载到文件夹,不要使用代码获取NSData并自行保存文件。这样,您的应用程序在下载完成时不需要 运行。

  4. 确保您可以判断文件是否已经下载,以防下载中断,或者 phone 在下载中途重新启动。你可以例如通过比较它们的名称(如果它们足够独特),或将注释保存到 plist 中,使您可以将下载的文件与其来源的 URL 相匹配,或在您的案例中构成识别特征的任何内容来执行此操作。

  5. 启动时,检查所有文件是否都在。如果没有,将缺少的放在上面的下载列表中,按顺序下载,如#2.

  6. 在开始下载任何内容之前(包括在上一次下载完成或失败后下载下一个文件),使用 Apple [=13] 的可达性 API 进行可达性检查=].这将告诉您用户是否有连接,以及您是使用 WiFi 还是蜂窝网络(一般来说,您 想通过蜂窝网络下载大量文件,大多数蜂窝连接都是按流量计费的)。

如果您的图像存储在不同的服务器上,或者它们相对较小并且设置连接的开销比实际下载数据的开销大,您可以修改代码以一次下载多张图像,但通常如果您如果同时从服务器下载超过 4 张图像,您可能看不到性能优势,因为每增加一张图像只会减少其他图像的可用带宽量。