2015-02-02 2 views
0

У меня есть этот пример кода, взятый из примера этого iPhone chat server.iphone socket stream buffer различен на симуляторе и iPhone 6 плюс?

В моем случае мне нужно отправить большие объемы данных, 100k-200k, таким образом, я изменил размер буфера на то, что может вместить то, что я хочу.

На симуляторе iOS (эмуляция 6plus) все работает отлично, как только я пытаюсь отлаживать свой iPhone 6plus после реализации предложений @tc. Я получаю входящее сообщение, разбитое на 3-4 части! Есть идеи?

2015-02-03 22:42:34.756 Sock1[7390:3773054] Incoming message: XML part 1 
2015-02-03 22:42:34.759 Sock1[7390:3773054] Incoming message: XML part 2 
2015-02-03 22:42:34.774 Sock1[7390:3773054] Incoming message: XML part 3 
2015-02-03 22:42:34.794 Sock1[7390:3773054] Incoming message: XML part 4 

XML-часть 1-4 составляет все сообщение, которое должно быть в одном, поскольку в нем нет символа нулевого байта.

Просто, чтобы сделать вещи еще более странные, когда я добавить точку останова на этой линии:

[currentMessage appendBytes:buffer length:len]; 

и пройти шаг за шагом (69-70 нажав Продолжить) все работает отлично! Никаких ошибок! Так что это связано со скоростью разбора или получения или чего-то, что я не могу понять?

Я уверен, что сервер не отправляет нулевые байт-символы, кроме конечного в конце сообщения.

код я использую это:

@implementation ViewController 

bool connectionError = true; 

- (void)viewDidLoad { 
    [super viewDidLoad]; 
    // Do any additional setup after loading the view, typically from a nib. 
} 

- (void)didReceiveMemoryWarning { 
    [super didReceiveMemoryWarning]; 
    // Dispose of any resources that can be recreated. 
} 

