From ba066c34dde204aa192d03a23a81356374d93731 Mon Sep 17 00:00:00 2001 From: chatty Date: Wed, 7 Apr 1993 11:50:31 +0000 Subject: Initial revision --- comm/OLD/Server.cc | 395 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 395 insertions(+) create mode 100644 comm/OLD/Server.cc (limited to 'comm/OLD/Server.cc') diff --git a/comm/OLD/Server.cc b/comm/OLD/Server.cc new file mode 100644 index 0000000..a2e908b --- /dev/null +++ b/comm/OLD/Server.cc @@ -0,0 +1,395 @@ +/* + * The Unix Channel + * + * by Michel Beaudouin-Lafon + * + * Copyright 1990-1993 + * Laboratoire de Recherche en Informatique (LRI) + * + * Servers + * + * $Id$ + * $CurLog$ + */ + +#include "Server.h" + +/*?class UchServer +The class \typ{UchServer} derives from \typ{UchStream}. +It is associated to a channel set to monitor the connected clients in parallel. +Whenever a new client connects to the server, the virtual function \fun{HandleNew} is called. +This function must return a pointer to an object of class \typ{UchClient} (or a derived class of \typ{UchClient}). + +Because the server and the clients may be on different machines with different architectures, +the byte swapping is always done by the server, by convention. (NOTE: byte swapping is not currently implemented) +?*/ + +/*?class UchClient +Class \typ{UchClient} derives from \typ{UchMsgStream}. +It is still an abstract class because the virtual functions \fun{NewMessage} and \fun{ConvertAnswer} +of \typ{UchMsgStream} are not defined in \typ{UchClient}. +Instances of \fun{UchClient} only know that they may belong to an instance of \typ{UchServer}. +Thus the virtual function \fun{Delete} is redefined to achieve a clean removal from the server's list. +?*/ + +// server ports +// constructors, +// destructor +// copy +// + +/*? +Construct an empty server port. +?*/ +UchServer :: UchServer () +: UchStream (), + Clients (), + ChanSet (0) +{ +} + +/*?nodoc?*/ +UchServer :: UchServer (const UchServer& sp) +: UchStream (sp), + Clients (), + ChanSet (0) +{ +} + +/*? +Construct a server port bound to address \var{a}. +When using Internet domain addresses, \var{a} should refer to +the wildcard address so that clients can connect from any machine. +This can be done with the instruction \com{new UchInetAddress(ANYADDR)}. +?*/ +UchServer :: UchServer (UchAddress* a) +: UchStream (a, 0), + Clients (), + ChanSet (0) +{ +} + +/*?nodoc?*/ +UchServer :: ~UchServer () +{ + UchClient* cl = 0; + + while (cl = Clients.First ()) + cl->Delete (); + ChanSet = 0; // will delete it if necessary +} + +/*?nodoc?*/ +UchChannel* +UchServer :: Copy () const +{ + return new UchServer (*this); +} + +/*? +Remove a client from the set of clients connected to the server. +This is normally done automatically whenever the client disconnects itself, +as a side effect of destroying the client. +?*/ +void +UchServer :: RemoveClient (UchClient* cl) +{ + if (Clients.Remove (cl)) { + cl->MyServer = 0; + HandleRemove (cl); + } else + Error (ErrWarn, "UchServer::RemoveClient", "not owned by this server"); +} + +/*? +Bind the socket and listen to it. +Initialize the channel set of the server to \var{cs}, or create a new one if \var{cs} is 0. +The channel set is used by the server to read and process messages from its clients: +whenever a client connects to the server, an instance of \typ{UchClient} is added to the channel set. +Whenever data is readable from a client, it is read in an input buffer and processed as soon +as a full message is received. +Whenever a client closes the connection, it is removed from the channel set of the server. +This function returns TRUE if all went well, else it calls \fun{SysError} and returns FALSE. +You may want to pass your own channel set if you are already multiplexing +i/o on that channel set. For instance, you may have several \typ{UchServer} objects +in your application. +A smart pointer to the channel set is actually kept in the server, so that it can be shared +securely. +?*/ +bool +UchServer :: Setup (UchMultiplexer* cs) +{ + if (Listen () < 0) { + SysError (ErrWarn, "UchServer::Setup"); + return FALSE; + } + SetMode (IORead); + if (! cs) + cs = new UchMultiplexer; + ChanSet = cs; + ChanSet->Add (this); + + return TRUE; +} + +/*? +This function removes the server from its channel set. +This has the effect of ignoring any incoming connections. +This function will also make \fun{Run} exit, if there is no other channel in the channel set of the server. +Unless you added your own channels to this channel set, +this will be the case if there is no client currently connected. +This is the only way to exit properly from \fun{Run}. +?*/ +void +UchServer :: Unlisten () +{ + if (ChanSet) + ChanSet->Remove (*this); +} + + +// handle connections on server port +// + +/*?nodoc?*/ +void +UchServer :: HandleRead () +{ +// cannot redefine Accept to return UchClient*, so this does not work : +// UchClient* cl = Accept (); +// need to delete the channel created by Accept, so this does not work either : +// UchClient* cl = new UchClient (Accept ()); +// so we do this (less elegant ...) : + UchChannel* ch = Accept (); + if (! ch) { + SysError (ErrWarn, "UchServer::HandleRead: Accept"); + return; + } + UchClient* cl = HandleNew (ch); + delete ch; + + cl->SetMode (IOReadSelect); + cl->SetOwner (this); + Clients.Append (cl); + if (ChanSet) + ChanSet->Add (cl); +} + +// Default virtual function for handling new connections +// If a class MY_CLIENT is derived from UchClient, +// a class MY_SERVER must be derived from UchServer, +// with MY_SERVER::HandleNew (UchChannel* ch) +// doing at least a return new MY_CLIENT (ch) +// +/*? +This virtual function is called whenever a new client connects to the server. +The default action is to return \com{new UchClient(ch)}. +This needs to be redefined only if you create a derived class of \typ{UchClient}, +say \typ{MY_CLIENT}, in which case it should be redefined in a derived class +of \typ{UchServer} to return \com{new MY_CLIENT(ch)}. +?*/ +UchClient* +UchServer :: HandleNew (UchChannel* ch) +{ + return new UchClient (ch); +} + +/*? +This virtual function is called whenever a client is removed (\fun{RemoveClient}). +It is a hook for the application to take whatever action; the default action is to do nothing. +When this function is called, the client is already removed from the client list of the server. +?*/ +void +UchServer :: HandleRemove (UchClient*) +{ + // nothing +} + +// default error functions +// +/*?nextdoc?*/ +bool +UchServer :: SysError (errtype how, const char* who, int excl1, int excl2) +{ + return ::SysError (how, who, excl1, excl2); +} + +/*? +Each server port class can have its own error handling routines. +Their default action is to call the global functions \fun{Error} and \fun{SysError}. +They can be called from the following functions: +\fun{Error} can be called from +\fun{RemoveClient} when the client does not belong to this server; +\fun{SysError} can be called from +\fun{Setup} when the socket could not be setup correctly, +and from \fun{HandleRead} +when the connection could not be accepted. +?*/ +void +UchServer :: Error (errtype how, const char* who, const char* what) +{ + ::Error (how, who, what); +} + +/*? +Setup the server if necessary, then scan and process its channel set +by calling \fun{LoopScan} for it. +To have a server active, you need at least to initialize it (and thus know its address), +and then run it. +If you have added your own channels to the channel set of the server, +their \fun{HandleRead} and \fun{HandleWrite} functions will be called normally by \fun{Run}. +?*/ +void +UchServer :: Run () +{ + if (! ChanSet) + if (! Setup ()) + Error (ErrFatal, "UchServer::Run", "could not setup"); + if (ChanSet) + ChanSet->LoopScan (); +} + +/*? +Send a message to all clients currently connected to this server. +If \var{flush} is TRUE, the output buffer of each client is flushed. +?*/ +void +UchServer :: Broadcast (UchMessage& msg, bool flush) +{ + CcuListIterOf li (Clients); + while (++li) + (*li)->Send (msg, flush); +} + +/*? +Send a message to all clients currently connected to this server, except \var{exclude}. +If \var{flush} is TRUE, the output buffer of each client is flushed. +?*/ +void +UchServer :: Broadcast (UchMessage& msg, UchClient* excl, bool flush) +{ + CcuListIterOf li (Clients); + while (++li) + if (*li != excl) + (*li)->Send (msg, flush); +} + +/*?nextdoc?*/ +void +UchServer :: Broadcast (UchMsgBuffer& buf, bool flush) +{ + CcuListIterOf li (Clients); + while (++li) + (*li)->Send (buf, flush); +} + +/*? +These functions are similar to the previous ones, except that they take +a buffer instead of a message. The buffer {\em must} contain a converted message. +It is more efficient to broadcast a buffer than a message because there is less +message conversion overhead. +?*/ +void +UchServer :: Broadcast (UchMsgBuffer& buf, UchClient* excl, bool flush) +{ + CcuListIterOf li (Clients); + while (++li) + if (*li != excl) + (*li)->Send (buf, flush); +} + +// clients: +// + +/*?nodoc?*/ +UchClient :: UchClient () +: UchMsgStream (), + MyServer (0) +{ +} + +/*?nodoc?*/ +UchClient :: UchClient (const UchClient& cl) +: UchMsgStream (cl), + MyServer (0) +{ +} + +/*? +Construct a new client connected to the server on channel \var{ch}. +?*/ +UchClient :: UchClient (UchChannel* ch) +: UchMsgStream (), + MyServer (0) +{ + UchChannel::Open (ch->FilDes ()); +} + +/*?nodoc?*/ +UchClient :: ~UchClient () +{ + if (MyServer) { + UchServer* s = MyServer; + s->Error (ErrWarn, "~UchClient", "client still in a server; deleting anyway ...\n"); + s->RemoveClient (this); + if (s->ChanSet) + s->ChanSet->Remove (*this); // calls the destructor if no more refs + } +} + +/*?nodoc?*/ +UchChannel* +UchClient :: Copy () const +{ + return new UchClient (*this); +} + +/*? +This function must be used to delete a client explicitly. +It is not safe to use the operator delete. +This function is called when an end of file is read from the client; +this means that you usually do not need to call it. +?*/ +void +UchClient :: Delete () +{ + if (MyServer) { + UchServer* s = MyServer; + s->RemoveClient (this); + if (s->ChanSet) + s->ChanSet->Remove (*this); // calls the destructor if no more refs + } +} + +/*?nodoc?*/ +void +UchClient :: SetOwner (UchServer* serv) +{ + if (MyServer) + serv->Error (ErrFatal, "UchClient::SetOwner", "already owned"); + else + MyServer = serv; +} + +#ifdef DOC +//fake entries for inline functions + +/*? +Return the channel set used by the server. +This is useful if you want to add your own channels to the channel set. +?*/ +UchMultiplexer* +UchServer :: GetChanSet () +{ +} + +/*? +Return the server corresponding to a given client. +?*/ +UchServer* +UchClient :: GetServer () +{ +} + +#endif /* DOC */ + -- cgit v1.1