2015-11-25 22 views
2

我正在使用NSURLSession API异步地从互联网上下载两种类型的csv数据(称为类型A和类型B)的OS X(Yosemite)应用程序。每种类型的csv都有多个请求。每个请求都是自定义类中包含的专用会话。有一个基类请求类和每个类型的子类。 (事后看起来可能不是一个理想的设计,但我认为我的问题无关紧要)。为什么我有时会遇到并发的NSURLSession请求的破坏回复

该应用程序的构建方式使得每种类型的csv数据都以顺序队列的形式下载。每种类型只有一个请求可以同时处于活动状态,但这两种类型都可以同时发生,并且两者都使用主线程用于委托回调。所有这一切通常很好。

我看到的问题是,有时在交通拥堵的情况下,我得到了“交叉听力”,即我有时会得到一个回应B类请求的响应,该类型B请求报告已成功完成,但它包含一些B类CVS线然后有一些类型的A行标记后 - 所以我有时(很少)在我的类型B请求中获得类型A数据。 (或其他方式)。

基本上,它看起来像Apples API中的“切换”逻辑会对哪个传入数据包属于哪个请求/会话感到困惑。两种不同的请求类型转到不同的URL,但它们是相关的,可能它们最终都解析为相同的IP,但我不确定这一点。我想知道,如果它们来自同一个服务器,那么它们可能与数据包标题有关,从而难以确定它们属于哪个请求(我在互联网协议上不够好,不知道这是否是一个明智的猜测) 。如果是这种情况,那么解决方案必须确保所有请求都在一个队列中,以使它们不能同时处于活动状态,但我不想在执行大型体系结构更改之前,我确信没有其他解决方法。

我寻找类似的问题,发现这个老问题(Why is my data getting corrupted when I send requests asynchronously in objective c for iOS?)似乎描述了完全相同的问题,但不幸的是它没有答案。除此之外,我没有发现任何类似的东西,所以我想我在这里做了一些愚蠢的事情,但是在我开始改变架构来修复它之前,知道为什么会出现这个问题会很好。

有没有人看过这个,知道什么原因和解决方法是?

我没有包含任何代码,因为我觉得没有意义,因为它似乎是一个架构问题,如果我添加代码,它将需要很多。不过,如果能帮助我们理解这个问题,我会很乐意补充您的建议。

编辑:

相关(我希望)代码添加到下面。注意对象只有一个镜头。该请求的参数由init方法注入,NSURLSession仅用于单个任务。因此,会话在启动后失效,并在解析数据后释放NSMutableData数组。

-(BOOL)executeRequest { 
    NSURLSessionConfiguration *theConfig = [NSURLSessionConfiguration ephemeralSessionConfiguration]; 
    NSURLSession *theSession = [NSURLSession sessionWithConfiguration:theConfig delegate:self delegateQueue:[NSOperationQueue mainQueue]]; 
    NSURLRequest *theRequest = [NSURLRequest requestWithURL:self.queryURL cachePolicy: NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:BSTTIMEOUT]; 
    NSURLSessionDataTask *theTask = [theSession dataTaskWithRequest:theRequest]; 
    if(!theTask) { 
     return NO; 
    } 
    [theTask resume]; 
    [theSession finishTasksAndInvalidate]; 

    self.internetData = [NSMutableData dataWithCapacity:0]; 

    return YES; 
} 

-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { 
    [self.internetData appendData:data]; 
    return; 
} 

-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { 
     if((error)||(![self parseData])) 
     { 
      self.internetData = nil; 

      if(!error) { 
       NSDictionary *errorDictionary = @{ NSLocalizedDescriptionKey : @"Parsing of internet data failed", NSLocalizedFailureReasonErrorKey : @"Bad data was found in received buffer"}; 
       error = [NSError errorWithDomain:NSCocoaErrorDomain code:EIO userInfo:errorDictionary]; 
      } 
      NSDictionary* ui = [NSDictionary dictionaryWithObject:error forKey:@"Error"]; 
      [[NSNotificationCenter defaultCenter] postNotificationName:[self failNotification] object:self userInfo:ui]; 
      return; 
     } 

     [[NSNotificationCenter defaultCenter] postNotificationName:[self successNotification] object:self]; 
     return; 
} 
+0

请添加一些代码,特别是处理数据接收的代码('-connection:didReceiveData:')。 –

+0

更新:我实现了一个请求队列,可以将csv请求串行化,以便一次只能有一个活动。这似乎解决了我的问题,并且性能可以接受,因为请求数量并不那么高,也没有时间限制。然而,主要问题(为什么会发生这种情况?)仍然未知,因此我不会将此作为答案发布。 – pco494

+0

我终于找到了解决方案,并将其作为未来参考的答案发布。 – pco494

回答

0

我终于设法追查我的(愚蠢的)错误。为了将来的参考,这个问题是由于没有意识到返回的数据不是零终止而引起的。

在大多数情况下,我请求的数据是XML和NSXMLParser类想要一个NSData而无需额外的尾随零,这样效果很好。

但偶尔失败的请求使用CSV格式,其中数据通过NSString,这是由[NSString stringWithUTF8String]创建的,其中期望以零终止的c样式字符串作为输入。这是主要的罪魁祸首。通常它应该如此。有时它直接失败,有时它只是做了一个缓冲区溢出并获得了一些在同一个内存区域的请求数据。这些是我在发布问题时注意到的情况。

因此,解决方案是切换到使用[[NSString alloc] initWithData: encoding:NSUTF8StringEncoding],它与非空终止的NSData缓冲区一起使用。

0

首先:你不应该为每个请求创建一个新的会话。这不再是会话。从docs

随着NSURLSession API,你的应用程序创建一个或多个会话,每个坐标一组相关的数据传输任务。例如,如果您正在编写Web浏览器,则您的应用程序可能会为每个选项卡或窗口创建一个会话,或者一个会话用于交互式使用,另一个会话用于后台下载。在每个会话中,您的应用程序会添加一系列任务,每个任务都表示对特定URL的请求(如果需要,请在HTTP重定向之后)。

第二:你在哪里存储会话等,所以它不会被释放?

您的主要问题:很显然,当请求可能正在运行时,您会启动新的请求。但是您只有一个NSMutableData实例可以接收-URLSession:task:didReceiveData:中的数据:许多请求,一个存储......当然这会混合起来。

+0

谢谢您的回复,但我不完全明白您的意思。我的印象是NSURLSession是建立网络连接的常用方式。请你澄清一下。此外,我意识到我的问题并不清楚。每个请求都在它自己的对象实例中,它拥有自己的NSURLSession,并且还将NSMutableData缓冲区作为一个属性保存,所以我不认为这些不同的请求会压制其他每个数据。这些对象只会触发一次,因此不可能每个请求都发起多个请求。 – pco494

+0

第一部分:请参阅我的编辑。第二个:好吧,我真的没有意识到每个请求都有一个新的实例。然而,在这种情况下,我会在'-executeRequest'的开始处检查是否已经有一个正在进行的请求,或者 - 更好地为实例提供一个完全参数化的初始化器,并立即执行请求。 –

+0

谢谢,是的,我开始请求威胁其他人正在运行,但从来没有在同一个NSURLSession。文档似乎表明这是意图,例如他们在Web浏览器中每个选项卡讨论一个会话。然而由于某种原因,我得到的答复感到困惑,以至于我得到应答的一部分,它应该发送到会话B中的会话A.仍然如您所说,我通过为所有对象实现顺序队列来解决此问题。仍然为什么答复的混淆发生仍然不清楚我会调查,如果我不小心可能创建一些共享会话的引用。 – pco494

相关问题