NSConnection Issue

Discussion in 'Mac Programming' started by Raska, Mar 31, 2009.

  1. macrumors 6502

    Joined:
    Aug 5, 2007
    Location:
    NJ
    #1
    I am creating two applications: a command-line Foundation application, and a Cocoa application. The command-line will be the server, and the Cocoa is the client.

    The Server code is as follows:
    Code:
    theConnection = [NSConnection serviceConnectionWithName: @"atcserver" rootObject: planes];
    			
    if(theConnection == nil) NSLog(@"Error creating Server!");
    			
    			
    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(clientHasConnected:) name: NSConnectionDidInitializeNotification object: theConnection];
    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(connectionHasDied:) name: NSConnectionDidDieNotification object: theConnection];
    			
    [theConnection setDelegate: self];
    			
    //Make sure the connections runs on its own thread
    [theConnection runInNewThread];
    //Remove the connection from the main run loop so all requests run on the specified thread
    [theConnection removeRunLoop: [NSRunLoop currentRunLoop]];
    The Client code is as follows:
    Code:
    theConnection = [NSConnection connectionWithRegisteredName: @"atcserver" host: nil];
    remoteObject = (NSMutableArray *)[[theConnection rootProxy] retain];
    			
    if (theConnection == nil) {
    	NSLog(@"Could not connect to server!");
    	exit(1);
    }
    else {
    	//Add self to the notification center to let it know if the connection has initalized or died
    	[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(connectionHasDied:) name: NSConnectionDidDieNotification object: theConnection];
    	//Make sure the connections runs on its own thread
    	[theConnection runInNewThread];
    	//Remove the connection from the main run loop so all requests run on the specified thread
    	[theConnection removeRunLoop: [NSRunLoop currentRunLoop]];
    }
    Where both 'theConnection' objects are NSConnections, and 'remoteObject' in the Client is of type id. It appears as though the client connects to the server, but when I attempt to call a method on remoteObject, I get the following error message:
    I have been racking my brain for what seems like forever but cannot figure out the problem. Any help would be greatly appreciated. Thanks in advance!
     
  2. Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
  3. thread starter macrumors 6502

    Joined:
    Aug 5, 2007
    Location:
    NJ
    #3
    "planes" is an NSMutableArray which will hold custom Plane objects.
     
  4. Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #4
    I suspect something is going on in some of your other code. I first thought it was an issue with using an array as the root object, but that's not the problem. Can you post more code?

    I would suggest not using NSMutableArray as the root object. Instead, use a custom object that conforms to your own protocol. This way you can set the protocol on the proxy object in the client via setProtocolForProxy: which reduces the amount of messages sent back and forth from the client and server.

    BTW I created a test project and it works just fine using this bare minimum code:

    Code:
    // Server
    - (void)applicationDidFinishLaunching:(NSNotification *)notif
    {
        NSMutableArray *array = [[NSMutableArray alloc] init];
        [array addObject:@"Hello"];
        NSConnection *conn = [NSConnection serviceConnectionWithName:@"testconn" rootObject:array];
        [conn runInNewThread];
    }
    
    // Client
    - (void)applicationDidFinishLaunching:(NSNotification *)notif
    {
        NSConnection *conn = [NSConnection connectionWithRegisteredName:@"testconn" host:nil];
        NSMutableArray *arrayProxy = (NSMutableArray *)[conn rootProxy];
        [arrayProxy addObject:@"World"];
        NSLog(@"arrayProxy: %@", arrayProxy);
    }
     
  5. thread starter macrumors 6502

    Joined:
    Aug 5, 2007
    Location:
    NJ
    #5
    All right I'll take care of the protocol. As for the current situation, here is my entire .m file for the server:
    Code:
    #import "ATCModel.h"
    #import "Plane.h"
    
    @implementation ATCModel
    
    -(id)init {
    	self = [super init];
    	
        if (self) {
    		planes = [[NSMutableArray alloc] init];
    		[self startServer];
        }
    	
    	return self;
    }
    
    -(void)startServer {	
    	//Engage Mutual Exclusion
    	@synchronized(self) {
    		@try {
    			theConnection = [NSConnection serviceConnectionWithName: @"atcserver" rootObject: planes];
    			
    			if(theConnection == nil) NSLog(@"Error creating Server!");
    			
    			
    			[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(clientHasConnected:) name: NSConnectionDidInitializeNotification object: theConnection];
    			[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(connectionHasDied:) name: NSConnectionDidDieNotification object: theConnection];
    			
    			[theConnection setDelegate: self];
    			
    			//Make sure the connections runs on its own thread
    			[theConnection runInNewThread];
    			//Remove the connection from the main run loop so all requests run on the specified thread
    			[theConnection removeRunLoop: [NSRunLoop currentRunLoop]];
    			
    			NSLog(@"Waiting for connections...");
    		}
    		@catch (NSException *anError) {
    			//If cannot connect to server, report error and exit application
    			[self dealloc];
    			NSLog(@"Error: Could not create server! %@", anError);
    			exit(1);
    		}
    	}
    }
    
    -(void)clientHasConnected {
    	NSLog(@"A Client has connected to the server!");
    }
    
    -(void)connectionHasDied {
    	NSLog(@"Connection has died. Reconnecting...");
    	//Engage Mutual Exclusion
    	@synchronized(self) {
    		//Clear out the connection and remove self from notifications
    		[[theConnection sendPort] invalidate];
    		[[theConnection receivePort] invalidate];
    		[theConnection release];
    		theConnection = nil;
    		[[NSNotificationCenter defaultCenter] removeObserver: self name: NSConnectionDidDieNotification object: theConnection];
    		[[NSNotificationCenter defaultCenter] removeObserver: self name: NSConnectionDidInitializeNotification object: theConnection];
    		
    		//Restart the server
    		[self startServer];
    	}
    }
    
    -(void)dealloc {
    	[theConnection release];
    	[planes release];
    	[super dealloc];
    }
    
    -(void)remoteMethod {
    	NSLog(@"Remote Method called!");
    }
    
    @end
    
    and for the client:
    Code:
    #import "ATCController.h"
    
    
    @implementation ATCController
    
    -(void)awakeFromNib {
    	NSLog(@"Starting Client...");
    	[self connectToServer];
    }
    
    -(void)connectToServer {
    	//Engage Mutual Exclusion
    	@synchronized(self) {
    		@try {								
    			theConnection = [NSConnection connectionWithRegisteredName: @"atcserver" host: nil];
    			remoteObject = (NSMutableArray *)[[theConnection rootProxy] retain];
    			
    			if (theConnection == nil) {
    				NSLog(@"Could not connect to server!");
    				exit(1);
    			}
    			else {
    				//Add self to the notification center to let it know if the connection has initalized or died
    				[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(connectionHasDied:) name: NSConnectionDidDieNotification object: theConnection];
    				//Make sure the connections runs on its own thread
    				[theConnection runInNewThread];
    				//Remove the connection from the main run loop so all requests run on the specified thread
    				[theConnection removeRunLoop: [NSRunLoop currentRunLoop]];
    				
    				[remoteObject remoteMethod];
    			}
    		}
    		@catch (NSException *anError) {
    			//If cannot connect to server, report error and exit application
    			NSLog(@"Error! %@", anError);
    			exit(1);
    		}
    	}
    }
    
    -(void)connectionEstablished {
    	NSLog(@"Connection to server established!");
    }
    
    -(void)connectionHasDied {
    	//Engage Mutual Exclusion
    	@synchronized(self) {
    		NSLog(@"Connection to Server has died. Reconnecting...");
    		//Clear out and the connection and remove self from notifications
    		[[theConnection sendPort] invalidate];
    		[[theConnection receivePort] invalidate];
    		[theConnection release];
    		theConnection = nil;
    		[[NSNotificationCenter defaultCenter] removeObserver: self name: NSConnectionDidDieNotification object: theConnection];
    		
    		//Reconnect to the server
    		[self connectToServer];
    	}
    }
    
    -(void)dealloc {
    	[theConnection release];
    	[remoteObject release];
    	[super dealloc];
    }
    
    @end
    
     
  6. Moderator emeritus

    kainjow

    Joined:
    Jun 15, 2000
    #6
    Ah :). I thought remoteMethod was a private method of NSDistantObject, which was confusing me.

    You are calling remoteMethod on the proxy object, which is an NSMutableArray, so that's why that error is occurring. I'd suggest setting the root object in your server to "self" (instead of "planes") and then provide an accessor to access the planes array.

    Edit: never call dealloc. Cocoa does that for you when your object is being deallocated.
     
  7. thread starter macrumors 6502

    Joined:
    Aug 5, 2007
    Location:
    NJ
    #7
    That did the trick! Thank you so much!

    Regarding setting a protocol, if I have the root object set to self, would setting up a protocol on my custom object still be effective?
     

Share This Page