2015-02-01 3 views
0

Я пытаюсь глубже копать, используя этот пример iPhone Chart Server, и все работает должным образом.Восстановление потерянного соединения с использованием сокетов

Что я хотел узнать дальше, так это то, как восстановить, когда соединение с сервером потеряно (по какой-либо причине) во время работы приложения.

Первая проблема заключается в том, что приложение пытается открыть входные потоки вывода & одновременно, если я реализую предупреждение, я получаю его два раза. Мне удалось разрешить это, закрыв потоки, если есть ошибка.

Во-вторых, если я являюсь другим контроллером представления, и соединение потеряно, я, похоже, не могу его восстановить. Я вызываю sendSocketMessage, и если есть ошибка, чтобы попытаться использовать initNetworkCommunication, но Я получаю фатальную ошибку.

Этот второй вопрос беспокоит меня. Я добавил точку останова All Exceptions, но ничего не получил. Как я пытаюсь «проверить» это, убедившись, что сервер работает, и приложение загружается и подключается. Затем я отключу сервер, попробую несколько кликов по приложению, и я получаю предупреждение. Я включаю сервер и пытаюсь снова щелкнуть, я получаю сообщение, отправленное на сервер, но затем приложение выходит из строя без информации!

@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]; 
     } 
     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... 

} 

- (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 
{ 

    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 

Я получаю ошибку это screen grab:

int main(int argc, char * argv[]) { 
    @autoreleasepool { 
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 
    } 
} 

Тема 1: EXC_BAD_ACCESS (код = 1, адрес = 0x0)

Любые советы?

+0

Это не там, где крах. Добавьте [контрольную точку исключения] (http://stackoverflow.com/questions/17802662/exception-breakpoint-in-xcode/17802723#17802723) и запустите ее снова. – rebello95

+0

@ rebello95: Я добавил один, но все еще не больше информации ... Экран захвата [здесь] (http://tinypic.com/view.php?pic=1z57kn8&s=8#.VM5ply7L-6E) – Kostas

ответ

0

Первый выпуск:

Я хотел бы использовать стратегию, которая включает в себя спаривание оповещений для объектов связи. Поскольку в API нет встроенного объекта соединения для работы с потоками CF, вам придется создавать свои собственные. Как правило, полезный объект соединения имеет свойства для связанных NSInputStream и NSOutputStream. Этот подход должен устранить проблему получения двух предупреждений, так как вы будете показывать предупреждение для одного объекта соединения, а не связанные с ним потоки ввода-вывода. Это также инкапсулирует свойства и операции аккуратно в один объект соединения, который вы можете отделить от своего контроллера представлений. Еще одно преимущество использования объекта соединения заключается в том, что у вас будет больше гибкости в графике объектов вашего контроллера объектов, так как вы будете использовать объект соединения через композицию, а не наследование в подклассе контроллера.

Второй вопрос:

main функция отображается, когда вы получаете сообщение об ошибке, а не потому, что это является причиной проблемы, а это функция в конце стека вызовов - когда исключение , система выходит из каждого вызова в стеке до тех пор, пока не будет поймана, или пока не достигнет конца стека вызовов, который обычно является функцией main в Objective-C.

Сначала я должен был перейти на панель точек останова и добавить общую точку останова, нажав на знак «+» в левом нижнем углу и выбрав Add Exception Breakpoint. Обычно это помогает мне, когда он успешно показывает точку в моем коде, которая первоначально выбрала исключение, вместо того, чтобы показывать функцию main, что не помогает. Вам не нужно редактировать эту точку останова после ее добавления. Иногда это приводит к тому, что отладчик останавливается без необходимости, если есть другие исключения, которые бросаются и попадают в систему.Если это так, то вы можете просто отключить точку прерывания исключения до тех пор, пока не прибудете в эту точку, а затем вручную повторно включите точку разрыва до начала операции.

Редактировать

Другой подход, который вы можете использовать, если точка всеохватывающий Exception разрыв терпит неудачу, является использование обработчика исключений. Вы, как правило, применять этот метод в вашем application:didLaunchWithOptions:, как это:

NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);

И реализовать обработчик для печати некоторой полезной информации:

void uncaughtExceptionHandler(NSException *exception) { 
    debugLog(@"CRASH: %@", exception); 
    debugLog(@"Stack trace: %@", [exception callStackSymbols]); 
} 
+0

Спасибо за ваш ответ! Любое рекомендуемое чтение о том, как сделать объект соединения? – Kostas

+0

Конечно! Поскольку вы используете CF, я бы посмотрел на объект подключения Chatty: https://github.com/nuthatch/chatty/blob/master/Classes/Connection.h – Sheamus

+0

Спасибо @Sheamus! Я все еще пытаюсь отлаживать и получать сообщение об ошибке, из-за которой он сбой, но пока не везет ... :( – Kostas

Смежные вопросы