使用套接字恢复丢失的连接

时间:2015-02-01 17:41:24

标签: ios objective-c iphone sockets xcode6

我试图通过使用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)

有什么建议吗?

1 个答案:

答案 0 :(得分:0)

第一期:

我会使用一种策略,将警报与连接对象配对。由于API中没有用于处理CF流的内置连接对象,因此您必须创建自己的连接对象。通常,有用的连接对象将具有关联的NSInputStreamNSOutputStream的属性。这种方法应该消除了获得两个警报的问题,因为您将显示一个连接对象的警报,而不是它的相关IO流。这还将属性和操作整齐地封装到一个连接对象中,您可以将其与视图控制器分开。使用连接对象的另一个好处是,您可以在视图控制器中获得更大的灵活性。对象图,因为您将通过合成使用连接对象,而不是视图控制器子类中的继承。

第二期:

当你收到错误时会显示main函数,而不是因为它导致问题,而是它是调用堆栈末尾的函数 - 当抛出异常时,系统退出每次调用堆栈直到被捕获,或直到它到达调用堆栈的末尾,这通常是Objective-C中的main函数。

我首先进入断点面板,然后通过点击左下角的+号并选择Add Exception Breakpoint来添加一般断点。这通常可以帮助我,当它成功地显示我的代码中最初抛出异常的点时,而不是显示main函数,这是没有用的。添加后,您不需要编辑此断点。有时它会导致调试器不必要地停止,如果有其他异常被抛出并被系统捕获。如果是这种情况,那么您可以简单地禁用异常断点,直到到达相关点,然后在启动操作之前手动重新启用断点。

修改

如果catch-all Exception断点失败,您可以使用的另一种方法是使用异常处理程序。您通常会在application:didLaunchWithOptions:方法中应用此方法,如下所示:

NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);

并且,实现您的处理程序以打印出一些有用的信息:

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