libs-base/Documentation/manual/DistributedObjects.texi

936 lines
32 KiB
Text
Raw Normal View History

@paragraphindent 0
@node Distributed Objects
@chapter Distributed Objects
@cindex distributed objects
Until now we have been concentrating on using the Objective-C language
to create programs that execute in a single process. But what if you
want your program to interact with objects in other processes, perhaps
running on different machines?
As a simple example, we may have a client process that needs to access a
telephone directory stored on a remote server. The client process could
send a message to the server that contained a person's name, and the
server could respond by returning that person's number.
The GNUstep base library provides a powerful set of classes that make
this type of remote messaging not only possible, but easy to program. So
what do these classes do and how can we use them? To answer that we must
first look at the way code interacts with objects in a single process,
and then look at how we can achieve the same interaction with objects
that exist in different processes.
@section Object Interaction
@cindex object interaction, remote objects
To continue with the example above, if the telephone directory existed
in the same process as the code that was accessing it, then a simple
message would return the wanted telephone number.
@example
NSString *wantedNumber = [telephoneDirectory teleNumber: personName];
@end example
Now object and method names just hold pointers to memory addresses. The
code executed at run time in response to the @code{teleNumber} message
is located at an address held by the name of the responding method (a
variable), while data in the telephone directory is located at an
address held by the @code{telephoneDirectory} variable.
In a single process these addresses can be accessed by the client code
at run time, but if the telephone directory is located on a remote
server, then the address of the remote object is not known in the client
process (the @code{telephoneDirectory} object and its responding method
are said to exist in a separate 'address space').
The Objective-C run-time library was not designed for this inter-process
communication or 'remote messaging'.
@section The GNUstep Solution
@cindex distributed objects
@cindex remote objects
@cindex client/server processes
@cindex NSConnection class
@cindex NSProxy class
@cindex NSRunLoop class
GNUstep overcomes these limitations by providing you with classes that
form, what is known as, a 'distributed objects' architecture that
extends the capabilities of the run-time system.
With the addition of a few lines of code in the client and server
programs, these extensions allow you to send a message to a remote
process by constructing a simple Objective-C statement. In the telephone
directory example, the statement to retrieve the telephone number would
now look something like this:
@example
NSString *wantedNumber = [proxyForDirectory teleNumber: personName];
@end example
Compare this to the original statement:
@example
NSString *wantedNumber = [telephoneDirectory teleNumber: personName];
@end example
Notice that the only difference between the two statements is the name
of the object receiving the message, i.e. @code{proxyForDirectory}
rather than @code{telephoneDirectory}. GNUstep makes it as simple as
this to communicate with an object in another process.
The variable @code{proxyForDirectory} is known as a 'proxy' for the
remote @code{telephoneDirectory} 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 forwards 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 'remote messaging' possible.
@subsection Code at the Server
@cindex server code, GNUstep
@cindex GNUstep server code
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 NSConnection must be registered with the network by
name. Making an object available to client 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 messages. This 'runloop' as it is known, is started by sending
a @code{run} message to an instance of the NSRunLoop class. Since an
NSRunLoop object is created automatically for each process, there is no
need to create one yourself. Simply get the default runloop, which is
returned by the @code{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 and returns a variable of the appropriate type. The
NSConnection manages all inter-process communication, decoding incoming
messages and encoding any returned values.
The code to vend the @code{telephoneDirectory} object and start the
runloop would look something like this:
@example
/*
* 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);
@end example
These additional lines of code turn a program into a distributed objects
server, ready to respond to incoming client messages.
@subsection Code at the Client
@cindex client code, GNUstep
@cindex GNUstep client code
At the client, all you need do is obtain a proxy for the responding
server object, using the name that was registered for the NSConnection
at the server.
@example
/* Create an instance of the NSAutoreleasePool class */
CREATE_AUTORELEASE_POOL(pool);
/* Get the proxy */
id proxy = [NSConnection
rootProxyForConnectionWithRegisteredName:
@i{registeredServerName}
host: @i{hostName}];
/* The rest of your program code goes here */
/* Release the pool */
RELEASE(pool);
@end example
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 yourself. If the @i{hostName} in this statement is 'nil',
then only the local host will be searched to find the
@i{registeredServerName}. If @i{hostName} is "*", then all hosts on the
network will be searched.
In the telephone directory example, the code to obtain the proxy from
any host on the network would be:
@example
id proxyForDirectory = [NSConnection
rootProxyForConnectionWithRegisteredName:
@@"DirectoryServer"
host: "*"];
@end example
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.
@example
NSString *wantedNumber = [proxyForDirectory teleNumber: personName];
@end example
@subsection Using a Protocol
@cindex protocol for distributed objects
@cindex distributed objects, using a protocol
A client process does not need to know the class of a remote server
object to avoid run-time errors, it only needs to know the messages to
which the remote object responds. This can be determined by the client
at run-time, by asking the server if it responds to a particular message
before the message is sent.
If the methods implemented at the server are stated in a formal
protocol, then the client can ask the server if it conforms to the
protocol, reducing the network traffic required for the individual
message/response requests.
A further advantage is gained at compile time, when the compiler will
issue a warning if the server fails to implement any method declared in
the protocol, or if 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 server programs with the usual compiler @code{#include}
directive. Only the server program needs to implement the methods
declared in the protocol. To enable compiler checking in the client
program, extend the type declaration for the proxy to this protocol, and
cast the returned proxy object to the same extended type.
In the telephone directory example, if the declared protocol was
@code{TelephoneDirectory}, declared in header file
@code{protocolHeader.h}, then the client code would now look like this:
@example
#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"
host: "*"];
@end example
Since class names and protocol names do not share the same 'address
space' in a process, the declared protocol and the class of the
responding server object can share the same name, making code easier to
understand.
For example, @code{proxyForDirectory} at the client could be a proxy for
an instance of the @code{TelephoneDirectory} class at the server, and
this class could implement the @code{TelephoneDirectory} protocol.
@section An Example Game Server
@cindex distributed objects, example (no error checking)
It would be tempting to show a complete example of the telephone
directory program, but a minimal amount of additional code is required
to turn the previous examples into a working implementation. Instead we
will look at an example called GameServer that uses distributed objects
in a client/server game.
@i{Exercise: Create a working client/server telephone directory on your
own by adding a few lines of code to the previous examples, perhaps with
a telephone directory holding only two or three names and numbers. At
first you could implement this as a single process and then when it
works, modify the code to run as client/server processes running on the
same machine. (Try passing the person's name into the client program as
a command-line argument. The server need only test the name, e.g. if
personName = "Fred" return "number".)}
@i{All you need do is create two directories, one for the client and one
for the server. Each directory will hold a GNUmakefile, the
client/server source code, and a copy of the protocol header file. When
the files compile, first run the server and then the client. What
happens when you run the client without the server? How would you
display a "No Server Connection" warning at the client? Appendix B
contains a small working example that you can compare with your own
implementation.}
@subsection What GameServer Does
@cindex game server example
Although there is no actual game to play, and while the code to vend an
object and connect to a remote process is similar to that already shown,
the code does show a number of additional techniques that can be used in
other client/server programs.
@itemize @bullet
@item When the client attempts to join the game, the server checks that
the client is entitled to join, based on the last time the client
played.
The rule is: if the client lost the last game, then they cannot re-play
for the next 2 hours; but if the client won the last game, then they can
re-play the game at any time (a reward for winning).@*@*
@item The server also makes sure the client is not already connected and
playing the game (i.e. they cannot play two games at the same time -
that would be cheating).@*@*
@item In addition to a proxy for the server being obtained at the
client, a proxy for the client is received at the server. This allows
two-way messaging, where the client can send messages to the server and
the server can send messages to the client (e.g. the state of play may
be affected by the actions of other players, or by other events at the
server).@*@*
Two protocols will therefore be required, one for the methods
implemented at the server and one for those implemented at the client.
@end itemize
Have a look at the program code and added comments. Can you work out
what is happening at the server and client? If you have any difficulties
then refer to the relevant sections in this manual, or to class
documentation at the GNUstep or Apple web-sites.
@subsection Protocol Adopted at Client
We have chosen @code{GameClient} as the name of both the protocol
adopted at the client and the class of the responding client object. The
header file declaring this protocol will simply declare the methods that
the class must implement.
@example
@@protocol GameClient
- (void) clientMessage: (bycopy NSString *)theMessage;
- (int) clientReply;
// Other methods would be added that
// reflect the nature of the game.
@@end
@end example
The protocol will be saved as @code{GameClient.h}.
@subsection Protocol Adopted at Server
We have chosen @code{GameServer} as the name of both the protocol
adopted at the server and the class of the responding server object. The
header file declaring this protocol will simply declare the methods that
the class must implement.
@example
@@protocol GameServer
- (BOOL) mayJoin: (id)client asPlayer: (bycopy NSString*)name;
- (int) startGame: (bycopy NSString*)name;
- (BOOL) endGame: (bycopy NSString*)name;
// Other methods would be added that
// reflect the nature of the game.
@@end
@end example
The protocol will be saved as @code{GameServer.h}.
@subsection Code at the Client
The client code contains the @code{main} function and the
@code{GameClient} class declaration and implementation.
The @code{main()} function attempts to connect to the server, while the
@code{GameClient} class adopts the @code{GameClient} protocol.
@example
#include <Foundation/Foundation.h>
#include "GameServer.h"
#include "GameClient.h"
/*
* GameClient class declaration:
* Adopt the GameClient protocol.
*/
@@interface GameClient : NSObject <GameClient>
@@end
/*
* GameClient class implementation.
*/
@@implementation GameClient
/*
* Implement clientMessage: as declared in the protocol.
* The method simply prints a message at the client.
*/
- (void) clientMessage: (NSString*)theMessage
@{
printf([theMessage cString]);
@}
/*
* Implement clientReply: as declared in the protocol.
* The method simply returns the character entered
* at the client keyboard.
*/
- (int) clientReply
@{
return getchar();
@}
@@end // End of GameClient class implementation.
/*
* The main function of the client program.
*/
int main(int argc, char **argv)
@{
CREATE_AUTORELEASE_POOL(pool);
id<GameServer> server;
int result;
NSString *name;
id client;
/*
* The NSUserName() function returns the name of the
* current user, which is sent to the server when we
* try to join the game.
*/
name = NSUserName();
/*
* Create a GameClient object that is sent to
* the server when we try to join the game.
*/
client = AUTORELEASE([GameClient new]);
/*
* Try to get a proxy for the root object of a server
* registered under the name 'JoinGame'. Since the host
* is '*', we can connect to any server on the local network.
*/
server = (id<GameServer>)[NSConnection
rootProxyForConnectionWithRegisteredName:
@@"JoinGame" host: @@"*"];
if(server == nil)
@{
printf("\n** No Connection to GameServer **\n");
result = 1;
@}
/*
* Try to join the game, passing a GameClient object as
* the client, and our user-name as name. The 'client'
* argument will be received as a proxy at the server.
*/
else if ([server mayJoin: client asPlayer: name] == NO)
@{
result = 1; // We cannot join the game.
@}
else
@{
/*
* At this point, we would actually start to play the game.
*/
[server startGame: name]; // Start playing game.
[server endGame: name]; // Finally end the game.
result = 0;
@}
RELEASE(pool);
return result;
@}
@end example
To summarise the code at the client:
@itemize @bullet
@item We obtained a proxy for the server and can now communicate
with the server using the methods declared in the @code{GameServer}
protocol.@*@*
@item We passed a @code{GameClient} object and our user-name to the
server (the @code{GameClient} object is received as a proxy at the
server). The server can now communicate with the client using the
methods declared in the @code{GameClient} protocol.@*@*
@item When the game is in progress, the server can alter the state
of the client object to reflect the success of the player.
@end itemize
@subsection Code at the Server
The server code contains the @code{main} function and the
@code{GameServer} class declaration and implementation.
The @code{main()} function vends the server's root object and starts the
runloop, while the @code{GameServer} class adopts the @code{GameServer}
protocol. The class also implements methods that initialise and
deallocate the root object's instance variables (dictionaries that hold
player information).
@example
#include <Foundation/Foundation.h>
#include "GameServer.h"
#include "GameClient.h"
/*
* GameServer class declaration:
* Adopt the GameServer protocol and declare
* GameServer instance variables.
*/
@@interface GameServer : NSObject <GameServer>
@{
NSMutableDictionary *delayUntil; // Delays to re-joining GameServer.
NSMutableDictionary *currentPlayers; // Proxies to each client.
NSMutableDictionary *hasWon; // Success in game for each player.
@}
@@end
/*
* GameServer class implementation.
*/
@@implementation GameServer
/* Initialise GameServer's instance variables. */
- (id) init
@{
self = [super init];
if (self != nil)
@{
/*
* Create a dictionary for a maximum of
* 10 named players that will hold a
* re-joining time delay.
*/
delayUntil = [[NSMutableDictionary alloc]
initWithCapacity: 10];
/*
* Create a dictionary that will hold the
* names of these players and a proxy for
* the received client objects.
*/
currentPlayers = [[NSMutableDictionary alloc]
initWithCapacity: 10];
/*
* Create a dictionary that will record
* a win for any of these named players.
*/
hasWon = [[NSMutableDictionary alloc]
initWithCapacity: 10];
@}
return self;
@}
/* Release GameServer's instance variables. */
- (void) dealloc
@{
RELEASE(delayUntil);
RELEASE(currentPlayers);
RELEASE(hasWon);
[super dealloc];
@}
/*
* Implement mayJoin:: as declared in the protocol.
* Adds the client to the list of current players.
* Each player is represented at the server by both
* name and by proxy to the received client object.
* A player cannot join the game if they are already playing,
* or if joining has been delayed until a later date.
*/
- (BOOL) mayJoin: (id)client asPlayer: (NSString*)name
@{
NSDate *delay; // The time a player can re-join the game.
NSString *aMessage;
if (name == nil)
@{
NSLog(@@"Attempt to join nil user");
return NO;
@}
/* Has the player already joined the game? */
if ([currentPlayers objectForKey: name] != nil)
@{
/* Inform the client that they cannot join. */
aMessage = @@"\nSorry, but you are already playing GameServer!\n";
[client clientMessage: aMessage];
return NO;
@}
/* Get the player's time delay for re-joining. */
delay = [delayUntil objectForKey: name];
/*
* Can the player join the game? Yes if there is
* no restriction or if the time delay has passed;
* otherwise no, they cannot join.
*/
if (delay == nil || [delay timeIntervalSinceNow] <= 0.0)
@{
/* Remove the old restriction on re-joining the game. */
[delayUntil removeObjectForKey: name];
/* Add the player to the list of current players. */
[currentPlayers setObject: client forKey: name];
[hasWon setObject: @@"NO" forKey: name]; // They've not won yet.
/* Inform the client that they have joined the game. */
aMessage = @@"\nWelcome to GameServer\n";
[client clientMessage: aMessage];
return YES;
@}
else
@{
/* Inform the client that they cannot re-join. */
aMessage = @@"\nSorry, you cannot re-join GameServer yet.\n";
[client clientMessage: aMessage];
return NO;
@}
@}
/*
* Implement startGame: as declared in the protocol.
* Simply ask the player if they want to win, and get
* there reply.
*/
- (int) startGame: (NSString *)name
@{
NSString *aMessage;
id client;
int reply;
client = [currentPlayers objectForKey: name];
aMessage = @@"\nDo you want to win this game? (Y/N <RET>) ... ";
[client clientMessage: aMessage];
reply = [client clientReply];
if(reply == 'y' || reply == 'Y')
[hasWon setObject: @@"YES" forKey: name]; // They win.
else [hasWon setObject: @@"NO" forKey: name]; // They loose.
return 0;
@}
/*
* Implement endGame: as declared in the protocol.
* Removes a player from the game, and either sets
* a restriction on the player re-joining or removes
* the current restriction.
*/
- (BOOL) endGame: (NSString*)name
@{
id client;
NSString *aMessage, *yesOrNo;
NSDate *now, *delay;
NSTimeInterval twoHours = 2 * 60 * 60; // Seconds in 2 hours.
if (name == nil)
@{
NSLog(@@"Attempt to end nil user");
return NO;
@}
now = [NSDate date];
delay = [now addTimeInterval: twoHours];
client = [currentPlayers objectForKey: name];
yesOrNo = [hasWon objectForKey: name];
if ([yesOrNo isEqualToString: @@"YES"]) // Has player won?
@{
/*
* Player wins, no time delay to re-joining the game.
* Remove any re-joining restriction and send
* a message to the client.
*/
[delayUntil removeObjectForKey: name];
aMessage = @@"\nWell played: you can re-join GameServer at any time.\n";
[client clientMessage: aMessage];
@}
else // Player lost
@{
/*
* Set a time delay for re-joining the game,
* and send a message to the client.
*/
[delayUntil setObject: delay forKey: name];
aMessage = @@"\nYou lost, but you can re-join GameServer in 2 hours.\n";
[client clientMessage: aMessage];
@}
/* Remove the player from the current game. */
[currentPlayers removeObjectForKey: name];
[hasWon removeObjectForKey: name];
return YES;
@}
@@end // End of GameServer class implementation
/*
* The main function of the server program simply
* vends the root object and starts the runloop.
*/
int main(int argc, char** argv)
@{
CREATE_AUTORELEASE_POOL(pool);
GameServer *server;
NSConnection *connXion;
server = AUTORELEASE([GameServer new]);
connXion = [NSConnection defaultConnection];
[connXion setRootObject: server];
[connXion registerName: @@"JoinGame"];
[[NSRunLoop currentRunLoop] run];
RELEASE(pool);
return 0;
@}
@end example
To summarise the code at the server:
@itemize @bullet
@item We vend the server's root object and start a runloop,
allowing clients to connect with the server.@*@*
@item When we receive a proxy for a client object, we communicate
with that client using methods declared in the @code{ClientServer}
protocol.@*@*
@item We create three dictionary objects, each referenced by player
name. @code{currentUsers} holds proxies for each of the current
players; @code{delayUntil} holds times when each player can re-join
the game; and @code{hasWon} holds a string for each player, which is
set to "YES" if the player wins.@*@*
@item When the game is in progress, the server can alter the state of
each client object to reflect the success of each player.
@end itemize
I hope you managed to understand most of the code in this example. If
you are reading the on-screen version, then you can copy and paste the
code to suitably named files, create makefiles, and then make and run
each program. What message is displayed if you immediately try to
re-join a game after loosing? And after winning?
@i{Exercise: Modify the server code so that the server records the
number of wins for each player, and displays this information at both
the start and end of each game.}
@section Language Support for Distributed Objects
Objective-C provides special 'type' qualifiers that can be used in a
protocol to control the way that message arguments are passed between
remote processes, while at run time, the run-time system transparently
uses what is known as 'forward invocation' to forward messages to a
remote process.
@subsection Protocol Type Qualifiers
@cindex protocol type qualifiers
@cindex in, out, and inout type qualifiers
@cindex out, type qualifier
@cindex oneway, type qualifier
@cindex bycopy and byref type qualifiers
When message arguments are passed by value then the receiving method can
only alter the copy it receives, and not the value of the original
variable. When an argument is passed by reference (as a pointer), the
receiving method has access to the original variable and can alter that
variable's data. In this case the argument is effectively passed 'in' to
the method, and then passed 'out' of the method (on method return).
When an argument is passed by reference to a remote object, the network
must handle this two-way traffic, whether or not the remote object
modifies the received argument.
Type qualifiers can be used in a protocol to control the way these
messages are handled, and to indicate whether or not the sending process
will wait for the remote process to return.
@itemize @bullet
@item The @b{oneway} qualifier is used in conjunction with a
@code{void} return type to inform the run-time system that the sending
process does not need to wait for the receiving method to return (known
as 'asynchronous' messaging). The protocol declaration for the receiving
method would look something like this:@*@*
@code{- (@b{oneway} void)noWaitForReply;}@*@*
@item The @b{in, out } and @b{inout} qualifiers can be used with pointer
arguments to control the direction in which an argument is passed. The
protocol declaration for the receiving methods would look something like
this:@*
@example
/*
* The value that 'number' points to will be passed @b{in} to the remote process.
* (No need to return the argument's value from the remote process.)
*/
@code{- setValue: (@b{in} int *)number;}
/*
* The value that 'number' points to will be passed @b{out} of the remote process.
* (No need to send the argument's value to the remote process.)
*/
@code{- getValue: (@b{out} int *)number;}
/*
* The value that 'number' points to is first passed @b{in} to the remote
* process, but will eventually be the value that is passed @b{out} of the
* remote process. (Send and return the argument's value.)
*/
@code{- changeValue: (@b{inout} int *)number;}
@end example
@item The @b{bycopy} and @b{byref} qualifiers can be used in a protocol
when the argument or return type is an object.@*@*
An object is normally passed by reference and received in the remote
process as a proxy. When an object is passed by copy, then a copy of
the object will be received in the remote process, allowing the remote
process to directly interact with the copy. Protocol declarations would
look something like this:@*
@example
/*
* Copy of object will be received in the remote process.
*/
- sortNames: (@b{bycopy} id)listOfNames;
/*
* Copy of object will be returned by the remote process.
*/
- (@b{bycopy} id)returnNames;
@end example
By default, large objects are normally sent @b{byref}, while small
objects like @code{NSStrings} are normally sent @b{bycopy}, but you
cannot rely on these defaults being adopted and should explicitly state
the qualifier in the protocol.@*@*
The @b{bycopy} qualifier can also be used in conjunction with the
@b{out} qualifier, to indicate that an object will be passed @b{out} of
the remote process by copy rather than by proxy (no need to send the
object).@*
@example
/*
* The object will not be received in the remote process, but the object
* will be returned @b{bycopy}.
*/
- sortAndReturn: (@b{bycopy out} id *)listOfNames;
@end example
You should be aware that some classes ignore the @b{bycopy} qualifier
and the object will be sent by reference. The @b{bycopy} qualifier will
also be ignored if the remote process does not have the class of the
object in its address space, since an object's instance variables are
accessed through the object's methods.@*@*
When a copy of an object is sent to a remote process, only the object's
instance variables are sent and received (an object's methods exist in
the address space of the object's class, not in the address space of the
individual object).
@end itemize
@subsection Message Forwarding
@cindex message forwarding, distributed objects
@cindex forward invocation, distributed objects
When a proxy forwards a message to a remote object, the GNUstep run-time
extensions automatically implement what is known as 'forward
invocation'. At run-time, the message received at the proxy is
automatically re-packaged and sent to the proxy's
@code{forwardInvocation:} method. This method determines whether or not
a remote object exists that can respond to the received message,
forwarding the message on (or not), depending on the reply.@*
While this handling of messages by the proxy is transparent to the
distributed objects programmer, any object can implement forward
invocation, allowing it to pass on messages for which it has no
responding method. Forward invocation is discussed in more detail in the
next chapter.
@section Error Checking
@cindex error checking, distributed objects
@cindex distributed objects, error checking
When dealing with distributed objects your code must be able to handle
the following situations: failure to vend the server object, exceptions
raised at run-time, and failure of the network connection.
@subsection Vending the Server Object
When vending the server object, your code must be able to handle the
situation in which the network does not accept the proposed registered
name for the server.
@subsection Catching Exceptions
There are two situations to consider.
@itemize @bullet
@item An @code{NSPortTimeoutException} is raised.@*@*
This exception is raised if a message takes too long to arrive at the
remote process, or if a reply takes too long to return. This will happen
if the remote process is busy, has hung, or if there is a problem with
the network. The best way to handle the exception is to close the
connection to the remote process.@*@*
@item An exception is raised in the remote process while the remote
process is executing a method.@*@*
In most cases you can deal directly with these exceptions in the process
in which they were raised; i.e. without having to consider the network
connection itself.
@end itemize
@subsection The Connection Fails
You can register an observer object to receive a notification, in the
form of a @code{connectionDidDie} message, when a registered connection
fails. The argument to this message will be an @code{NSNotification}
object that returns the failed connection when it receives an
@code{object} message.
To receive this 'notification' the observer must implement the
@code{connectionDidDie} method, but can be an instance of any class. The
observer can then handle the failure gracefully, by releasing any
references to the failed connection and releasing proxies that used the
connection. Registering an object to receive this notification is
described in more detail in the @code{NSConnection} class documentation.
@subsection GameServer Example with Error Checking
Earlier on in this section we looked at an example client/server program
called GameServer, and you will find a further example of this program,
with additional error checking, in Appendix B.