update for current api

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@23913 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
rfm 2006-10-19 08:19:07 +00:00
parent 785619760b
commit 2a475a30a2
2 changed files with 204 additions and 119 deletions

View file

@ -1,3 +1,7 @@
2006-10-19 Richard Frith-Macdonald <rfm@gnu.org>
* Documentation/manual/DistributedObjects.texi: Update for current API
2006-10-19 Matt Rice <ratmice@yahoo.com>
* Source/NSBundle.m (_find_framework): initialize file_name variable.

View file

@ -21,7 +21,7 @@ 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
@section Object Interaction
@cindex object interaction, remote objects
To continue with the example above, if the telephone directory existed
@ -47,7 +47,7 @@ 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
@section The GNUstep Solution
@cindex distributed objects
@cindex remote objects
@cindex client/server processes
@ -89,7 +89,7 @@ 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
@subsection Code at the Server
@cindex distributed objects, client code
In order to respond to client messages, the responding server object must be
@ -118,43 +118,43 @@ runloop would look something like this:
@example
/*
* The main() function: Set up the program
* The main() function: Set up the program
* as a 'Distributed Objects Server'.
*/
int main(void)
@{
/*
* Remember, create an instance of the
* 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).
* (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)
if ([connXion registerName: @@"DirectoryServer"] == NO)
@{
NSLog(@@"Unable to register as 'DirectoryServer'");
NSLog(@@"Perhaps another copy of this program is running?");
exit(1);
@}
/* Start the current runloop. */
[[NSRunLoop currentRunLoop] run];
/* Release the pool */
RELEASE(pool);
return 0;
@ -164,7 +164,7 @@ int main(void)
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
@subsection Code at the Client
@cindex distributed objects, client code
At the client, all you need do is obtain a proxy for the responding
@ -176,21 +176,92 @@ at the server.
CREATE_AUTORELEASE_POOL(pool);
/* Get the proxy */
id proxy = [NSConnection
rootProxyForConnectionWithRegisteredName:
@i{registeredServerName}
host: @i{hostName}];
id proxy = [NSConnection
rootProxyForConnectionWithRegisteredName: @i{registeredServerName}];
/* 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
to create one yourself.
The above example serves to establish a secure connection between processes
which are run by the same person and are both on the same host.
If you want your connections to work between different host or between
programs being run by different people, you do this slightly differently,
telling the system that you want to use 'socket' ports, which make TCP/IP
connections over the network.
@example
int main(void)
@{
CREATE_AUTORELEASE_POOL(pool);
/*
* Create a new socket port for your connection.
*/
NSSocketPort *port = [NSSocketPort port];
/*
* Create a connection using the socket port.
*/
NSConnection *connXion = [NSConnection connectionWithReceivePort: port
sendPort: port];
/*
* Set the responding server object as
* the root object for this connection.
*/
[connXion setRootObject: telephoneDirectory];
/*
* Try to register a name for the NSConnection,
* and report an error if this is not possible.
*/
if ([connXion registerName: @@"DirectoryServer"
withNameServer: [NSSocketPortNameServer sharedInstance]] == NO)
@{
NSLog(@@"Unable to register as 'DirectoryServer'");
NSLog(@@"Perhaps another copy of this program is running?");
exit(1);
@}
[[NSRunLoop currentRunLoop] run];
RELEASE(pool);
return 0;
@}
@end example
In the above example, we specify that the socket port name server is used
to register the name for the connection ... this makes the connection name
visible to processes running on other machines.
The client side code is as follows
@example
/* Create an instance of the NSAutoreleasePool class */
CREATE_AUTORELEASE_POOL(pool);
/* Get the proxy */
id proxy = [NSConnection
rootProxyForConnectionWithRegisteredName: @i{registeredServerName}
host: @i{hostName}
usingNameServer: [NSSocketPortNameServer sharedInstance]];
/* The rest of your program code goes here */
/* Release the pool */
RELEASE(pool);
@end example
If the @i{hostName} in this statement is 'nil'
or an empty string, then only the local host will be searched to find the
@i{registeredServerName}. If @i{hostName} is "*", then all hosts on the
local network will be searched.
@ -199,9 +270,9 @@ any host on the network would be:
@example
id proxyForDirectory = [NSConnection
rootProxyForConnectionWithRegisteredName:
@@"DirectoryServer"
host: "*"];
rootProxyForConnectionWithRegisteredName: @@"DirectoryServer"
host: @@"*"
usingNameServer: [NSSocketPortNameServer sharedInstance]];
@end example
With this additional line of code in the client program, you can now
@ -213,7 +284,7 @@ object.
@end example
@subsection Using a Protocol
@subsection Using a Protocol
@cindex protocol for distributed objects
@cindex distributed objects, using a protocol
@ -246,15 +317,14 @@ In the telephone directory example, if the declared protocol was
@example
#include "protocolHeader.h";
/* Extend the type declaration */
id<TelephoneDirectory> proxyForDirectory;
/* Cast the returned proxy object to the extended type */
/* Cast the returned proxy object to the extended type */
proxyForDirectory = (id<TelephoneDirectory>) [NSConnection
rootProxyForConnectionWithRegisteredName:
@@"DirectoryServer"
host: "*"];
rootProxyForConnectionWithRegisteredName: @@"DirectoryServer"
usingNameServer: [NSSocketPortNameServer sharedInstance]];
@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
@ -280,14 +350,14 @@ run the above example.
#include "TelephoneDirectory.h"
/*
* Declare the TelephoneDirectory class that
* Declare the TelephoneDirectory class that
* implements the 'teleNumber' instance method.
*/
@@interface TelephoneDirectory : NSObject <TelephoneDirectory>
@@end
/*
* Define the TelephoneDirectory class
* Define the TelephoneDirectory class
* and the instance method (teleNumber).
*/
@@implementation TelephoneDirectory : NSObject
@ -313,7 +383,7 @@ run the above example.
#include "TelephoneDirectory.h"
/*
* The main() function: Get the telephone number for
* The main() function: Get the telephone number for
* 'personName' from the server registered as 'DirectoryServer'.
*/
int main(int argc, char *argv[])
@ -324,16 +394,16 @@ int main(int argc, char *argv[])
CREATE_AUTORELEASE_POOL(pool);
/* Acquire the remote reference. */
proxyForDirectory = (id<TelephoneDirectory>) [NSConnection
rootProxyForConnectionWithRegisteredName:
@@"DirectoryServer"
host: @@"*"];
proxyForDirectory = (id<TelephoneDirectory>) [NSConnection
rootProxyForConnectionWithRegisteredName: @@"DirectoryServer"
host: @@"*"
usingNameServer: [NSSocketPortNameServer sharedInstance]];
if(proxyForDirectory == nil)
if (proxyForDirectory == nil)
printf("\n** WARNING: NO CONNECTION TO SERVER **\n");
else printf("\n** Connected to server **\n");
if(argc == 2) // Command line name entered
if (argc == 2) // Command line name entered
@{
returnedNumber = (char *)[proxyForDirectory teleNumber: personName];
printf("\n%s%s%s%s%s\n", "** (In client) The telephone number for ",
@ -361,7 +431,14 @@ you display a "No Server Connection" warning at the client?
@cindex Distributed Objects Name Server, GNUstep
You might wonder how the client finds the server, or, rather, how it finds the
directory the server lists itself in. In fact an auxiliary process will
directory the server lists itself in.
For the default connection type (a connection only usable on the local host
between processes run by the same person), a private file (or the registry
on ms-windows) is used to hold the name registration information.
For connections using socket ports to communicate between hosts,
an auxiliary process will
automatically be started on each machine, if it isn't running already, that
handles this, allowing the server to register and the client to send a query
behind the scenes. This @i{GNUstep Distributed Objects Name Server} runs as
@ -423,7 +500,7 @@ to class documentation @uref{../Reference/index.html, here} or at the Apple
web site.
@subsection Protocol Adopted at Client
@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
@ -435,7 +512,7 @@ the class must implement.
- (void) clientMessage: (bycopy NSString *)theMessage;
- (int) clientReply;
// Other methods would be added that
// Other methods would be added that
// reflect the nature of the game.
@@end
@ -456,7 +533,7 @@ the class must implement.
- (int) startGame: (bycopy NSString*)name;
- (BOOL) endGame: (bycopy NSString*)name;
// Other methods would be added that
// Other methods would be added that
// reflect the nature of the game.
@@end
@ -477,19 +554,19 @@ The @code{main()} function attempts to connect to the server, while the
#include "GameServer.h"
#include "GameClient.h"
/*
* GameClient class declaration:
/*
* 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.
*/
@ -498,9 +575,9 @@ The @code{main()} function attempts to connect to the server, while the
printf([theMessage cString]);
@}
/*
/*
* Implement clientReply: as declared in the protocol.
* The method simply returns the character entered
* The method simply returns the character entered
* at the client keyboard.
*/
- (int) clientReply
@ -522,33 +599,34 @@ int main(int argc, char **argv)
/*
* The NSUserName() function returns the name of the
* current user, which is sent to the server when we
* 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.
* 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
* 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)
server = (id<GameServer>)[NSConnection
rootProxyForConnectionWithRegisteredName: @@"JoinGame"
host: @@"*"
usingNameServer: [NSSocketPortNameServer sharedInstance]];
if (server == nil)
@{
printf("\n** No Connection to GameServer **\n");
result = 1;
@}
/*
* Try to join the game, passing a GameClient object as
* 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.
*/
@ -590,8 +668,8 @@ object to reflect the success of the player.
@subsection Code at the Server
The server code contains the @code{main} function and the
@code{GameServer} class declaration and implementation.
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}
@ -604,9 +682,9 @@ player information).
#include "GameServer.h"
#include "GameClient.h"
/*
/*
* GameServer class declaration:
* Adopt the GameServer protocol and declare
* Adopt the GameServer protocol and declare
* GameServer instance variables.
*/
@@interface GameServer : NSObject <GameServer>
@ -617,7 +695,7 @@ player information).
@}
@@end
/*
/*
* GameServer class implementation.
*/
@@implementation GameServer
@ -628,26 +706,26 @@ player information).
self = [super init];
if (self != nil)
@{
/*
* Create a dictionary for a maximum of
* 10 named players that will hold a
/*
* Create a dictionary for a maximum of
* 10 named players that will hold a
* re-joining time delay.
*/
delayUntil = [[NSMutableDictionary alloc]
delayUntil = [[NSMutableDictionary alloc]
initWithCapacity: 10];
/*
* Create a dictionary that will hold the
/*
* Create a dictionary that will hold the
* names of these players and a proxy for
* the received client objects.
*/
currentPlayers = [[NSMutableDictionary alloc]
currentPlayers = [[NSMutableDictionary alloc]
initWithCapacity: 10];
/*
* Create a dictionary that will record
/*
* Create a dictionary that will record
* a win for any of these named players.
*/
hasWon = [[NSMutableDictionary alloc]
hasWon = [[NSMutableDictionary alloc]
initWithCapacity: 10];
@}
return self;
@ -664,7 +742,7 @@ player information).
/*
* Implement mayJoin:: as declared in the protocol.
* Adds the client to the list of current players.
* 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,
@ -680,7 +758,7 @@ player information).
NSLog(@@"Attempt to join nil user");
return NO;
@}
/* Has the player already joined the game? */
if ([currentPlayers objectForKey: name] != nil)
@{
@ -689,39 +767,39 @@ player information).
[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;
/*
* 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
else
@{
/* Inform the client that they cannot re-join. */
aMessage = @@"\nSorry, you cannot re-join GameServer yet.\n";
[client clientMessage: aMessage];
return NO;
return NO;
@}
@}
/*
/*
* Implement startGame: as declared in the protocol.
* Simply ask the player if they want to win, and get
* there reply.
@ -731,20 +809,20 @@ player information).
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')
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
@ -756,34 +834,34 @@ player information).
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.
* 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,
@{
/*
* Set a time delay for re-joining the game,
* and send a message to the client.
*/
[delayUntil setObject: delay forKey: name];
@ -800,19 +878,22 @@ player information).
@@end // End of GameServer class implementation
/*
* The main function of the server program simply
* 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;
NSSocketPort *port;
NSConnection *connXion;
server = AUTORELEASE([GameServer new]);
connXion = [NSConnection defaultConnection];
port = [NSSocketPort port];
connXion = [NSConnection connectionWithReceivePort: port sendPort: port];
[connXion setRootObject: server];
[connXion registerName: @@"JoinGame"];
[connXion registerName: @@"JoinGame"
withNameServer: [NSSocketPortNameServer sharedInstance]];
[[NSRunLoop currentRunLoop] run];
RELEASE(pool);
return 0;
@ -886,34 +967,34 @@ 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
@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
@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
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
* 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;}
@ -949,18 +1030,18 @@ type.
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
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;