- (void)initNetworkCommunication { 
    CFReadStreamRef readStream; 
    CFWriteStreamRef writeStream; 

    CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (CFStringRef)@"192.168.1.1", 6035, &readStream, &writeStream); 

    inputStream = (__bridge NSInputStream *)readStream; 
    outputStream = (__bridge NSOutputStream *)writeStream; 

    [inputStream setDelegate:self]; 
    [outputStream setDelegate:self]; 

    [inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 
    [outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 

    [inputStream open]; 
    [outputStream open]; 

} 

- (void)closeAll { 
    NSLog(@"Closing streams."); 

    [inputStream close]; 
    [outputStream close]; 

    [inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 
    [outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; 

    [inputStream setDelegate:nil]; 
    [outputStream setDelegate:nil]; 

    inputStream = nil; 
    outputStream = nil; 
} 

- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent { 

    switch (streamEvent) { 

    case NSStreamEventOpenCompleted: 
     NSLog(@"Stream opened"); 
     break; 

    case NSStreamEventHasBytesAvailable: 

     connectionError = false; 
     if (theStream == inputStream) { 

     uint8_t buffer[1024]; 
     long len; 

     NSMutableData *currentMessage; 
     currentMessage = [NSMutableData dataWithBytes:"" length:strlen("")]; 

     while ([inputStream hasBytesAvailable]) { 
      len = [inputStream read:buffer maxLength:sizeof(buffer)]; 
      [currentMessage appendBytes:buffer length:len]; 
     } 

     NSString *newMessage = [[NSString alloc] initWithData:currentMessage encoding:NSUTF8StringEncoding]; 
     NSLog(@"Incoming message: %@",newMessage); 

     NSData *nullByte = [NSMutableData dataWithLength:1]; 
     len = currentMessage.length; 
     NSRange searchRange = {0, len}; 
     for (NSRange r; (r = [currentMessage rangeOfData:nullByte options:0 range:searchRange]).length;) { 
      NSString *message = [[NSString alloc] initWithBytes:currentMessage.bytes length:r.location encoding:NSUTF8StringEncoding]; 
      searchRange.location = r.location+r.length; 
      searchRange.length = len - searchRange.location; 

      [self messageReceived:message]; 
     } 
     [currentMessage replaceBytesInRange:(NSRange){0, searchRange.location} withBytes:NULL length:0]; 
     } 
     break; 

    case NSStreamEventErrorOccurred: 
     NSLog(@"Can not connect to the host!"); 
     connectionError = true; 

     [self closeAll]; 

     [self connectionLost]; 

     break; 


    case NSStreamEventEndEncountered: 
     break; 

    default: 
     NSLog(@"Unknown event"); 
    } 

} 

- (void) connectionLost { 

    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Alert!" 
          message:@"Connection to the server lost!" 
          delegate:nil 
         cancelButtonTitle:@"OK" 
         otherButtonTitles:nil]; 

    [alert show]; 

} 

- (void) messageReceived:(NSString *)message { 

    [messages addObject:message]; 

    if (inputStream.streamStatus == NSStreamStatusClosed || inputStream.streamStatus == NSStreamStatusError || inputStream.streamStatus == NSStreamStatusNotOpen) { 
    [self closeAll]; 
    [self initNetworkCommunication]; 
    } 

    // do things with the message, XML parsing...  
} 

- (void) initConnection { 

    [self initNetworkCommunication]; 

    messages = [[NSMutableArray alloc] init]; 

} 

- (IBAction)joinChat:(id)sender { 

    [self initConnection]; 

    [self sendSocketMessage: @"iam:" message: _inputNameField.text]; 

} 


- (void) sendSocketMessage:(NSString*) sendCommand message:(NSString*) sendMessage 
{ 
    // do something... 

    if (outputStream.streamStatus == NSStreamStatusClosed || outputStream.streamStatus == NSStreamStatusError || outputStream.streamStatus == NSStreamStatusNotOpen) { 
    [self closeAll]; 
    [self initNetworkCommunication]; 
    }  

    NSString *response = [NSString stringWithFormat: @"%@%@", sendCommand, sendMessage]; 
    NSData *data = [[NSData alloc] initWithData:[response dataUsingEncoding:NSASCIIStringEncoding]]; 
    [outputStream write:[data bytes] maxLength:[data length]]; 
    NSLog(@"clint sent: %@", response); 

} 



@end 

ответ

1

Короткий ответ, что TCP предоставляет поток байтов, а не поток переменной длины сообщений ¹. Я не знаю, почему он работает в симуляторе, если только 192.168.1.1 не является одним и тем же машином (в этом случае он не подчиняется обычному управлению потоком, хотя, возможно, это удивительно, что буферы ядра такие большие).

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

  • Длина-префиксы. Примеры включают в себя сетевые строки DJB (например, 5: abcde для байта «abcde»), HTTP-контент Content-Length, HTTP-кодирование с кодировкой HTTP 1.1 и различные двоичные протоколы, обычно с 8-битным, 16-разрядным или с переменной длиной слова длины.
  • Разделители. Примеры включают новые строки (традиционно CRLF, например, в Telnet/SMTP/HTTP/IRC), нулевые байты, границы MIME (ick).

В этом случае мы можем использовать тот факт, что в XML не допускаются пустые байты. currentMessage считается NSMutableData ivar, а не nil.

while ([inputStream hasBytesAvailable]) { 
    len = [inputStream read:buffer maxLength:sizeof(buffer)]; 
    [currentMessage appendBytes:buffer length:len]; 
} 
NSData * nullByte = [NSMutableData dataWithLength:1]; 
len = currentMessage.length; 
NSRange searchRange = {0, len}; 
for (NSRange r; (r = [currentMessage rangeOfData:nullByte options:0 range:searchRange]).length;) { 
    NSString * message = [[NSString alloc] initWithBytes:currentMessage.bytes length:r.location encoding:NSUTF8StringEncoding]; 
    searchRange.location = r.location+r.length; 
    searchRange.length = len - searchRange.location; 
    [self messageReceived:message]; 
} 
[currentMessage replaceBytesInRange:(NSRange){0, searchRange.location} withBytes:NULL length:0]; 

отметить также, что при инициализации вещи в -viewDidLoad требует некоторой осторожности, если представление может быть выгружено (это не меньше проблем, если вы поддерживаете только IOS 6+, так как представления больше не будет автоматически выгружены).

+0

Спасибо за ваш вход @tc. ! Я работаю над личным проектом только для iOS8, и ваш код очень помог! Теперь, чтобы узнать, как я могу восстановиться, когда соединение сломается ... но это еще одна тема (http://stackoverflow.com/questions/28265700/recovering-a-lost-connection-using-sockets) ... – Kostas

+0

I говорил слишком быстро @ tc.Я отредактировал вопрос по мере возникновения другого типа проблемы ... – Kostas

+0

'currentMessage' должен быть ivar, а не локальной переменной. –