我试图通过使用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 :(得分:0)
第一期:
我会使用一种策略,将警报与连接对象配对。由于API中没有用于处理CF流的内置连接对象,因此您必须创建自己的连接对象。通常,有用的连接对象将具有关联的NSInputStream
和NSOutputStream
的属性。这种方法应该消除了获得两个警报的问题,因为您将显示一个连接对象的警报,而不是它的相关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]);
}