I have been trying to get a basic TCP client up and running for my iOS application but have run into a block which i cannot seem to work my head around.
So far i can connect, send a message which is received on the server side but then my app crashes.
Client.h
#import <Foundation/Foundation.h>
@interface Client : NSObject <NSStreamDelegate>
{
NSInputStream *inputStream;
NSOutputStream *outputStream;
}
-(void)initNetworkCommunication;
-(void)send:(NSString*)message;
@end
Client.m
#import "Client.h"
@implementation Client
- (void)initNetworkCommunication {
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)@"10.0.1.51", 7769, &readStream, &writeStream);
inputStream = ( NSInputStream *)CFBridgingRelease(readStream);
outputStream = ( NSOutputStream *)CFBridgingRelease(writeStream);
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
}
- (void)send:(NSString*)message
{
NSData *data = [[NSData alloc] initWithData:[message dataUsingEncoding:NSUTF8StringEncoding]];
[outputStream write:[data bytes] maxLength:[data length]];
}
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
NSLog(@"stream event %i", streamEvent);
switch (streamEvent) {
case NSStreamEventOpenCompleted:
NSLog(@"Stream opened");
break;
case NSStreamEventHasBytesAvailable:
if (theStream == inputStream) {
uint8_t buffer[1024];
int len;
while ([inputStream hasBytesAvailable]) {
len = [inputStream read:buffer maxLength:sizeof(buffer)];
if (len > 0) {
NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding];
if (nil != output) {
NSLog(@"server said: %@", output);
}
}
}
}
break;
case NSStreamEventErrorOccurred:
NSLog(@"Can not connect to the host!");
break;
case NSStreamEventEndEncountered:
NSLog(@"End Encountered!");
[theStream close];
[theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
theStream = nil;
break;
default:
NSLog(@"Unknown event");
}
}
@end
The output in the console is
2013-10-03 17:01:38.542 MMORPG[6076:70b] stream event 1
2013-10-03 17:01:38.543 MMORPG[6076:70b] Stream opened
2013-10-03 17:01:43.495 MMORPG[6076:70b] stream event 4
2013-10-03 17:01:43.495 MMORPG[6076:70b] Unknown event
It seems like, my message is sent, i receive stream event #4 and then i get a bad access crash. The problem is i have no idea what its having trouble accessing?
Any help would be greatly appreciated!
The problem is that NSStream
keeps an assign/unsafe_unretained reference to its delegate. If the delegate is released before the stream is closed and released, the stream will try to call methods on its now deallocated delegate, causing a crash. The solution is to either make sure some other object has a strong reference to the client, preventing its early deallocation, or else make sure you close and release the stream before its delegate is deallocated.