~ubuntu-branches/ubuntu/karmic/gnustep-base/karmic

« back to all changes in this revision

Viewing changes to Documentation/manual/DistributedObjects.texi

  • Committer: Bazaar Package Importer
  • Author(s): Eric Heintzmann
  • Date: 2005-04-17 00:14:38 UTC
  • mfrom: (1.2.1 upstream) (2.1.2 hoary)
  • Revision ID: james.westby@ubuntu.com-20050417001438-enf0y07c9tku85z1
Tags: 1.10.3-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
56
56
@cindex NSRunLoop class
57
57
 
58
58
GNUstep overcomes these limitations by providing you with classes that
59
 
form, what is known as, a 'distributed objects' architecture that
 
59
form what is known as a 'distributed objects' architecture that
60
60
extends the capabilities of the run-time system.
61
61
 
62
62
With the addition of a few lines of code in the client and server
90
90
this 'remote messaging' possible.
91
91
 
92
92
@subsection Code at the Server 
93
 
@cindex server code, GNUstep
94
 
@cindex GNUstep server code
95
 
 
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.
103
 
 
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.
110
 
 
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
 
94
 
 
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.
 
101
 
 
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.
 
108
 
 
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.}
116
115
 
117
116
The code to vend the @code{telephoneDirectory} object and start the
118
117
runloop would look something like this:
119
118
 
120
119
@example
 
120
/*
 
121
 * The main() function: Set up the program 
 
122
 * as a 'Distributed Objects Server'.
 
123
 */
 
124
int main(void)
 
125
@{
121
126
  /*
122
127
   * Remember, create an instance of the 
123
128
   * NSAutoreleasePool class.
152
157
  
153
158
  /* Release the pool */
154
159
  RELEASE(pool);
 
160
  return 0;
 
161
@}
155
162
@end example
156
163
 
157
164
These additional lines of code turn a program into a distributed objects
158
165
server, ready to respond to incoming client messages.
159
166
 
160
167
@subsection Code at the Client 
161
 
@cindex client code, GNUstep
162
 
@cindex GNUstep client code
 
168
@cindex distributed objects, client code
163
169
 
164
170
At the client, all you need do is obtain a proxy for the responding
165
 
server object, using the name that was registered for the NSConnection
 
171
server object, using the name that was registered for the @code{NSConnection}
166
172
at the server.
167
173
 
168
174
@example
171
177
 
172
178
  /* Get the proxy */
173
179
  id proxy = [NSConnection 
174
 
              rootProxyForConnectionWithRegisteredName:
175
 
              @i{registeredServerName}
176
 
              host: @i{hostName}]; 
 
180
                rootProxyForConnectionWithRegisteredName:
 
181
                  @i{registeredServerName}
 
182
                host: @i{hostName}]; 
177
183
              
178
184
  /* The rest of your program code goes here */
179
185
  
186
192
to create one yourself. If the @i{hostName} in this statement is 'nil',
187
193
then only the local host will be searched to find the
188
194
@i{registeredServerName}. If @i{hostName} is "*", then all hosts on the
189
 
network will be searched.
 
195
local network will be searched.
190
196
 
191
197
In the telephone directory example, the code to obtain the proxy from
192
198
any host on the network would be:
193
199
 
194
200
@example
195
201
  id proxyForDirectory = [NSConnection
196
 
                          rootProxyForConnectionWithRegisteredName: 
197
 
                          @@"DirectoryServer" 
198
 
                          host: "*"]; 
 
202
                            rootProxyForConnectionWithRegisteredName: 
 
203
                              @@"DirectoryServer" 
 
204
                            host: "*"]; 
199
205
@end example
200
206
 
201
207
With this additional line of code in the client program, you can now
206
212
  NSString *wantedNumber = [proxyForDirectory teleNumber: personName];
207
213
@end example
208
214
 
 
215
 
209
216
@subsection Using a Protocol 
210
217
@cindex protocol for distributed objects
211
218
@cindex distributed objects, using a protocol
258
265
an instance of the @code{TelephoneDirectory} class at the server, and
259
266
this class could implement the @code{TelephoneDirectory} protocol.
260
267
 
261
 
@section An Example Game Server
 
