使用nsurlsession在for循环中多次访问Web服务时出错

时间:2014-12-17 01:01:47

标签: ios nsurlsession

我需要从服务器获取图像信息,例如图像名称,图像ID。然后使用image id作为参数之一进行发布,获取图像实际数据。更具体地说,我应该得到三个图像。

首先,我使用getImageInfo来获取图像信息。

- (void)getImageInfo {
// compose request
NSUserDefaults *getUserInfo = [NSUserDefaults standardUserDefaults];
NSString *uid = [getUserInfo objectForKey:@"uid"];
NSString *checkCode = [getUserInfo objectForKey:@"checkCode"];
NSString *data = [NSString stringWithFormat:@"uid=%@&yangzhengma=%@", uid, checkCode];

NSURL *url = [NSURL URLWithString:@"http://121.199.35.173:8080/xihuan22dcloud/services/Shibietupianservice/serviceGetallshibietu"];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPBody = [data dataUsingEncoding:NSUTF8StringEncoding];
request.HTTPMethod = @"POST";

[[self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){

    if (!error) {

        NSHTTPURLResponse *httpResp = (NSHTTPURLResponse*) response;
        if (httpResp.statusCode == 200) {

            // parse data in ram and put into images' imageInfos array
            [self.images parseImageInfo:[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]];

            [self getImageRawData];

            dispatch_async(dispatch_get_main_queue(), ^{
                [self.tableView reloadData];
            });
        }
    }
}] resume];}

然后我使用getImageRawData来获取三个图像数据。

- (void)getImageRawData {
// compose request dynamically
NSUserDefaults *getUserInfo = [NSUserDefaults standardUserDefaults];
NSString *uid = [getUserInfo objectForKey:@"uid"];
NSString *checkCode = [getUserInfo objectForKey:@"checkCode"];
NSURL *url = [NSURL URLWithString:@"http://121.199.35.173:8080/xihuan22dcloud/services/Shibietupianservice/serviceGetthetupian"];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
NSInteger count = 0;

for (ImageInformation *temp in self.images.imageInfos) {
    NSString *data = [NSString stringWithFormat:@"uid=%@&yangzhengma=%@&tupianid=%@", uid, checkCode, temp.imageId];

    request.HTTPBody = [data dataUsingEncoding:NSUTF8StringEncoding];[[self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){

        // if client side is no errors, continue
        if (!error) {

            // if server side is no errors, continue
            NSHTTPURLResponse *httpResp = (NSHTTPURLResponse*) response;
            if (httpResp.statusCode == 200) {

                NSLog(@"图片内容:%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);

                // in ram and put into images' imageRawData array
                [self.images parseImageRawData:[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] withImageId:temp.imageId withIndex:count];

                // store data to disk
                //                    NSString *path = [[NSString alloc] initWithFormat:@"image%@", temp.imageId];
                //                    [FCFileManager writeFileAtPath:path content:data];

                dispatch_async(dispatch_get_main_queue(), ^{
                    [self.tableView reloadData];
                });
            }
        }
    }] resume];

    count++;
}}

在这里,它将循环三次,三个响应返回,只有最后一个完成,其他包含错误消息,或者有时不完整的原始数据。现在我正在深入研究并发编程指南,我猜串行队列可能会解决这个问题。

输出如下:

2014-12-16 22:38:48.739 WeddingNewVersion[997:83366] 图片内容:<ns:serviceGetthetupianResponse xmlns:ns="http://serviceimpl.my.com"><ns:return>error</ns:return></ns:serviceGetthetupianResponse>
2014-12-16 22:38:48.749 WeddingNewVersion[997:83366] 图片内容:<ns:serviceGetthetupianResponse xmlns:ns="http://serviceimpl.my.com"><ns:return>error</ns:return></ns:serviceGetthetupianResponse>
2014-12-16 22:38:51.943 WeddingNewVersion[997:83366] 图片内容:<ns:serviceGetthetupianResponse xmlns:ns="http://serviceimpl.my.com"><ns:return>/9j/...(complete data)...9k=%%226654474.0</ns:return></ns:serviceGetthetupianResponse>

请求参数:

2014-12-17 14:59:25.364 WeddingNewVersion[1875:226651] uid=6&yangzhengma=odWoDXWcBv1jOrEhywkq7L&tupianid=41
2014-12-17 14:59:25.368 WeddingNewVersion[1875:226651] uid=6&yangzhengma=odWoDXWcBv1jOrEhywkq7L&tupianid=42
2014-12-17 14:59:25.368 WeddingNewVersion[1875:226651] uid=6&yangzhengma=odWoDXWcBv1jOrEhywkq7L&tupianid=43

问题可能不在撰写请求中。

----------------------------------------------- -update1 ----------------------------------------------- < / p>

我试图将会话的数据任务放入串行队列。失望,这不起作用。

        dispatch_async(self.serialQueue, ^{
        [[self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){...}] resume];
    });

同时,我将session的delegateQueue设为nil,引用说如果为nil,则会话创建一个串行操作队列,用于执行所有委托方法调用和完成处理程序调用。

现在我仍然很困惑如何做对。

----------------------------------------------- UPDATE2 ------------------------------------------------ < / p>

