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

我试图通过使用iPhone图表服务器的这个例子深入挖掘,并且所有工作都按预期工作。

我接下来要学的是如何在应用程序运行时丢失连接到服务器的连接(无论出于何种原因)。

首先问题是,应用程序试图同时打开input和输出stream,因此如果我实现了一个警报,我得到了两次。 我设法通过closuresstream来解决这个问题。

第二个是,如果我是另一个视图控制器,连接丢失,我似乎无法恢复它。我打电话给sendSocketMessage,如果有错误平面尝试使用initNetworkCommunication,但我得到一个致命的错误。

这第二个问题让我烦恼。 我已经添加了一个所有exception断点,但什么都没有。 我如何尝试“testing”这是通过确保服务器的工作和应用程序加载和连接。 然后我closures服务器,尝试几下点击应用程序,我得到警报。 我打开服务器,并尝试再次点击,我得到的消息发送到服务器,但然后应用程序崩溃,没有任何信息!

@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 

我得到的错误是这个屏幕抓取 :

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

线程1:EXC_BAD_ACCESS(代码= 1,地址= 0x0)

有什么build议?

首要问题:

我将使用一种策略,将您的警报与连接对象配对。 由于API中没有用于处理CFstream的内置连接对象,因此您必须创build自己的连接对象。 通常,有用的连接对象将具有关联的NSInputStreamNSOutputStream 。 这种方法应该消除获得两个警报的问题,因为您将显示一个连接对象的警报,而不是相关的IOstream。 这也将属性和操作巧妙地封装到一个连接对象中,你可以从你的视图控制器中分离出来。 使用连接对象的另一个好处是你将在视图控制器的对象图中拥有更多的灵活性,因为你将通过合成来使用连接对象,而不是视图控制器子类中的inheritance。

第二个问题:

当你得到错误的时候显示main函数,不是因为它引起了问题,而是在调用堆栈结束时的函数 – 当抛出exception时,系统退出堆栈中的每个调用直到它被捕获,或者直到到达调用堆栈的末端,这通常是Objective-C中的mainfunction。

我将首先进入断点面板,然后添加一个通用断点,点击左下angular的+号,然后selectAdd Exception Breakpoint 。 这通常帮助我,当它成功地显示在我的代码,最初抛出一个exception的点,而不是显示mainfunction,这是没有用的。 一旦添加,您不需要编辑这个断点。 有时会导致debugging器不必要地停止,如果系统中有其他exception被抛出并被捕获。 如果是这种情况,那么您可以简单地禁用exception中断点,直到到达问题点,然后在启动操作之前手动重新启用中断点。

编辑

如果catch-allexception断点失败,另一种方法是使用exception处理程序。 您通常会在application:didLaunchWithOptions:方法,如下所示:

NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);

而且,实现你的处理程序来打印出一些有用的信息:

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