268
 
 
269
@subsection Complete Code for Telephone Directory Application
 
270
 
 
271
Here we provide the rest of the code needed for client and server to actually
 
272
run the above example.
 
273
 
 
274
@b{Code At Server}
 
275
 
 
276
@example
 
277
#include <Foundation/Foundation.h>
 
278
 
 
279
/* Include the TelephoneDirectory protocol header file */
 
280
#include "TelephoneDirectory.h"
 
281
 
 
282
/*
 
283
 * Declare the TelephoneDirectory class that 
 
284
 * implements the 'teleNumber' instance method.
 
285
 */
 
286
@@interface TelephoneDirectory : NSObject <TelephoneDirectory>
 
287
@@end
 
288
 
 
289
/*
 
290
 * Define the TelephoneDirectory class 
 
291
 * and the instance method (teleNumber).
 
292
 */
 
293
@@implementation TelephoneDirectory : NSObject
 
294
- (char *) teleNumber: (char *) personName
 
295
@{
 
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";
 
299
@}
 
300
@@end
 
301
 
 
302
 /* main() function: Set up the program  as a 'Distibuted Objects Server'. */
 
303
 /*   [use code from server example above ...] */
 
304
@end example
 
305
 
 
306
@b{Code at Client}
 
307
 
 
308
 
 
309
@example
 
310
#include <Foundation/Foundation.h>
 
311
 
 
312
/* Include the TelephoneDirectory protocol header file */
 
313
#include "TelephoneDirectory.h"
 
314
 
 
315
/*
 
316
 * The main() function: Get the telephone number for 
 
317
 * 'personName' from the server registered as 'DirectoryServer'.
 
318
 */
 
319
int main(int argc, char *argv[])
 
320
@{
 
321
  char *personName = argv[1];
 
322
  char *returnedNumber;
 
323
  id<TelephoneDirectory> proxyForDirectory;
 
324
  CREATE_AUTORELEASE_POOL(pool);
 
325
 
 
326
  /* Acquire the remote reference. */
 
327
  proxyForDirectory = (id<TelephoneDirectory>) [NSConnection 
 
328
                       rootProxyForConnectionWithRegisteredName: 
 
329
                       @@"DirectoryServer" 
 
330
                       host: @@"*"];           
 
331
 
 
332
  if(proxyForDirectory == nil) 
 
333
    printf("\n** WARNING: NO CONNECTION TO SERVER **\n");
 
334
  else printf("\n** Connected to server **\n");
 
335
  
 
336
  if(argc == 2) // Command line name entered
 
337
  @{
 
338
    returnedNumber = (char *)[proxyForDirectory teleNumber: personName];
 
339
    printf("\n%s%s%s%s%s\n", "** (In client) The telephone number for ",
 
340
           personName, " is:",
 
341
           returnedNumber, "  **");
 
342
  @}
 
343
  else printf("\n** No name entered **\n");
 
344
  printf("\n%s\n\n", "** End of client program **");
 
345
  RELEASE(pool);
 
346
  return 0;
 
347
@}
 
348
@end example
 
349
 
 
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?
 
357
 
 
358
 
 
359
@subsection GNUstep Distributed Objects Name Server
 
360
@cindex gdomap
 
361
@cindex Distributed Objects Name Server, GNUstep
 
362
 
 
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.
 
371
 
 
372
 
 
373
@subsection Look Ma, No Stubs!
 
374
 
 
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}.
 
380
 
 
381
 
 
382
@section A More Involved Example
262
383
@cindex distributed objects, example (no error checking)
 
384
@cindex game server example
263
385
 
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.
269
388
 
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".)}
278
 
 
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
286
 
implementation.}
287
 
 
288
 
@subsection What GameServer Does
289
 
@cindex game server example
290
 
 
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:
295
394
 
296
395
@itemize @bullet
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
299
 
played. 
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).@*@*
303
 
 
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).@*@*
307
 
 
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 
313
 
server).@*@*
 
396
@item
 
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).@*
 
402
 
 
403
@item
 
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
 
406
cheating).@*
 
407
 
 
408
@item
 
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).@*
314
414
 
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.
317
417
@end itemize
318
418
 
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
 
