1@paragraphindent 0
2
3@node Distributed Objects
4@chapter Distributed Objects
5@cindex distributed objects
6
7Until now we have been concentrating on using the Objective-C language
8to create programs that execute in a single process. But what if you
9want your program to interact with objects in other processes, perhaps
10running on different machines?
11
12As a simple example, we may have a client process that needs to access a
13telephone directory stored on a remote server. The client process could
14send a message to the server that contained a person's name, and the
15server could respond by returning that person's number.
16
17The GNUstep base library provides a powerful set of classes that make
18this type of remote messaging not only possible, but easy to program. So
19what do these classes do and how can we use them? To answer that we must
20first look at the way code interacts with objects in a single process,
21and then look at how we can achieve the same interaction with objects
22that exist in different processes.
23
24@section Object Interaction
25@cindex object interaction, remote objects
26
27To continue with the example above, if the telephone directory existed
28in the same process as the code that was accessing it, then a simple
29message would return the wanted telephone number.
30
31@example
32  NSString *wantedNumber = [telephoneDirectory teleNumber: personName];
33@end example
34
35Now object and method names just hold pointers to memory addresses. The
36code executed at run time in response to the @code{teleNumber} message
37is located at an address held by the name of the responding method (a
38variable), while data in the telephone directory is located at an
39address held by the @code{telephoneDirectory} variable.
40
41In a single process these addresses can be accessed by the client code
42at run time, but if the telephone directory is located on a remote
43server, then the address of the remote object is not known in the client
44process (the @code{telephoneDirectory} object and its responding method
45are said to exist in a separate 'address space').
46
47The Objective-C run-time library was not designed for this inter-process
48communication or 'remote messaging'.
49
50@section The GNUstep Solution
51@cindex distributed objects
52@cindex remote objects
53@cindex client/server processes
54@cindex NSConnection class
55@cindex NSProxy class
56@cindex NSRunLoop class
57
58GNUstep overcomes these limitations by providing you with classes that
59form what is known as a 'distributed objects' architecture that
60extends the capabilities of the run-time system.
61
62With the addition of a few lines of code in the client and server
63programs, these extensions allow you to send a message to a remote
64process by constructing a simple Objective-C statement. In the telephone
65directory example, the statement to retrieve the telephone number would
66now look something like this:
67
68@example
69  NSString *wantedNumber = [proxyForDirectory teleNumber: personName];
70@end example
71
72Compare this to the original statement:
73
74@example
75  NSString *wantedNumber = [telephoneDirectory teleNumber: personName];
76@end example
77
78Notice that the only difference between the two statements is the name
79of the object receiving the message, i.e. @code{proxyForDirectory}
80rather than @code{telephoneDirectory}. GNUstep makes it as simple as
81this to communicate with an object in another process.
82
83The variable @code{proxyForDirectory} is known as a 'proxy' for the
84remote @code{telephoneDirectory} object. A proxy is simply a substitute
85for the remote object, with an address in the 'address space' of the
86local client process, that receives messages and forwards them on to the
87remote server process in a suitably coded form.
88
89Let us now take a look at the additional lines of code required to make
90this 'remote messaging' possible.
91
92@subsection Code at the Server
93@cindex distributed objects, client code
94
95In order to respond to client messages, the responding server object must be
96set as the 'root object' of an instance of the @code{NSConnection} class, and
97this @code{NSConnection} must be registered with the network by name. Making
98an object available to client processes in this way is known as 'vending' the
99object. The registered name for the @code{NSConnection} is used by the client
100when obtaining a proxy for the responding server object over the network.
101
102The only other code you need to consider is the code that listens for incoming
103messages. This 'runloop', as it is known, is started by sending a @code{run}
104message to an instance of the @code{NSRunLoop} class.  Since an
105@code{NSRunLoop} object is created automatically for each process, there is no
106need to create one yourself. Simply get the default runloop, which is returned
107by the @code{+currentRunLoop} class method.
108
109When the runloop detects an incoming message, the message is passed to the
110root object of the @code{NSConnection}, which performs a method in response to
111the message and returns a variable of the appropriate type. The
112@code{NSConnection} manages all inter-process communication, decoding incoming
113messages and encoding any returned values.
114@comment{Sequence diagram would be really useful here.}
115
116The code to vend the @code{telephoneDirectory} object and start the
117runloop would look something like this:
118
119@example
120/*
121 * The main() function: Set up the program
122 * as a 'Distributed Objects Server'.
123 */
124int main(void)
125@{
126  /*
127   * Remember, create an instance of the
128   * NSAutoreleasePool class.
129   */
130  CREATE_AUTORELEASE_POOL(pool);
131
132  /*
133   * Get the default NSConnection object
134   * (a new one is automatically created if none exists).
135   */
136  NSConnection *connXion = [NSConnection defaultConnection];
137
138  /*
139   * Set the responding server object as
140   * the root object for this connection.
141   */
142  [connXion setRootObject: telephoneDirectory];
143
144  /*
145   * Try to register a name for the NSConnection,
146   * and report an error if this is not possible.
147   */
148  if ([connXion registerName: @@"DirectoryServer"] == NO)
149  @{
150    NSLog(@@"Unable to register as 'DirectoryServer'");
151    NSLog(@@"Perhaps another copy of this program is running?");
152    exit(1);
153  @}
154
155  /* Start the current runloop. */
156  [[NSRunLoop currentRunLoop] run];
157
158  /* Release the pool */
159  RELEASE(pool);
160  return 0;
161@}
162@end example
163
164These additional lines of code turn a program into a distributed objects
165server, ready to respond to incoming client messages.
166
167@subsection Code at the Client
168@cindex distributed objects, client code
169
170At the client, all you need do is obtain a proxy for the responding
171server object, using the name that was registered for the @code{NSConnection}
172at the server.
173
174@example
175  /* Create an instance of the NSAutoreleasePool class */
176  CREATE_AUTORELEASE_POOL(pool);
177
178  /* Get the proxy */
179  id proxy = [NSConnection
180    rootProxyForConnectionWithRegisteredName: @i{registeredServerName}];
181
182  /* The rest of your program code goes here */
183
184  /* Release the pool */
185  RELEASE(pool);
186@end example
187
188The code that obtains the proxy automatically creates an NSConnection
189object for managing the inter-process communication, so there is no need
190to create one yourself.
191
192The above example serves to establish a secure connection between processes
193which are run by the same person and are both on the same host.
194
195If you want your connections to work between different host or between
196programs being run by different people, you do this slightly differently,
197telling the system that you want to use 'socket' ports, which make TCP/IP
198connections over the network.
199
200@example
201int main(void)
202@{
203  CREATE_AUTORELEASE_POOL(pool);
204
205  /*
206   * Create a new socket port for your connection.
207   */
208  NSSocketPort *port = [NSSocketPort port];
209
210  /*
211   * Create a connection using the socket port.
212   */
213  NSConnection *connXion = [NSConnection connectionWithReceivePort: port
214							  sendPort: port];
215
216  /*
217   * Set the responding server object as
218   * the root object for this connection.
219   */
220  [connXion setRootObject: telephoneDirectory];
221
222  /*
223   * Try to register a name for the NSConnection,
224   * and report an error if this is not possible.
225   */
226  if ([connXion registerName: @@"DirectoryServer"
227    withNameServer: [NSSocketPortNameServer sharedInstance]] == NO)
228  @{
229    NSLog(@@"Unable to register as 'DirectoryServer'");
230    NSLog(@@"Perhaps another copy of this program is running?");
231    exit(1);
232  @}
233
234  [[NSRunLoop currentRunLoop] run];
235
236  RELEASE(pool);
237  return 0;
238@}
239@end example
240
241In the above example, we specify that the socket port name server is used
242to register the name for the connection ... this makes the connection name
243visible to processes running on other machines.
244
245The client side code is as follows
246
247@example
248  /* Create an instance of the NSAutoreleasePool class */
249  CREATE_AUTORELEASE_POOL(pool);
250
251  /* Get the proxy */
252  id proxy = [NSConnection
253    rootProxyForConnectionWithRegisteredName: @i{registeredServerName}
254    host: @i{hostName}
255    usingNameServer: [NSSocketPortNameServer sharedInstance]];
256
257  /* The rest of your program code goes here */
258
259  /* Release the pool */
260  RELEASE(pool);
261@end example
262
263If the @i{hostName} in this statement is 'nil'
264or an empty string, then only the local host will be searched to find the
265@i{registeredServerName}. If @i{hostName} is "*", then all hosts on the
266local network will be searched.
267
268In the telephone directory example, the code to obtain the proxy from
269any host on the network would be:
270
271@example
272  id proxyForDirectory = [NSConnection
273    rootProxyForConnectionWithRegisteredName: @@"DirectoryServer"
274    host: @@"*"
275    usingNameServer: [NSSocketPortNameServer sharedInstance]];
276@end example
277
278With this additional line of code in the client program, you can now
279construct a simple Objective-C statement to communicate with the remote
280object.
281
282@example
283  NSString *wantedNumber = [proxyForDirectory teleNumber: personName];
284@end example
285
286
287@subsection Using a Protocol
288@cindex protocol for distributed objects
289@cindex distributed objects, using a protocol
290
291A client process does not need to know the class of a remote server
292object to avoid run-time errors, it only needs to know the messages to
293which the remote object responds. This can be determined by the client
294at run-time, by asking the server if it responds to a particular message
295before the message is sent.
296
297If the methods implemented at the server are stated in a formal
298protocol, then the client can ask the server if it conforms to the
299protocol, reducing the network traffic required for the individual
300message/response requests.
301
302A further advantage is gained at compile time, when the compiler will
303issue a warning if the server fails to implement any method declared in
304the protocol, or if the client contains any message to which the server
305cannot respond.
306
307The protocol is saved to a header file and then included in both client
308and server programs with the usual compiler @code{#include}
309directive. Only the server program needs to implement the methods
310declared in the protocol. To enable compiler checking in the client
311program, extend the type declaration for the proxy to this protocol, and
312cast the returned proxy object to the same extended type.
313
314In the telephone directory example, if the declared protocol was
315@code{TelephoneDirectory}, declared in header file
316@code{protocolHeader.h}, then the client code would now look like this:
317
318@example
319  #include "protocolHeader.h";
320
321  /* Extend the type declaration */
322  id<TelephoneDirectory> proxyForDirectory;
323
324  /* Cast the returned proxy object to the extended type */
325  proxyForDirectory = (id<TelephoneDirectory>) [NSConnection
326    rootProxyForConnectionWithRegisteredName: @@"DirectoryServer"
327    usingNameServer: [NSSocketPortNameServer sharedInstance]];
328@end example
329Since class names and protocol names do not share the same 'address
330space' in a process, the declared protocol and the class of the
331responding server object can share the same name, making code easier to
332understand.
333
334For example, @code{proxyForDirectory} at the client could be a proxy for
335an instance of the @code{TelephoneDirectory} class at the server, and
336this class could implement the @code{TelephoneDirectory} protocol.
337
338
339@subsection Complete Code for Telephone Directory Application
340
341Here we provide the rest of the code needed for client and server to actually
342run the above example.
343
344@b{Code At Server}
345
346@example
347#include <Foundation/Foundation.h>
348
349/* Include the TelephoneDirectory protocol header file */
350#include "TelephoneDirectory.h"
351
352/*
353 * Declare the TelephoneDirectory class that
354 * implements the 'teleNumber' instance method.
355 */
356@@interface TelephoneDirectory : NSObject <TelephoneDirectory>
357@@end
358
359/*
360 * Define the TelephoneDirectory class
361 * and the instance method (teleNumber).
362 */
363@@implementation TelephoneDirectory : NSObject
364- (char *) teleNumber: (char *) personName
365@{
366  if (strcmp(personName, "Jack") == 0) return " 0123 456";
367  else if (strcmp(personName, "Jill") == 0) return " 0456 789";
368  else return " Number not found";
369@}
370@@end
371
372 /* main() function: Set up the program  as a 'Distibuted Objects Server'. */
373 /*   [use code from server example above ...] */
374@end example
375
376@b{Code at Client}
377
378
379@example
380#include <Foundation/Foundation.h>
381
382/* Include the TelephoneDirectory protocol header file */
383#include "TelephoneDirectory.h"
384
385/*
386 * The main() function: Get the telephone number for
387 * 'personName' from the server registered as 'DirectoryServer'.
388 */
389int main(int argc, char *argv[])
390@{
391  char *personName = argv[1];
392  char *returnedNumber;
393  id<TelephoneDirectory> proxyForDirectory;
394  CREATE_AUTORELEASE_POOL(pool);
395
396  /* Acquire the remote reference. */
397  proxyForDirectory = (id<TelephoneDirectory>) [NSConnection
398    rootProxyForConnectionWithRegisteredName: @@"DirectoryServer"
399    host: @@"*"
400    usingNameServer: [NSSocketPortNameServer sharedInstance]];
401
402  if (proxyForDirectory == nil)
403    printf("\n** WARNING: NO CONNECTION TO SERVER **\n");
404  else printf("\n** Connected to server **\n");
405
406  if (argc == 2) // Command line name entered
407  @{
408    returnedNumber = (char *)[proxyForDirectory teleNumber: personName];
409    printf("\n%s%s%s%s%s\n", "** (In client) The telephone number for ",
410           personName, " is:",
411           returnedNumber, "  **");
412  @}
413  else printf("\n** No name entered **\n");
414  printf("\n%s\n\n", "** End of client program **");
415  RELEASE(pool);
416  return 0;
417@}
418@end example
419
420To get this running, all you need do is create two directories, one for the
421client and one for the server. Each directory will hold a makefile, the client
422or server source code, and a copy of the protocol header file. When the files
423compile, first run the server and then the client.  You can try this on the
424same machine, or on two different machines (with GNUstep installed) on the
425same LAN. What happens when you run the client without the server? How would
426you display a "No Server Connection" warning at the client?
427
428
429@subsection GNUstep Distributed Objects Name Server
430@cindex gdomap
431@cindex Distributed Objects Name Server, GNUstep
432
433You might wonder how the client finds the server, or, rather, how it finds the
434directory the server lists itself in.
435
436For the default connection type (a connection only usable on the local host
437between processes run by the same person), a private file (or the registry
438on ms-windows) is used to hold the name registration information.
439
440For connections using socket ports to communicate between hosts,
441an auxiliary process will
442automatically be started on each machine, if it isn't running already, that
443handles this, allowing the server to register and the client to send a query
444behind the scenes.  This @i{GNUstep Distributed Objects Name Server} runs as
445'@code{gdomap}' and binds to port 538.  See the manual page or the HTML
446``GNUstep Base Tools'' @uref{../../Tools/Reference/index.html,
447documentation} for further information.
448
449
450@subsection Look Ma, No Stubs!
451
452One difference you may have noticed in the example we just looked at from
453other remote method invocation interfaces such as CORBA and Java RMI was that
454there are @i{no stub classes}.  The source of this great boon is described at
455the end of this chapter: @ref{Distributed Objects, , Language Support for
456Distributed Objects}.
457
458
459@section A More Involved Example
460@cindex distributed objects, example (no error checking)
461@cindex game server example
462
463Now we will look at an example called GameServer that uses distributed objects
464in a client/server game.
465
466Actually the game itself is not implemented, just its distributed support
467structure, and while the code to vend an object and connect to a remote
468process is similar to that already shown, the code does show a number of
469additional techniques that can be used in other client/server programs.  Here
470are the requirements we will implement:
471
472@itemize @bullet
473@item
474When the client attempts to join the game, the server checks that the client
475is entitled to join, based on the last time the client played.  The rule is:
476if the client lost the last game, then they cannot re-play for the next 2
477hours; but if the client won the last game, then they can re-play the game at
478any time (a reward for winning).@*
479
480@item
481The server also makes sure the client is not already connected and playing the
482game (i.e. they cannot play two games at the same time - that would be
483cheating).@*
484
485@item
486In addition to a proxy for the server being obtained at the client, a proxy
487for the client is received at the server. This allows two-way messaging, where
488the client can send messages to the server and the server can send messages to
489the client (e.g. the state of play may be affected by the actions of other
490players, or by other events at the server).@*
491
492Two protocols will therefore be required, one for the methods
493implemented at the server and one for those implemented at the client.
494@end itemize
495
496Have a look at the program code in the following sections and added
497comments. Can you work out what is happening at the server and client? If you
498have any difficulties then refer to the relevant sections in this manual, or
499to class documentation @uref{../Reference/index.html, here} or at the Apple
500web site.
501
502
503@subsection Protocol Adopted at Client
504
505We have chosen @code{GameClient} as the name of both the protocol
506adopted at the client and the class of the responding client object. The
507header file declaring this protocol will simply declare the methods that
508the class must implement.
509
510@example
511@@protocol GameClient
512- (void) clientMessage: (bycopy NSString *)theMessage;
513- (int) clientReply;
514
515// Other methods would be added that
516// reflect the nature of the game.
517
518@@end
519@end example
520
521The protocol will be saved as @code{GameClient.h}.
522
523@subsection Protocol Adopted at Server
524
525We have chosen @code{GameServer} as the name of both the protocol
526adopted at the server and the class of the responding server object. The
527header file declaring this protocol will simply declare the methods that
528the class must implement.
529
530@example
531@@protocol GameServer
532- (BOOL) mayJoin: (id)client asPlayer: (bycopy NSString*)name;
533- (int) startGame: (bycopy NSString*)name;
534- (BOOL) endGame: (bycopy NSString*)name;
535
536// Other methods would be added that
537// reflect the nature of the game.
538
539@@end
540@end example
541
542The protocol will be saved as @code{GameServer.h}.
543
544@subsection Code at the Client
545
546The client code contains the @code{main} function and the
547@code{GameClient} class declaration and implementation.
548
549The @code{main()} function attempts to connect to the server, while the
550@code{GameClient} class adopts the @code{GameClient} protocol.
551
552@example
553#include <Foundation/Foundation.h>
554#include "GameServer.h"
555#include "GameClient.h"
556
557/*
558 * GameClient class declaration:
559 * Adopt the GameClient protocol.
560 */
561@@interface GameClient : NSObject <GameClient>
562@@end
563
564/*
565 * GameClient class implementation.
566 */
567@@implementation GameClient
568
569/*
570 * Implement clientMessage: as declared in the protocol.
571 * The method simply prints a message at the client.
572 */
573- (void) clientMessage: (NSString*)theMessage
574@{
575  printf([theMessage cString]);
576@}
577
578/*
579 * Implement clientReply: as declared in the protocol.
580 * The method simply returns the character entered
581 * at the client keyboard.
582 */
583- (int) clientReply
584@{
585  return getchar();
586@}
587@@end  // End of GameClient class implementation.
588
589/*
590 * The main function of the client program.
591 */
592int main(int argc, char **argv)
593@{
594  CREATE_AUTORELEASE_POOL(pool);
595  id<GameServer> server;
596  int result;
597  NSString *name;
598  id client;
599
600  /*
601   * The NSUserName() function returns the name of the
602   * current user, which is sent to the server when we
603   * try to join the game.
604   */
605  name = NSUserName();
606
607  /*
608   * Create a GameClient object that is sent to
609   * the server when we try to join the game.
610   */
611  client = AUTORELEASE([GameClient new]);
612
613  /*
614   * Try to get a proxy for the root object of a server
615   * registered under the name 'JoinGame'. Since the host
616   * is '*', we can connect to any server on the local network.
617   */
618  server = (id<GameServer>)[NSConnection
619    rootProxyForConnectionWithRegisteredName: @@"JoinGame"
620    host: @@"*"
621    usingNameServer: [NSSocketPortNameServer sharedInstance]];
622  if (server == nil)
623    @{
624      printf("\n** No Connection to GameServer **\n");
625      result = 1;
626    @}
627
628  /*
629   * Try to join the game, passing a GameClient object as
630   * the client, and our user-name as name. The 'client'
631   * argument will be received as a proxy at the server.
632   */
633  else if ([server mayJoin: client asPlayer: name] == NO)
634    @{
635      result = 1; // We cannot join the game.
636    @}
637  else
638    @{
639      /*
640       * At this point, we would actually start to play the game.
641       */
642      [server startGame: name]; // Start playing game.
643      [server endGame: name]; // Finally end the game.
644      result = 0;
645    @}
646  RELEASE(pool);
647  return result;
648@}
649@end example
650
651To summarise the code at the client:
652
653@itemize @bullet
654@item
655We obtained a proxy for the server and can now communicate with the server
656using the methods declared in the @code{GameServer} protocol.@*
657
658@item
659We passed a @code{GameClient} object and our user-name to the server (the
660@code{GameClient} object is received as a proxy at the server).  The server
661can now communicate with the client using the methods declared in the
662@code{GameClient} protocol.@*
663
664@item
665When the game is in progress, the server can alter the state of the client
666object to reflect the success of the player.
667@end itemize
668
669@subsection Code at the Server
670
671The server code contains the @code{main} function and the
672@code{GameServer} class declaration and implementation.
673
674The @code{main()} function vends the server's root object and starts the
675runloop, while the @code{GameServer} class adopts the @code{GameServer}
676protocol. The class also implements methods that initialise and
677deallocate the root object's instance variables (dictionaries that hold
678player information).
679
680@example
681#include <Foundation/Foundation.h>
682#include "GameServer.h"
683#include "GameClient.h"
684
685/*
686 * GameServer class declaration:
687 * Adopt the GameServer protocol and declare
688 * GameServer instance variables.
689 */
690@@interface GameServer : NSObject <GameServer>
691@{
692  NSMutableDictionary *delayUntil; // Delays to re-joining GameServer.
693  NSMutableDictionary *currentPlayers; // Proxies to each client.
694  NSMutableDictionary *hasWon; // Success in game for each player.
695@}
696@@end
697
698/*
699 * GameServer class implementation.
700 */
701@@implementation GameServer
702
703/* Initialise GameServer's instance variables. */
704- (id) init
705@{
706  self = [super init];
707  if (self != nil)
708    @{
709      /*
710       * Create a dictionary for a maximum of
711       * 10 named players that will hold a
712       * re-joining time delay.
713       */
714      delayUntil = [[NSMutableDictionary alloc]
715                     initWithCapacity: 10];
716      /*
717       * Create a dictionary that will hold the
718       * names of these players and a proxy for
719       * the received client objects.
720       */
721      currentPlayers = [[NSMutableDictionary alloc]
722                       initWithCapacity: 10];
723
724      /*
725       * Create a dictionary that will record
726       * a win for any of these named players.
727       */
728      hasWon = [[NSMutableDictionary alloc]
729                 initWithCapacity: 10];
730    @}
731  return self;
732@}
733
734/* Release GameServer's instance variables. */
735- (void) dealloc
736@{
737  RELEASE(delayUntil);
738  RELEASE(currentPlayers);
739  RELEASE(hasWon);
740  [super dealloc];
741@}
742
743/*
744 * Implement mayJoin:: as declared in the protocol.
745 * Adds the client to the list of current players.
746 * Each player is represented at the server by both
747 * name and by proxy to the received client object.
748 * A player cannot join the game if they are already playing,
749 * or if joining has been delayed until a later date.
750 */
751- (BOOL) mayJoin: (id)client asPlayer: (NSString*)name
752@{
753  NSDate  *delay; // The time a player can re-join the game.
754  NSString *aMessage;
755
756  if (name == nil)
757    @{
758      NSLog(@@"Attempt to join nil user");
759      return NO;
760    @}
761
762  /* Has the player already joined the game? */
763  if ([currentPlayers objectForKey: name] != nil)
764    @{
765      /* Inform the client that they cannot join. */
766      aMessage = @@"\nSorry, but you are already playing GameServer!\n";
767      [client clientMessage: aMessage];
768      return NO;
769    @}
770
771  /* Get the player's time delay for re-joining. */
772  delay = [delayUntil objectForKey: name];
773
774  /*
775   * Can the player join the game? Yes if there is
776   * no restriction or if the time delay has passed;
777   * otherwise no, they cannot join.
778   */
779  if (delay == nil || [delay timeIntervalSinceNow] <= 0.0)
780    @{
781      /* Remove the old restriction on re-joining the game. */
782      [delayUntil removeObjectForKey: name];
783
784      /* Add the player to the list of current players. */
785      [currentPlayers setObject: client forKey: name];
786      [hasWon setObject: @@"NO" forKey: name]; // They've not won yet.
787
788      /* Inform the client that they have joined the game. */
789      aMessage = @@"\nWelcome to GameServer\n";
790      [client clientMessage: aMessage];
791      return YES;
792    @}
793  else
794    @{
795      /* Inform the client that they cannot re-join. */
796      aMessage = @@"\nSorry, you cannot re-join GameServer yet.\n";
797      [client clientMessage: aMessage];
798      return NO;
799    @}
800@}
801
802/*
803 * Implement startGame: as declared in the protocol.
804 * Simply ask the player if they want to win, and get
805 * there reply.
806 */
807- (int) startGame: (NSString *)name
808@{
809  NSString *aMessage;
810  id client;
811  int reply;
812
813  client = [currentPlayers objectForKey: name];
814
815  aMessage = @@"\nDo you want to win this game? (Y/N <RET>) ... ";
816  [client clientMessage: aMessage];
817
818  reply = [client clientReply];
819  if (reply == 'y' || reply == 'Y')
820    [hasWon setObject: @@"YES" forKey: name]; // They win.
821  else [hasWon setObject: @@"NO" forKey: name]; // They loose.
822  return 0;
823@}
824
825/*
826 * Implement endGame: as declared in the protocol.
827 * Removes a player from the game, and either sets
828 * a restriction on the player re-joining or removes
829 * the current restriction.
830 */
831- (BOOL) endGame: (NSString*)name
832@{
833  id client;
834  NSString *aMessage, *yesOrNo;
835  NSDate *now, *delay;
836  NSTimeInterval twoHours = 2 * 60 * 60; // Seconds in 2 hours.
837
838  if (name == nil)
839    @{
840      NSLog(@@"Attempt to end nil user");
841      return NO;
842    @}
843
844  now = [NSDate date];
845  delay = [now addTimeInterval: twoHours];
846  client = [currentPlayers objectForKey: name];
847  yesOrNo = [hasWon objectForKey: name];
848
849  if ([yesOrNo isEqualToString: @@"YES"]) // Has player won?
850    @{
851      /*
852       * Player wins, no time delay to re-joining the game.
853       * Remove any re-joining restriction and send
854       * a message to the client.
855       */
856      [delayUntil removeObjectForKey: name];
857      aMessage = @@"\nWell played: you can re-join GameServer at any time.\n";
858      [client clientMessage: aMessage];
859
860    @}
861  else // Player lost
862    @{
863      /*
864       * Set a time delay for re-joining the game,
865       * and send a message to the client.
866       */
867      [delayUntil setObject: delay forKey: name];
868      aMessage = @@"\nYou lost, but you can re-join GameServer in 2 hours.\n";
869      [client clientMessage: aMessage];
870    @}
871
872  /* Remove the player from the current game. */
873  [currentPlayers removeObjectForKey: name];
874  [hasWon removeObjectForKey: name];
875  return YES;
876@}
877
878@@end  // End of GameServer class implementation
879
880/*
881 * The main function of the server program simply
882 * vends the root object and starts the runloop.
883 */
884int main(int argc, char** argv)
885@{
886  CREATE_AUTORELEASE_POOL(pool);
887  GameServer	*server;
888  NSSocketPort	*port;
889  NSConnection	*connXion;
890
891  server = AUTORELEASE([GameServer new]);
892  port = [NSSocketPort port];
893  connXion = [NSConnection connectionWithReceivePort: port sendPort: port];
894  [connXion setRootObject: server];
895  [connXion registerName: @@"JoinGame"
896    withNameServer: [NSSocketPortNameServer sharedInstance]];
897  [[NSRunLoop currentRunLoop] run];
898  RELEASE(pool);
899  return 0;
900@}
901@end example
902
903To summarise the code at the server:
904
905@itemize @bullet
906@item
907We vend the server's root object and start a runloop, allowing clients to
908connect with the server.@*
909
910@item
911When we receive a proxy for a client object, we communicate with that client
912using methods declared in the @code{ClientServer} protocol.@*
913
914@item
915We create three dictionary objects, each referenced by player
916name. @code{currentUsers} holds proxies for each of the current players;
917@code{delayUntil} holds times when each player can re-join the game; and
918@code{hasWon} holds a string for each player, which is set to "YES" if the
919player wins.@*
920
921@item
922When the game is in progress, the server can alter the state of each client
923object to reflect the success of each player.
924@end itemize
925
926I hope you managed to understand most of the code in this example. If
927you are reading the on-screen version, then you can copy and paste the
928code to suitably named files, create makefiles, and then make and run
929each program. What message is displayed if you immediately try to
930re-join a game after losing? And after winning?
931
932@i{Exercise: Modify the server code so that the server records the
933number of wins for each player, and displays this information at both
934the start and end of each game.}
935
936
937@section Language Support for Distributed Objects
938
939Objective-C provides special 'type' qualifiers that can be used in a
940protocol to control the way that message arguments are passed between
941remote processes, while at run time, the run-time system transparently
942uses what is known as 'forward invocation' to forward messages to a
943remote process.  (See @ref{Advanced Messaging, , Forwarding}.)
944
945
946@subsection Protocol Type Qualifiers
947@cindex protocol type qualifiers
948@cindex in, out, and inout type qualifiers
949@cindex out, type qualifier
950@cindex oneway, type qualifier
951@cindex bycopy and byref type qualifiers
952
953When message arguments are passed by value then the receiving method can
954only alter the copy it receives, and not the value of the original
955variable. When an argument is passed by reference (as a pointer), the
956receiving method has access to the original variable and can alter that
957variable's data. In this case the argument is effectively passed 'in' to
958the method, and then passed 'out' of the method (on method return).
959
960When an argument is passed by reference to a remote object, the network
961must handle this two-way traffic, whether or not the remote object
962modifies the received argument.
963
964Type qualifiers can be used in a protocol to control the way these
965messages are handled, and to indicate whether or not the sending process
966will wait for the remote process to return.
967
968@itemize @bullet
969@item  The @b{oneway} qualifier is used in conjunction with a
970@code{void} return type to inform the run-time system that the sending
971process does not need to wait for the receiving method to return (known
972as 'asynchronous' messaging). The protocol declaration for the receiving
973method would look something like this:@*@*
974
975@code{- (@b{oneway} void)noWaitForReply;}@*@*
976
977@item The @b{in, out } and @b{inout} qualifiers can be used with pointer
978arguments to control the direction in which an argument is passed. The
979protocol declaration for the receiving methods would look something like
980this:@*
981
982@example
983/*
984 * The value that 'number' points to will be passed @b{in} to the remote process.
985 * (No need to return the argument's value from the remote process.)
986 */
987@code{- setValue: (@b{in} int *)number;}
988
989/*
990 * The value that 'number' points to will be passed @b{out} of the remote process.
991 * (No need to send the argument's value to the remote process.)
992 */
993@code{- getValue: (@b{out} int *)number;}
994
995/*
996 * The value that 'number' points to is first passed @b{in} to the remote
997 * process, but will eventually be the value that is passed @b{out} of the
998 * remote process. (Send and return the argument's value.)
999 */
1000@code{- changeValue: (@b{inout} int *)number;}
1001@end example
1002
1003Passing of arguments by reference is very restricted in Objective-C.
1004it applies only to pointers to C data types, not to objects, and except
1005for the special case of a pointer to a nul terminated C string (@b{char*})
1006the pointer is assumed to refer to a single data item of the specified
1007type.
1008
1009@example
1010/*
1011 * A method passing an unsigned short integer by reference.
1012 */
1013@code{- updateCounter: (@b{inout} unsigned shortn *)value;}
1014
1015/*
1016 * A method passing a structure by reference.
1017 */
1018@code{- updateState: (@b{inout} struct stateInfo *)value;}
1019
1020/*
1021 * As a special case, a char (or equivalent typedef) passed by reference
1022 * is assumed to be a nul terminated string ... there is no way to pass
1023 * a single character by reference:
1024 */
1025@code{- updateBuffer: (@b{inout} char *)str;}
1026
1027@end example
1028
1029@item The @b{bycopy} and @b{byref} qualifiers can be used in a protocol
1030when the argument or return type is an object.@*@*
1031
1032An object is normally passed by reference and received in the remote
1033process as a proxy. When an object is passed by copy, then a copy of
1034the object will be received in the remote process, allowing the remote
1035process to directly interact with the copy. Protocol declarations would
1036look something like this:@*
1037
1038@example
1039/*
1040 * Copy of object will be received in the remote process.
1041 */
1042- sortNames: (@b{bycopy} id)listOfNames;
1043
1044/*
1045 * Copy of object will be returned by the remote process.
1046 */
1047- (@b{bycopy} id)returnNames;
1048@end example
1049
1050By default, large objects are normally sent @b{byref}, while small
1051objects like @code{NSStrings} are normally sent @b{bycopy}, but you
1052cannot rely on these defaults being adopted and should explicitly state
1053the qualifier in the protocol.@*@*
1054
1055
1056The @b{bycopy} qualifier can also be used in conjunction with the
1057@b{out} qualifier, to indicate that an object will be passed @b{out} of
1058the remote process by copy rather than by proxy (no need to send the
1059object).@*
1060
1061@example
1062/*
1063 * The object will not be received in the remote process, but the object
1064 * will be returned @b{bycopy}.
1065 */
1066- sortAndReturn: (@b{bycopy out} id *)listOfNames;
1067@end example
1068
1069You should be aware that some classes ignore the @b{bycopy} qualifier
1070and the object will be sent by reference. The @b{bycopy} qualifier will
1071also be ignored if the remote process does not have the class of the
1072object in its address space, since an object's instance variables are
1073accessed through the object's methods.@*@*
1074
1075When a copy of an object is sent to a remote process, only the object's
1076instance variables are sent and received (an object's methods exist in
1077the address space of the object's class, not in the address space of the
1078individual object).
1079
1080@end itemize
1081
1082@subsection Message Forwarding
1083@cindex message forwarding, distributed objects
1084@cindex forward invocation, distributed objects
1085
1086If you have used other remote invocation mechanisms such as CORBA or Java
1087RMI, you may have noticed a big difference from these in the GNUstep
1088Distributed Object paradigm -- there are no ``stub'' classes, either on the
1089client or the server.  This tremendously simplifies the use of remote
1090invocation and is possible due to the Objective-C message-forwarding facility
1091(@ref{Advanced Messaging, , Forwarding}).
1092
1093In GNUstep, there are proxies on the client and server side that handle
1094network communications and serialization/deserialization of arguments and
1095return values just as in CORBA and RMI, but when it comes to responding to the
1096client and server protocol method calls themselves, they are intercepted
1097through the use of the @code{forwardInvocation:} method, where they can be
1098passed on to the registered client and server objects through the ordinary
1099Objective-C message sending mechanism.
1100
1101
1102@section Error Checking
1103@cindex error checking, distributed objects
1104@cindex distributed objects, error checking
1105
1106When dealing with distributed objects your code must be able to handle
1107the following situations: failure to vend the server object, exceptions
1108raised at run-time, and failure of the network connection.
1109
1110@subsection Vending the Server Object
1111When vending the server object, your code must be able to handle the
1112situation in which the network does not accept the proposed registered
1113name for the server.
1114
1115@subsection Catching Exceptions
1116There are two situations to consider.
1117@itemize @bullet
1118@item An @code{NSPortTimeoutException} is raised.@*@*
1119This exception is raised if a message takes too long to arrive at the
1120remote process, or if a reply takes too long to return. This will happen
1121if the remote process is busy, has hung, or if there is a problem with
1122the network. The best way to handle the exception is to close the
1123connection to the remote process.@*@*
1124
1125@item An exception is raised in the remote process while the remote
1126process is executing a method.@*@*
1127In most cases you can deal directly with these exceptions in the process
1128in which they were raised; i.e. without having to consider the network
1129connection itself.
1130@end itemize
1131
1132@subsection The Connection Fails
1133You can register an observer object to receive a notification, in the
1134form of a @code{connectionDidDie:} message, when a registered connection
1135fails. The argument to this message will be an @code{NSNotification}
1136object that returns the failed connection when it receives an
1137@code{object} message.  See @ref{Base Library, , Event-Based Communications}
1138for more information on notifications.
1139
1140To receive this 'notification' the observer must implement the
1141@code{connectionDidDie:} method, but can be an instance of any class. The
1142observer can then handle the failure gracefully, by releasing any
1143references to the failed connection and releasing proxies that used the
1144connection. Registering an object to receive this notification is
1145described in more detail in the @code{NSConnection} class documentation.
1146
1147@comment{Need some comments on reference counting and disposal.}
1148
1149
1150@page
1151
1152