90
90
this 'remote messaging' possible.
92
92
@subsection Code at the Server
93
@cindex server code, GNUstep
94
@cindex GNUstep server code
96
In order to respond to client messages, the responding server object
97
must be set as the 'root object' of an instance of the NSConnection
98
class, and this NSConnection must be registered with the network by
99
name. Making an object available to client processes in this way is
100
known as 'vending' the object. The registered name for the NSConnection
101
is used by the client when obtaining a proxy for the responding server
102
object over the network.
104
The only other code you need to consider is the code that listens for
105
incoming messages. This 'runloop' as it is known, is started by sending
106
a @code{run} message to an instance of the NSRunLoop class. Since an
107
NSRunLoop object is created automatically for each process, there is no
108
need to create one yourself. Simply get the default runloop, which is
109
returned by the @code{currentRunLoop} class method.
111
When the runloop detects an incoming message, the message is passed to
112
the root object of the NSConnection, which performs a method in response
113
to the message and returns a variable of the appropriate type. The
114
NSConnection manages all inter-process communication, decoding incoming
93
@cindex distributed objects, client code
95
In order to respond to client messages, the responding server object must be
96
set as the 'root object' of an instance of the @code{NSConnection} class, and
97
this @code{NSConnection} must be registered with the network by name. Making
98
an object available to client processes in this way is known as 'vending' the
99
object. The registered name for the @code{NSConnection} is used by the client
100
when obtaining a proxy for the responding server object over the network.
102
The only other code you need to consider is the code that listens for incoming
103
messages. This 'runloop', as it is known, is started by sending a @code{run}
104
message to an instance of the @code{NSRunLoop} class. Since an
105
@code{NSRunLoop} object is created automatically for each process, there is no
106
need to create one yourself. Simply get the default runloop, which is returned
107
by the @code{+currentRunLoop} class method.
109
When the runloop detects an incoming message, the message is passed to the
110
root object of the @code{NSConnection}, which performs a method in response to
111
the message and returns a variable of the appropriate type. The
112
@code{NSConnection} manages all inter-process communication, decoding incoming
115
113
messages and encoding any returned values.
114
@comment{Sequence diagram would be really useful here.}
117
116
The code to vend the @code{telephoneDirectory} object and start the
118
117
runloop would look something like this:
121
* The main() function: Set up the program
122
* as a 'Distributed Objects Server'.
122
127
* Remember, create an instance of the
123
128
* NSAutoreleasePool class.
258
265
an instance of the @code{TelephoneDirectory} class at the server, and
259
266
this class could implement the @code{TelephoneDirectory} protocol.
261
@section An Example Game Server
269
@subsection Complete Code for Telephone Directory Application
271
Here we provide the rest of the code needed for client and server to actually
272
run the above example.
277
#include <Foundation/Foundation.h>
279
/* Include the TelephoneDirectory protocol header file */
280
#include "TelephoneDirectory.h"
283
* Declare the TelephoneDirectory class that
284
* implements the 'teleNumber' instance method.
286
@@interface TelephoneDirectory : NSObject <TelephoneDirectory>
290
* Define the TelephoneDirectory class
291
* and the instance method (teleNumber).
293
@@implementation TelephoneDirectory : NSObject
294
- (char *) teleNumber: (char *) personName
296
if (strcmp(personName, "Jack") == 0) return " 0123 456";
297
else if (strcmp(personName, "Jill") == 0) return " 0456 789";
298
else return " Number not found";
302
/* main() function: Set up the program as a 'Distibuted Objects Server'. */
303
/* [use code from server example above ...] */
310
#include <Foundation/Foundation.h>
312
/* Include the TelephoneDirectory protocol header file */
313
#include "TelephoneDirectory.h"
316
* The main() function: Get the telephone number for
317
* 'personName' from the server registered as 'DirectoryServer'.
319
int main(int argc, char *argv[])
321
char *personName = argv[1];
322
char *returnedNumber;
323
id<TelephoneDirectory> proxyForDirectory;
324
CREATE_AUTORELEASE_POOL(pool);
326
/* Acquire the remote reference. */
327
proxyForDirectory = (id<TelephoneDirectory>) [NSConnection
328
rootProxyForConnectionWithRegisteredName:
332
if(proxyForDirectory == nil)
333
printf("\n** WARNING: NO CONNECTION TO SERVER **\n");
334
else printf("\n** Connected to server **\n");
336
if(argc == 2) // Command line name entered
338
returnedNumber = (char *)[proxyForDirectory teleNumber: personName];
339
printf("\n%s%s%s%s%s\n", "** (In client) The telephone number for ",
341
returnedNumber, " **");
343
else printf("\n** No name entered **\n");
344
printf("\n%s\n\n", "** End of client program **");
350
To get this running, all you need do is create two directories, one for the
351
client and one for the server. Each directory will hold a makefile, the client
352
or server source code, and a copy of the protocol header file. When the files
353
compile, first run the server and then the client. You can try this on the
354
same machine, or on two different machines (with GNUstep installed) on the
355
same LAN. What happens when you run the client without the server? How would
356
you display a "No Server Connection" warning at the client?
359
@subsection GNUstep Distributed Objects Name Server
361
@cindex Distributed Objects Name Server, GNUstep
363
You might wonder how the client finds the server, or, rather, how it finds the
364
directory the server lists itself in. In fact an auxiliary process will
365
automatically be started on each machine, if it isn't running already, that
366
handles this, allowing the server to register and the client to send a query
367
behind the scenes. This @i{GNUstep Distributed Objects Name Server} runs as
368
'@code{gdomap}' and binds to port 538. See the manual page or the HTML
369
``GNUstep Base Tools'' @uref{../../Tools/Reference/index.html,
370
documentation} for further information.
373
@subsection Look Ma, No Stubs!
375
One difference you may have noticed in the example we just looked at from
376
other remote method invocation interfaces such as CORBA and Java RMI was that
377
there are @i{no stub classes}. The source of this great boon is described at
378
the end of this chapter: @ref{Distributed Objects, , Language Support for
379
Distributed Objects}.
382
@section A More Involved Example
262
383
@cindex distributed objects, example (no error checking)
384
@cindex game server example
264
It would be tempting to show a complete example of the telephone
265
directory program, but a minimal amount of additional code is required
266
to turn the previous examples into a working implementation. Instead we
267
will look at an example called GameServer that uses distributed objects
386
Now we will look at an example called GameServer that uses distributed objects
268
387
in a client/server game.
270
@i{Exercise: Create a working client/server telephone directory on your
271
own by adding a few lines of code to the previous examples, perhaps with
272
a telephone directory holding only two or three names and numbers. At
273
first you could implement this as a single process and then when it
274
works, modify the code to run as client/server processes running on the
275
same machine. (Try passing the person's name into the client program as
276
a command-line argument. The server need only test the name, e.g. if
277
personName = "Fred" return "number".)}
279
@i{All you need do is create two directories, one for the client and one
280
for the server. Each directory will hold a GNUmakefile, the
281
client/server source code, and a copy of the protocol header file. When
282
the files compile, first run the server and then the client. What
283
happens when you run the client without the server? How would you
284
display a "No Server Connection" warning at the client? Appendix B
285
contains a small working example that you can compare with your own
288
@subsection What GameServer Does
289
@cindex game server example
291
Although there is no actual game to play, and while the code to vend an
292
object and connect to a remote process is similar to that already shown,
293
the code does show a number of additional techniques that can be used in
294
other client/server programs.
389
Actually the game itself is not implemented, just its distributed support
390
structure, and while the code to vend an object and connect to a remote
391
process is similar to that already shown, the code does show a number of
392
additional techniques that can be used in other client/server programs. Here
393
are the requirements we will implement:
297
@item When the client attempts to join the game, the server checks that
298
the client is entitled to join, based on the last time the client
300
The rule is: if the client lost the last game, then they cannot re-play
301
for the next 2 hours; but if the client won the last game, then they can
302
re-play the game at any time (a reward for winning).@*@*
304
@item The server also makes sure the client is not already connected and
305
playing the game (i.e. they cannot play two games at the same time -
306
that would be cheating).@*@*
308
@item In addition to a proxy for the server being obtained at the
309
client, a proxy for the client is received at the server. This allows
310
two-way messaging, where the client can send messages to the server and
311
the server can send messages to the client (e.g. the state of play may
312
be affected by the actions of other players, or by other events at the
397
When the client attempts to join the game, the server checks that the client
398
is entitled to join, based on the last time the client played. The rule is:
399
if the client lost the last game, then they cannot re-play for the next 2
400
hours; but if the client won the last game, then they can re-play the game at
401
any time (a reward for winning).@*
404
The server also makes sure the client is not already connected and playing the
405
game (i.e. they cannot play two games at the same time - that would be
409
In addition to a proxy for the server being obtained at the client, a proxy
410
for the client is received at the server. This allows two-way messaging, where
411
the client can send messages to the server and the server can send messages to
412
the client (e.g. the state of play may be affected by the actions of other
413
players, or by other events at the server).@*
315
415
Two protocols will therefore be required, one for the methods
316
416
implemented at the server and one for those implemented at the client.
319
Have a look at the program code and added comments. Can you work out
320
what is happening at the server and client? If you have any difficulties
321
then refer to the relevant sections in this manual, or to class
322
documentation at the GNUstep or Apple web-sites.
419
Have a look at the program code in the following sections and added
420
comments. Can you work out what is happening at the server and client? If you
421
have any difficulties then refer to the relevant sections in this manual, or
422
to class documentation @uref{../Reference/index.html, here} or at the Apple
324
426
@subsection Protocol Adopted at Client
718
822
To summarise the code at the server:
721
@item We vend the server's root object and start a runloop,
722
allowing clients to connect with the server.@*@*
724
@item When we receive a proxy for a client object, we communicate
725
with that client using methods declared in the @code{ClientServer}
728
@item We create three dictionary objects, each referenced by player
729
name. @code{currentUsers} holds proxies for each of the current
730
players; @code{delayUntil} holds times when each player can re-join
731
the game; and @code{hasWon} holds a string for each player, which is
732
set to "YES" if the player wins.@*@*
734
@item When the game is in progress, the server can alter the state of
735
each client object to reflect the success of each player.
826
We vend the server's root object and start a runloop, allowing clients to
827
connect with the server.@*
830
When we receive a proxy for a client object, we communicate with that client
831
using methods declared in the @code{ClientServer} protocol.@*
834
We create three dictionary objects, each referenced by player
835
name. @code{currentUsers} holds proxies for each of the current players;
836
@code{delayUntil} holds times when each player can re-join the game; and
837
@code{hasWon} holds a string for each player, which is set to "YES" if the
841
When the game is in progress, the server can alter the state of each client
842
object to reflect the success of each player.
738
845
I hope you managed to understand most of the code in this example. If
739
846
you are reading the on-screen version, then you can copy and paste the
740
847
code to suitably named files, create makefiles, and then make and run
741
848
each program. What message is displayed if you immediately try to
742
re-join a game after loosing? And after winning?
849
re-join a game after losing? And after winning?
744
851
@i{Exercise: Modify the server code so that the server records the
745
852
number of wins for each player, and displays this information at both
746
853
the start and end of each game.}
748
856
@section Language Support for Distributed Objects
750
858
Objective-C provides special 'type' qualifiers that can be used in a
751
859
protocol to control the way that message arguments are passed between
752
860
remote processes, while at run time, the run-time system transparently
753
861
uses what is known as 'forward invocation' to forward messages to a
862
remote process. (See @ref{Advanced Messaging, , Forwarding}.)
756
865
@subsection Protocol Type Qualifiers
757
866
@cindex protocol type qualifiers
891
1001
@subsection Message Forwarding
892
1002
@cindex message forwarding, distributed objects
893
1003
@cindex forward invocation, distributed objects
894
When a proxy forwards a message to a remote object, the GNUstep run-time
895
extensions automatically implement what is known as 'forward
896
invocation'. At run-time, the message received at the proxy is
897
automatically re-packaged and sent to the proxy's
898
@code{forwardInvocation:} method. This method determines whether or not
899
a remote object exists that can respond to the received message,
900
forwarding the message on (or not), depending on the reply.@*
902
While this handling of messages by the proxy is transparent to the
903
distributed objects programmer, any object can implement forward
904
invocation, allowing it to pass on messages for which it has no
905
responding method. Forward invocation is discussed in more detail in the
1005
If you have used other remote invocation mechanisms such as CORBA or Java
1006
RMI, you may have noticed a big difference from these in the GNUstep
1007
Distributed Object paradigm -- there are no ``stub'' classes, either on the
1008
client or the server. This tremendously simplifies the use of remote
1009
invocation and is possible due to the Objective-C message-forwarding facility
1010
(@ref{Advanced Messaging, , Forwarding}).
1012
In GNUstep, there are proxies on the client and server side that handle
1013
network communications and serialization/deserialization of arguments and
1014
return values just as in CORBA and RMI, but when it comes to responding to the
1015
client and server protocol method calls themselves, they are intercepted
1016
through the use of the @code{forwardInvocation:} method, where they can be
1017
passed on to the registered client and server objects through the ordinary
1018
Objective-C message sending mechanism.
909
1021
@section Error Checking