summaryrefslogtreecommitdiff
path: root/comm/OLD/Server.cc
diff options
context:
space:
mode:
authorchatty1993-04-07 11:50:31 +0000
committerchatty1993-04-07 11:50:31 +0000
commitba066c34dde204aa192d03a23a81356374d93731 (patch)
tree39391f6235d2cf8a59a0634ac5ea430cdd21f5d4 /comm/OLD/Server.cc
parent05ab076e1c2a9ca16472f9a6b47b8d22914b3783 (diff)
downloadivy-league-ba066c34dde204aa192d03a23a81356374d93731.zip
ivy-league-ba066c34dde204aa192d03a23a81356374d93731.tar.gz
ivy-league-ba066c34dde204aa192d03a23a81356374d93731.tar.bz2
ivy-league-ba066c34dde204aa192d03a23a81356374d93731.tar.xz
Initial revision
Diffstat (limited to 'comm/OLD/Server.cc')
-rw-r--r--comm/OLD/Server.cc395
1 files changed, 395 insertions, 0 deletions
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 <UchClient> 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 <UchClient> li (Clients);
+ while (++li)
+ if (*li != excl)
+ (*li)->Send (msg, flush);
+}
+
+/*?nextdoc?*/
+void
+UchServer :: Broadcast (UchMsgBuffer& buf, bool flush)
+{
+ CcuListIterOf <UchClient> 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 <UchClient> 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 */
+