|
|
Sample Programs from NeXSTEP 3.3
/*
* VoteTally.m Written by Joe Freeman 5/27/92
*
* Note:
* You can talk back to a remote object, if it gives you
* its id. But, you cannot, while responding to a message that came
* remote, send a message to a remote server in the same process. That
* be blocked out by the conversation that you are responding to.
*
* You can talk to the NXConnection object in the client, that
* was created when connecting to the server, if the client has
* done a runFromAppkit using that connection object
*
* Also,
* clientList = nil if we are not the server
*
*/
#import "VoteTally.h"
@implementation VoteTally
#define numAttendees ([clientList count] )
- appDidInit:sender
{
/* You should really give it the name of the machine you want it to
* connect to. The "*" will cause the server to look at every machine
* on the subnet. That could take a LONG time.
*/
remoteHub = [NXConnection connectToName:"VoteTally" onHost:"*"];
if (remoteHub){
[clearAllMenu setEnabled:NO];
/* if someone is already the hub, then we will be the client
* we find the NXConnection object that is handling the Proxy
* that represents the vote server. We then register this object
* to handle errors for that server
*
* the client understands more than this protocol
* but these are used most so lets tell it about the them.
*/
[[scrollers window] setTitle: "Voting Client"];
ourServer = [remoteHub connectionForProxy];
[remoteHub setProtocolForProxy:@protocol(VoteTallyServerMethods)];
} else {
/* The fact that no one is registered, means that we are going to
* be the server. So, we register our connection
*
* The server understands the client protocol also
* but these are the most common messages
*/
clientList = [[List alloc] init];
[[scrollers window] setTitle: "Voting Server"];
ourServer = [NXConnection registerRoot: self withName:"VoteTally"];
/* add ourselves as a psuedo client */
remoteHub = self;
}
/*
* we take ourServer, which represets the NXConnection objec that
* will handle all our proxys (?) and make ourselves the handler for
* errors for that object. Then we run from appkit.
*
* The case for the voting server makes sense. We need to run from
* appkit so that we can server all the clients.
*
* The case for the clients is not as clear. We register the
* NXConnection that is behind our connection to the vote server
* so that the server, which will have our ID will be able to talk
* back without the client having initiated the connection. This is
* NOT OBVIOUS from the PR1 docs
*/
[ourServer registerForInvalidationNotification:self];
[ourServer runFromAppKit];
[remoteHub addClient:self];
return self;
}
- appWillTerminate:sender
{
[remoteHub removeClient:self];
return self;
}
- free
{
[super free];
[ourServer free];
return self;
}
/* ============================================================
* who is part of this conference anyway?
*
* All three of these messages may cause traffic to ALL the
* clients which means the NXConnection objects on those
* need to be free. So, we declare these oneway to release
*
* ============================================================ */
- addClient:sender
{
id connectionToClient;
if (sender != self){
/* do some error trapping and optimizaitons */
/* figure out who is behind this new client and get their errors */
connectionToClient = [sender connectionForProxy];
[connectionToClient registerForInvalidationNotification:self];
/* make sure this new object is smart about the client messages */
[sender setProtocolForProxy:@protocol(VoteTallyClientMethods)];
}
/* do some bookeeping */
[clientList addObjectIfAbsent:sender];
[self updateStats];
return self;
}
- removeClient:anObject
{
[clientList removeObject:anObject];
if ( anObject != self ){
/* the server should rebuild the stats */
[self updateStats];
}
return self;
}
/* an updateStats:message means two different things, depending on server
* Server rebuilds the stats and then tells all clients to get stats
* Client gets all the stats from the server
*/
#define NUMSLIDERS 6
- (void) updateStats
{
int last = numAttendees;
int i,s;
float sliders[NUMSLIDERS];
float tmp;
int iTmp;
/// fprintf(stderr,"%d updateStats with remote hub = %d\n", self, remoteHub);
[remoteHub getNumAttend:&i];
[numVoting setIntValue: i];
if (remoteHub != self){
for ( s = 0 ; s < NUMSLIDERS; s++){
[remoteHub getMean:&tmp at:s];
[[means findCellWithTag:s] setFloatValue:tmp];
}
} else {
/* build new stats */
for ( s = 0 ; s < NUMSLIDERS; s++){
sliders[s] = 0.0;
for ( i = 0 ; i < last; i++){
[[clientList objectAt:i] getValue:&iTmp at:s];
sliders[s]+= iTmp;
}
sliders[s] = sliders[s]/numAttendees;
[[means findCellWithTag:s] setFloatValue:sliders[s]];
}
/// fprintf(stderr,"%d stats rebuilt \n", self);
/* tell all the clients to update their displays from us */
for ( i = 0 ; i < last ; i++){
if ([clientList objectAt:i] != self)
[[clientList objectAt:i] updateStats];
}
}
}
/* ============================================================
*
* ============================================================ */
/*
* only the hub is allowed to restore everyone's score
*/
- resetAll:sender
{
[clientList makeObjectsPerform:@selector(resetLocal:) with:self];
[self updateStats];
return self;
}
/* reset is the same as moving all sliders. So, we should notify hub
* if the hub sent us this message, then it already knows
*/
- resetLocal:sender
{
int i,numRows,numCols;
[scrollers getNumRows:&numRows numCols:&numCols];
for ( i = 0 ; i < numCols; i++){
[[scrollers findCellWithTag:i] setIntValue:50];
}
if (sender != remoteHub)
[remoteHub updateStats];
return self;
}
/* a slider has moved... have the hub query everyone for new stats */
- takeVoteFrom:sender
{
[remoteHub updateStats];
return self;
}
/* ============================================================
*
* ============================================================ */
- getNumAttend:(int *)anInt
{
*anInt = numAttendees;
return self;
}
- getValue:(int *)aVal at:(int)index
{
*aVal = [[scrollers findCellWithTag:index] intValue];
return self;
}
- getMean:(float *)aFloat at:(int)index
{
*aFloat = [[means findCellWithTag:index] floatValue];
return self;
}
/* ============================================================
*
* part of the registerForInvalidationNotification stuff
*
*
* ============================================================ */
- senderIsInvalid:sender
{
if (self != remoteHub){
/* server is dead, so lets kill ourselves */
remoteHub = nil;
[NXApp terminate:self];
} else {
/* the server ran into a dead client */
[self removeClient:sender];
}
return self;
}
@end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.