I'm trying to dig deeper by using this example of an iPhone Chart Server and all is working as expected.
What I wanted to learn next is how to recover when the connection to the server is lost (for whatever reason) while the app is running.
First issue is that the app tries to open the input & output streams concurrently thus if I implement an alert, I get it two times. I managed to resolve this by closing the streams if there is an error.
The second is that if I'm another view controller and the connection is lost I can't seem to be able to recover it.I call the sendSocketMessage and if there is the error flat to try to use the initNetworkCommunication but I get a fatal error.
This 2nd issue is troubling me. I've added an All Exceptions breakpoint but got nothing. How I try to 'test' this is by making sure the server works and the app loads and connects. Then I shut-down the server, try a few clicks on the app and I get the alert. I switch-on the server and try to click again, I get the message sent to the server but then the app crashes with no info!
@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
The error I get is this screen grab:
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
Thread 1: EXC_BAD_ACCESS(code=1, address=0x0)
Any advice?