423
web site.
 
424
 
323
425
 
324
426
@subsection Protocol Adopted at Client 
325
427
 
471
573
To summarise the code at the client:
472
574
 
473
575
@itemize @bullet
474
 
@item We obtained a proxy for the server and can now communicate 
475
 
with the server using the methods declared in the @code{GameServer} 
476
 
protocol.@*@*
477
 
 
478
 
@item We passed a @code{GameClient} object and our user-name to the 
479
 
server (the @code{GameClient} object is received as a proxy at the 
480
 
server).  The server can now communicate with the client using the 
481
 
methods declared in the @code{GameClient} protocol.@*@*
482
 
 
483
 
@item When the game is in progress, the server can alter the state 
484
 
of the client object to reflect the success of the player.
 
576
@item
 
577
We obtained a proxy for the server and can now communicate with the server
 
578
using the methods declared in the @code{GameServer} protocol.@*
 
579
 
 
580
@item
 
581
We passed a @code{GameClient} object and our user-name to the server (the
 
582
@code{GameClient} object is received as a proxy at the server).  The server
 
583
can now communicate with the client using the methods declared in the
 
584
@code{GameClient} protocol.@*
 
585
 
 
586
@item
 
587
When the game is in progress, the server can alter the state of the client
 
588
object to reflect the success of the player.
485
589
@end itemize
486
590
 
487
591
@subsection Code at the Server
718
822
To summarise the code at the server:
719
823
 
720
824
@itemize @bullet
721
 
@item We vend the server's root object and start a runloop, 
722
 
allowing clients to connect with the server.@*@*
723
 
 
724
 
@item When we receive a proxy for a client object, we communicate 
725
 
with that client using methods declared in the @code{ClientServer} 
726
 
protocol.@*@*
727
 
 
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.@*@*
733
 
 
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.
 
825
@item
 
826
We vend the server's root object and start a runloop, allowing clients to
 
827
connect with the server.@*
 
828
 
 
829
@item
 
830
When we receive a proxy for a client object, we communicate with that client
 
831
using methods declared in the @code{ClientServer} protocol.@*
 
832
 
 
833
@item
 
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
 
838
player wins.@*
 
839
 
 
840
@item
 
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.
736
843
@end itemize
737
844
 
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?
743
850
 
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.}
747
854
 
 
855
 
748
856
@section Language Support for Distributed Objects
749
857
 
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
754
 
remote process.
 
862
remote process.  (See @ref{Advanced Messaging, , Forwarding}.)
 
863
 
755
864
 
756
865
@subsection Protocol Type Qualifiers
757
866
@cindex protocol type qualifiers
759
868
@cindex out, type qualifier
760
869
@cindex oneway, type qualifier
761
870
@cindex bycopy and byref type qualifiers
 
871
 
762
872
When message arguments are passed by value then the receiving method can
763
873
only alter the copy it receives, and not the value of the original
764
874
variable. When an argument is passed by reference (as a pointer), the
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.@*
901
 
 
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
906
 
next chapter.
 
1004
 
 
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}).
 
1011
 
 
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.
907
1019
 
908
1020
 
909
1021
@section Error Checking
941
1053
form of a @code{connectionDidDie:} message, when a registered connection
942
1054
fails. The argument to this message will be an @code{NSNotification}
943
1055
object that returns the failed connection when it receives an
944
 
@code{object} message.
 
1056
@code{object} message.  See @ref{Base Library, , Event-Based Communications}
 
1057
for more information on notifications.
945
1058
 
946
1059
To receive this 'notification' the observer must implement the
947
1060
@code{connectionDidDie:} method, but can be an instance of any class. The
950
1063
connection. Registering an object to receive this notification is
951
1064
described in more detail in the @code{NSConnection} class documentation.
952
1065
 
953
 
@subsection GameServer Example with Error Checking
954
 
 
955
 
Earlier on in this section we looked at an example client/server program
956
 
called GameServer, and you will find a further example of this program,
957
 
with additional error checking, in Appendix B.
958
 
 
959
 
 
 
1066
@comment{Need some comments on reference counting and disposal.}
 
1067
 
 
1068
 
 
1069
@page
960
1070