Vous êtes sur la page 1sur 5

GNUstep overcomes these limitations by providing you with classes that form what is known as a distributed objects architecture

that extends the capabilities of t he run-time system. With the addition of a few lines of code in the client and server programs, thes e extensions allow you to send a message to a remote process by constructing a s imple Objective-C statement. In the telephone directory example, the statement t o retrieve the telephone number would now look something like this: NSString *wantedNumber = [proxyForDirectory teleNumber: personName]; Compare this to the original statement: NSString *wantedNumber = [telephoneDirectory teleNumber: personName]; Notice that the only difference between the two statements is the name of the ob ject receiving the message, i.e. proxyForDirectory rather than telephoneDirector y. GNUstep makes it as simple as this to communicate with an object in another p rocess. The variable proxyForDirectory is known as a proxy for the remote telephoneDirecto ry object. A proxy is simply a substitute for the remote object, with an address in the address space of the local client process, that receives messages and forw ards them on to the remote server process in a suitably coded form. Let us now take a look at the additional lines of code required to make this te messaging possible. remo

-------------------------------------------------------------------------------[ < ] [ > ] [ << ] [ Up ] [ >> ] [Top] [Contents] [Index] [ ? ] 7.2.1 Code at the Server In order to respond to client messages, the responding server object must be set as the root object of an instance of the NSConnection class, and this NSConnectio n must be registered with the network by name. Making an object available to cli ent processes in this way is known as vending the object. The registered name for the NSConnection is used by the client when obtaining a proxy for the responding server object over the network. The only other code you need to consider is the code that listens for incoming m essages. This runloop , as it is known, is started by sending a run message to an i nstance of the NSRunLoop class. Since an NSRunLoop object is created automatical ly for each process, there is no need to create one yourself. Simply get the def ault runloop, which is returned by the +currentRunLoop class method. When the runloop detects an incoming message, the message is passed to the root object of the NSConnection, which performs a method in response to the message a nd returns a variable of the appropriate type. The NSConnection manages all inte r-process communication, decoding incoming messages and encoding any returned va lues. The code to vend the telephoneDirectory object and start the runloop would look something like this: /* * The main() function: Set up the program * as a 'Distributed Objects Server'.

*/ int main(void) { /* * Remember, create an instance of the * NSAutoreleasePool class. */ CREATE_AUTORELEASE_POOL(pool); /* * Get the default NSConnection object * (a new one is automatically created if none exists). */ NSConnection *connXion = [NSConnection defaultConnection]; /* * Set the responding server object as * the root object for this connection. */ [connXion setRootObject: telephoneDirectory]; /* * Try to register a name for the NSConnection, * and report an error if this is not possible. */ if ([connXion registerName: @"DirectoryServer"] == NO) { NSLog(@"Unable to register as 'DirectoryServer'"); NSLog(@"Perhaps another copy of this program is running?"); exit(1); } /* Start the current runloop. */ [[NSRunLoop currentRunLoop] run]; /* Release the pool */ RELEASE(pool); return 0; } These additional lines of code turn a program into a distributed objects server, ready to respond to incoming client messages. -------------------------------------------------------------------------------[ < ] [ > ] [ << ] [ Up ] [ >> ] [Top] [Contents] [Index] [ ? ] 7.2.2 Code at the Client At the client, all you need do is obtain a proxy for the responding server objec t, using the name that was registered for the NSConnection at the server. /* Create an instance of the NSAutoreleasePool class */ CREATE_AUTORELEASE_POOL(pool); /* Get the proxy */ id proxy = [NSConnection rootProxyForConnectionWithRegisteredName: registeredServerName]; /* The rest of your program code goes here */

