Попытка использовать GCDAsyncSocket для буферизованной передачи

(ОБНОВЛЕНО) Я пытаюсь прочитать большой файл (видео или изображение) и отправить его на удаленный сервер с помощью запроса SOAP. Мне нужно закодировать данные в виде строки Base64. Я пытаюсь сделать это следующим образом:

  • Создайте шаблон xml для запроса SOAP, который будет "обходить" данные, закодированные в base64.

  • вставьте первую часть XML-файла SOAP в буфер

  • откройте видеофайл и закодируйте его в виде фрагментов и вставьте каждый закодированный фрагмент в буфер

  • наконец, нажмите на вторую часть SOAP xml

Чтобы иметь возможность "ставить в очередь" части, как указано выше, я пытаюсь использовать GCDAsyncSocket с его возможностями буферизации. Я полагаю, что, поскольку GCDAsyncSocket работает на уровне TCP, мне нужно самому написать заголовок HTTP POST. Итак, есть много движущихся частей, которые я только смутно понимаю, и я могу делать все это неправильно. Но мое гнездо никогда не взлетает, и я даже не знаю, как его отладить. Вот мой соответствующий код, попробуйте посмотреть, заметите ли вы очевидные ошибки:

NSString *soapBody = ...; //Create the SOAP xml here with the part in the middle reserved for the Base64 encoded data (marked with string "CUT_HERE";
NSArray *soapBodyArray = [soapBody componentsSeparatedByString:@"CUT_HERE"];
self.p_soapBodyPart1 = [soapBodyArray[0] dataUsingEncoding:NSUTF8StringEncoding];
self.p_soapBodyPart2 = [soapBodyArray[1] dataUsingEncoding:NSUTF8StringEncoding];
socketQueue = dispatch_queue_create("socketQueue", NULL);//socketQueue is an ivar
self.p_socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:socketQueue];//create the socket
NSError *err = nil;
if (![p_socket connectToHost:myURL onPort:80 error:&err]) // Asynchronous!
{
   NSLog(@"I goofed: %@", err);
   return;
}
NSString* httpHeader = [NSString stringWithFormat:@"POST %@ HTTP/1.1\r\nHost: %@\r\nAccept-Encoding: gzip, deflate\r\nContent-Type: text/xml\r\nAccept-Language: en-us\r\nAccept: */*\r\nSOAPAction: http://tempuri.org/myAction\r\nConnection: keep-alive\r\nUser-Agent: ...\r\n\r\n", webserviceOperationsPath, webserviceHost];//Create the HTTP POST header 
[p_socket writeData:[httpHeader dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:1]; //enqueue the HTTP header
[p_socket writeData:self.p_soapBodyPart1 withTimeout:-1 tag:2]; //enqueue the first part of the SOAP xml
[self setUpStreamsForInputFile: [self.p_mediaURL path]];//set up NSInputStream to read from media file and encode it as Base64

Сокет, кажется, всегда соединяется правильно, что я вижу, используя этот метод делегата:

- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port
{
    NSLog(@"Socket Connected");
}

Метод setUpStreamsForInputFile (который вызывается в первом листинге кода выше):

- (void)setUpStreamsForInputFile:(NSString *)inpath {
    self.p_iStream = [[NSInputStream alloc] initWithFileAtPath:inpath];
    [p_iStream setDelegate:self];
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
    dispatch_async(queue, ^ {
        [p_iStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
                           forMode:NSDefaultRunLoopMode];
        [p_iStream open];
        [[NSRunLoop currentRunLoop] run];
    });
}

Теперь установка NSInputStream в предыдущем методе будет отправлять события этому делегату:

- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
    switch(eventCode) {
        case NSStreamEventHasBytesAvailable:
        {
            if (stream == self.p_iStream){
                if(!self.p_tempMutableData) {
                    self.p_tempMutableData = [NSMutableData data];
                }
                uint8_t buf[24000];
                unsigned int len = 0;
                len = [p_iStream read:buf maxLength:24000];//read a chunk from the file
                if(len) {
                        [p_tempMutableData appendBytes:(const void *)buf length:len];
                        NSString* base64encData = [Base64 encodeBase64WithData:self.p_tempMutableData];//encode the chunk
                        self.p_streamEncData = [base64encData dataUsingEncoding:NSUTF8StringEncoding];
                        [p_socket writeData:self.p_streamEncData withTimeout:-1 tag:3];//write the encoded chunk to the socket 
                }   
            }
            break;
        }
        case NSStreamEventEndEncountered:
        {
            [stream close];
            [stream removeFromRunLoop:[NSRunLoop currentRunLoop]
                              forMode:NSDefaultRunLoopMode];
            stream = nil;
            [p_socket writeData:self.p_soapBodyPart2 withTimeout:-1 tag:4];//write the second part of SOAP xml
            break;
        }
        ... //some other events handled here
    }
}

Сокет должен выводить данные в журнал с этим делегатом

- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
{
    if (tag == 1)
        NSLog(@"HTTP Header Written");
    else if (tag == 2)
        NSLog(@"Soap Part 1 written");
    else if (tag == 3)
        NSLog(@"File written");
    else if (tag == 4)
        NSLog(@"Soap Part 2 written");
}

но это происходит как-то случайно. Например, иногда я вижу первые 2, если они вызваны, а иногда нет. Когда я делаю это, и он достигает третьего "если" (того, где я записываю фактические закодированные данные), он записывает его только 2 или 3 раза, и все - я думаю, слишком мало, учитывая размер файла. Я никогда не вижу, чтобы он доходил до последнего "если", где он должен написать последнюю часть SOAP xml. Буду признателен за любую помощь! Заранее спасибо.

Дальнейшее обновление (19.03.13)

Сегодня, тестируя сокет, я больше не получаю события записи вообще, что говорит мне, что оно случайное, и я делаю что-то ужасно неправильно. Сегодня соединение открывается, но в какой-то момент время истекает, как я вижу с помощью следующего метода делегата:

- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err
{    // This method is executed on the socketQueue (not the main thread)
    dispatch_async(dispatch_get_main_queue(), ^{
        @autoreleasepool {
            NSLog(@"socketDidDisconnect:withError: \"%@\"", err);
        }
    });
}

который возвращается

socketDidDisconnect:withError: "Ошибка домена =NSPOSIXErrorDomain Code=60 " Тайм-аут операции " UserInfo=0x1cd89b00 {NSLocalizedFailureReason= Ошибка в функции connect(), NSLocalizedDescription= Тайм-аут операции}"

пока я все еще выполняю записи данных Base64 в потоковом делегате выше.

0 ответов

Другие вопросы по тегам