代码之家  ›  专栏  ›  技术社区  ›  maddie

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

  •  0
  • maddie  · 技术社区  · 6 年前

    我的应用程序提供了从我们的服务器下载3430个高分辨率图像的选项,每个图像大小为50k-600k字节。

    最初的方法是下载所有这些文件,但我们意识到这会导致很多nsurlertimedout错误并使我们的程序崩溃。我们现在已经实现了它,这样我们就可以下载所有的图像,但一次只能下载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的演示代码作为参考,我在调用之前编写了自己的代码来ping服务器 batchDownloadImagesFromServer .

    - (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 回复  |  直到 6 年前
        1
  •  1
  •   uliwitness    6 年前

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

    另外,请注意,如果连接不好,仍然会出现超时错误,因此无论是什么原因导致的崩溃都需要修复。你必须能从暂停中恢复过来。

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

    1. 你的下载是在没有 @autoreleasepool {} 挡住里面。这意味着你下载的所有文件数据都累积在RAM中,并超出了应用程序的内存限制。请确保将自动释放池放在长时间运行的循环中。

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

    3. 您的服务器中充斥着请求,其中的一些DDoS保护决定在几分钟内忽略来自您的请求,作为一种自我保护。许多服务器对在给定的时间段内他们将从客户端接受多少请求,或者一个客户端可能同时有多少打开的连接有限制。

    我认为通过显示执行实际下载的代码可以更好地为您服务。你不应该 需要 获取精确的ping定时信息,下载一堆图像文件。

    假设上述一个或多个可能性是真的,我建议您实现如下下载代码:

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

    2. 编写代码,以便按顺序下载这些url。也就是说,不要让它开始下载一个文件,直到前一个文件完成(或失败,你决定暂时跳过它)。

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

    4. 确保您可以判断文件是否已下载,以防下载中断,或手机在下载过程中重新启动。例如,您可以通过比较它们的名称(如果它们足够独特)或将注释保存到plist,plist允许您将下载的文件与它来自的URL相匹配,或者在您的案例中构成识别特征的任何内容。

    5. 启动时,检查所有文件是否都存在。如果没有,把丢失的放在上面的下载列表中,然后按顺序下载,如#2所示。

    6. 在开始下载任何东西(包括在上一次下载完成或失败后下载下一个文件)之前,请使用Apple的 SystemConfiguration.framework . 这将告诉你用户是否有连接,以及你是在WiFi还是手机上(一般来说,你有 要通过蜂窝网络下载大量文件,大多数蜂窝网络连接都是按流量计费的)。

    如果您的图像存储在单独的服务器上,或者它们比较小,并且建立连接的开销比实际下载数据的开销更大,那么您可以修改代码以一次下载多个图像,但通常情况下,如果您同时从一个服务器下载4个以上的图像,您可能看不到性能上的好处,因为每个额外的映像只会减少其他映像可用的带宽。