/* Release the pool */ RELEASE(pool); The code that obtains the proxy automatically creates an NSConnection object for managing the inter-process communication, so there is no need to create one you rself. The above example serves to establish a secure connection between processes whic h are run by the same person and are both on the same host. If you want your connections to work between different host or between programs being run by different people, you do this slightly differently, telling the sys tem that you want to use socket ports, which make TCP/IP connections over the netw ork. int main(void) { CREATE_AUTORELEASE_POOL(pool); /* * Create a new socket port for your connection. */ NSSocketPort *port = [NSSocketPort port]; /* * Create a connection using the socket port. */ NSConnection *connXion = [NSConnection connectionWithReceivePort: port sendPort: port]; /* * Set the responding server object as * the root object for this connection. */ [connXion setRootObject: telephoneDirectory]; /* * Try to register a name for the NSConnection, * and report an error if this is not possible. */ if ([connXion registerName: @"DirectoryServer" withNameServer: [NSSocketPortNameServer sharedInstance]] == NO) { NSLog(@"Unable to register as 'DirectoryServer'"); NSLog(@"Perhaps another copy of this program is running?"); exit(1); } [[NSRunLoop currentRunLoop] run]; RELEASE(pool); return 0; } In the above example, we specify that the socket port name server is used to reg ister the name for the connection ... this makes the connection name visible to processes running on other machines.

The client side code is as follows /* Create an instance of the NSAutoreleasePool class */ CREATE_AUTORELEASE_POOL(pool); /* Get the proxy */ id proxy = [NSConnection rootProxyForConnectionWithRegisteredName: registeredServerName host: hostName usingNameServer: [NSSocketPortNameServer sharedInstance]]; /* The rest of your program code goes here */ /* Release the pool */ RELEASE(pool); If the hostName in this statement is nil or an empty string, then only the local h ost will be searched to find the registeredServerName. If hostName is "*", then all hosts on the local network will be searched. In the telephone directory example, the code to obtain the proxy from any host o n the network would be: id proxyForDirectory = [NSConnection rootProxyForConnectionWithRegisteredName: @"DirectoryServer" host: @"*" usingNameServer: [NSSocketPortNameServer sharedInstance]]; With this additional line of code in the client program, you can now construct a simple Objective-C statement to communicate with the remote object. NSString *wantedNumber = [proxyForDirectory teleNumber: personName]; -------------------------------------------------------------------------------[ < ] [ > ] [ << ] [ Up ] [ >> ] [Top] [Contents] [Index] [ ? ] 7.2.3 Using a Protocol A client process does not need to know the oid run-time errors, it only needs to know ct responds. This can be determined by the ver if it responds to a particular message class of a remote server object to av the messages to which the remote obje client at run-time, by asking the ser before the message is sent.

If the methods implemented at the server are stated in a formal protocol, then t he client can ask the server if it conforms to the protocol, reducing the networ k traffic required for the individual message/response requests. A further advantage is gained at compile time, when the compiler will issue a wa rning if the server fails to implement any method declared in the protocol, or i f the client contains any message to which the server cannot respond. The protocol is saved to a header file and then included in both client and serv er programs with the usual compiler #include directive. Only the server program needs to implement the methods declared in the protocol. To enable compiler chec king in the client program, extend the type declaration for the proxy to this pr otocol, and cast the returned proxy object to the same extended type.

In the telephone directory example, if the declared protocol was TelephoneDirect ory, declared in header file protocolHeader.h, then the client code would now lo ok like this: #include "protocolHeader.h"; /* Extend the type declaration */ id<TelephoneDirectory> proxyForDirectory; /* Cast the returned proxy object to the extended type */ proxyForDirectory = (id<TelephoneDirectory>) [NSConnection rootProxyForConnectionWithRegisteredName: @"DirectoryServer" usingNameServer: [NSSocketPortNameServer sharedInstance]]; Since class names and protocol names do not share the same address space in a proc ess, the declared protocol and the class of the responding server object can sha re the same name, making code easier to understand. For example, proxyForDirectory at the client could be a proxy for an instance of the TelephoneDirectory class at the server, and this class could implement the TelephoneDirectory