我将[NSThread sleepForTimeInterval:0.5]添加到分派到串行队列的块中。

        dispatch_async(self.serialQueue, ^{
        [[self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){...}] resume];
        [NSThread sleepForTimeInterval:0.5];
    });

它不起作用。这三个答案是完整的,但它们都是一样的。

提前谢谢!

3 个答案:

答案 0 :(得分:0)

我只是在猜测,因为我从未尝试过,但可能你的数据任务都使用了相同的TCP端口。

如果将它们序列化,那将是正常的 - 按顺序依次排列 - 但如果它们重叠,那么服务器将收到乱码的HTTP请求:

GET /foo
GET /bar
GET /baz

服务器会看到的内容可能是:

GET /fGET /baroo
GET /baz

你的第三个请求确实有效可能是时间上的意外。

如果您绝对要求同时发出三个请求,可以通过多种方式打开三个不同的端口。我不知道如何使用Cocoa和Objective-C,但你可以使用C和Berkeley Socket系统调用来做到这一点。 Cocoa / Cocoa Touch网络方法只是套接字的包装。

答案 1 :(得分:0)

有几点想法:

  1. 您使用单个NSMutableURLRequest实例的技巧,并为每个请求重复变更(先前的请求仍在进行中),这很奇怪。

    本着线程安全的精神,我会为每个并发请求使用单独的NSMutableURLRequest。您不希望冒险让发出这些请求的线程改变请求对象,而某些后台线程执行其中一个先前请求。 (参见线程编程指南中的Apple Thread Safety Summary,其中指出可变类通常不是线程安全的。)

    话虽如此,NSURLConnection文档给我们的印象是这个请求对象会被复制,从而缓解了这个问题。我在NSURLSession文档中没有看到这种保证(虽然我怀疑它做同样的事情)。

    我不认为这是问题所在(如果这是问题,问题可能比你报告的问题更不稳定,而且,我怀疑NSURLSession正在优雅地处理这个问题,无论如何),但作为良好的线程安全编码习惯,谨慎的做法是让每个并发请求都有自己的NSMutableURLRequest对象。

  2. 您已确认请求中使用的信息有效。

    如果你想把它提升到一个新的水平,你可以使用Charles(或Wire Shark或你喜欢的任何工具)来观察实际的请求。这些工具对于调试这些问题非常宝贵。

    如果你观察Charles的请求并确认它们是有效的,那么这绝对可以消除客户端问题。

  3. 奇怪的是,您没有从NSError收到dataTaskWithRequest个对象。您也没有从服务器接收200以外的statusCode。这意味着您的请求已成功发送到服务器并由服务器接收。

    相反,服务器正在处理请求,但在完成请求时遇到问题。这让我想知道服务器代码本身。我怀疑服务器代码中存在阻止并发操作发生的事情(例如,在请求期间锁定某些共享资源,例如临时文件或SQL表)。我会仔细查看服务器代码并确保没有潜在的争用问题。

    此外,我会修改服务器代码,而不是简单地报告&#34;错误&#34;,而是产生有意义的错误消息(例如系统提供的错误消息,错误代码等)。您的服务器正在检测错误,因此您应该让它准确地告诉您错误是什么。

  4. 注意,我明确建议您不要按顺序执行请求。这是不可取的。虽然它可以解决当前的问题,但是你会付出巨大的性能损失,并且它不具备可扩展性。请记住,您必须优雅地处理并发请求,因为您可能会在某个时刻拥有该应用的多个用户。

    我会仔细查看服务器代码,在错误消息中添加更多调试信息,以便跟踪问题。

答案 2 :(得分:0)

我把请求放入for循环,它可以工作。关于NSMutableRequest和NSURLSession的抢劫的第一个想法似乎是对的,我试图抓住整个想法。谢谢你的回答。无论如何,这是代码。

for (ImageInformation *temp in self.images.imageInfos) {
    // compose request dynamically
    NSURL *url = [NSURL URLWithString:@"http://121.199.35.173:8080/xihuan22dcloud/services/Shibietupianservice/serviceGetthetupian"];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    request.HTTPMethod = @"POST";
    NSString *data = [NSString stringWithFormat:@"uid=%@&yangzhengma=%@&tupianid=%@", uid, checkCode, temp.imageId];

    request.HTTPBody = [data dataUsingEncoding:NSUTF8StringEncoding];

    // data task
    dispatch_async(self.serialQueue, ^{
        [[self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){

            // if client side is no errors, continue
            if (!error) {

                // if server side is no errors, continue
                NSHTTPURLResponse *httpResp = (NSHTTPURLResponse*) response;
                if (httpResp.statusCode == 200) {

                    // in ram and put into images' imageRawData array
                    [self.images parseImageRawData:[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] withImageId:temp.imageId];

                    // store data to disk
                    //                    [FCFileManager writeFileAtPath:path content:data];


                    // dispatch display image task to main
                    dispatch_async(dispatch_get_main_queue(), ^{
                        if ([self.images.imageDrawDatasDic count] == [self.images.imageInfos count]) {
                            [self.tableView reloadData];
                        }
                    });
                }
            }
        }] resume];
        [NSThread sleepForTimeInterval:0.5];
    });
}

}