NSURLSession Exchange服务器拒绝多个并发请求

时间:2017-04-13 13:04:11

标签: ios concurrency exchangewebservices nsurlsession

我已将select t.* from t where not exists (select 1 from t t2 where t2.itemno = t.itemno and t2.itemmarket = 6 ); 代码替换为NSURLConnection以使用EWS Exchange服务器获取数据。我的应用程序进行多个并发API调用。它工作正常,但现在,当我使用NSURLSession时,我的一些API调用得到正确的响应,有些从Exchange服务器获取错误,如下所示:

NSURLSession

显然问题是同时连接太多。

我的代码流程是:

我的{ "s:Envelope" = { "s:Body" = { "m:GetItemResponse" = { "m:ResponseMessages" = { "m:GetItemResponseMessage" = { ResponseClass = Error; "m:DescriptiveLinkKey" = { text = 0; }; "m:Items" = { }; "m:MessageText" = { text = "An internal server error occurred. The operation failed., Cannot open mailbox /o=First Organization/ou=Exchange Administrative Group(FYDIBOHF23SPDLT)/cn=Recipients/cn=00037FFEE6F0D3D2."; }; "m:MessageXml" = { "t:Value" = ( { Name = InnerErrorMessageText; text = "Too many concurrent connections opened."; }, { Name = InnerErrorResponseCode; text = ErrorTooManyObjectsOpened; }, { Name = InnerErrorDescriptiveLinkKey; text = 0; } ); }; "m:ResponseCode" = { text = ErrorInternalServerError; }; }; }; "xmlns:m" = "http://schemas.microsoft.com/exchange/services/2006/messages"; "xmlns:t" = "http://schemas.microsoft.com/exchange/services/2006/types"; }; "xmlns:xsd" = "http://www.w3.org/2001/XMLSchema"; "xmlns:xsi" = "http://www.w3.org/2001/XMLSchema-instance"; }; "s:Header" = { "h:ServerVersionInfo" = { MajorBuildNumber = 1034; MajorVersion = 15; MinorBuildNumber = 11; MinorVersion = 1; xmlns = "http://schemas.microsoft.com/exchange/services/2006/types"; "xmlns:h" = "http://schemas.microsoft.com/exchange/services/2006/types"; "xmlns:xsd" = "http://www.w3.org/2001/XMLSchema"; "xmlns:xsi" = "http://www.w3.org/2001/XMLSchema-instance"; }; }; "xmlns:s" = "http://schemas.xmlsoap.org/soap/envelope/"; }; 有一个方法

HTTPRequest.m

我正在同时下载电子邮件:

- (void)fetchData
{
    dispatch_async(dispatch_get_main_queue(), ^{

        sessionAlive = sessionAlive + 1;

        NSLog(@"sessionCount: %ld", (long)sessionAlive);

        NSURLSessionConfiguration *defaultConfiguration = [NSURLSessionConfiguration ephemeralSessionConfiguration];

        NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration:defaultConfiguration
                                                                     delegate:self
                                                               delegateQueue:nil];

        NSURLSessionDataTask *dataTask = [defaultSession dataTaskWithRequest:request];

        [dataTask resume];
    });
}

// Some NSURLSession delegates methods here

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

-(void)URLSession:(NSURLSession *)session
             task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
    if (error)
    {
        self.failureBlock ? self.failureBlock(error) : nil;
    }
    else
    {
        NSData *data;

        if (self.data)
        {
            data = [NSData dataWithData:self.data];
        }

        self.successBlock ? self.successBlock(self.redirectLocation, data) : nil;
    }

    [session finishTasksAndInvalidate]; // We must release the session, else it holds strong referance for it's delegate (in our case EWSHTTPRequest).
                                        // And it wont allow the delegate object to free -> cause memory leak
}

我认为我的问题是我正在为每次通话进行会话,但我不知道其他方式。

如果我不能使用共享实例会话,因为我需要将会话的委托与每个HTTPRequest对象相关联以处理响应。

有任何建议或更好的方法吗?

2 个答案:

答案 0 :(得分:2)

为了最大限度地减少架构更改,我可能会建议一个管理会话的单例,但代理调用相关对象的委托。换句话说:

  1. 使用NSURLRequest在该单例类上调用方法并让它创建任务;将您的HTTPRequest对象也传递给该方法,以便它知道应该使用结果调用谁。
  2. 在单例类中,将新创建的NSURLSession[Data|Download|Upload]Task对象存储为字典中的键。
  3. 将您的HTTPRequest对象存储为相应的值。
  4. 编写一大堆每个任务委托方法,每个方法都在字典中查找任务,并在正确的HTTPRequest对象上调用相同的委托方法。
  5. 任务完成后,将其从字典中删除以释放HTTPRequest对象。
  6. 或者,如果所有HTTPRequest对象真正在做的是提供一堆身份验证处理方法并累积数据,您可以考虑创建一个单独的类,它采用NSURLRequest对象和块来代替,并在你获得数据时运行阻止。

    就此而言,如果您不是以递增方式处理数据,您可能还会考虑直接将该块作为任务的completionHandler块传递,并让会话本身处理数据累积。

答案 1 :(得分:1)

您可以根据需要使用任意数量的会话,但是如果您创建了太多与服务器的连接,我会提供:

它需要在某些单例中为您的请求提供共享操作队列,但是 而不是GCD使用主要或全局操作队列考虑使用NSOperationQueue和NSOperations。 NSOperationQueue可以限制可以在同一时间执行的任务数量。

var operationCount:Int当前队列中的操作数。

您可以限制同时连接的数量。 据我所知,dispatch_get_global_queue没有这种可能性,因为你只是使用外部现有队列而不能为它分配限制。

所以而不是

dispatch_async(backgroundQueue, ^{...

您可以使用相同的块创建NSBlockOperation,并将此操作添加到NSOperationQueue。如果您添加的任务多于' operationCount'只有' operationCount'请求将被发出。所有其他人将等待执行任务之一完成。

这不是完整的代码,而是您可以尝试的想法

 NSOperationQueue *backgroundOperationQueue = [[NSOperationQueue alloc] init];
 backgroundOperationQueue.operationCount = 5;
 NSOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:yourblock];
[backgroundOperationQueue addOperation:blockOperation];

如果你更喜欢GCD,考虑使用串行队列,它只允许一次任务。

dispatch_queue_t queue;
queue = dispatch_queue_create("com.example.MyQueue", NULL);