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/Agent.cc | 386 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 386 insertions(+) create mode 100644 comm/OLD/Agent.cc (limited to 'comm/OLD/Agent.cc') diff --git a/comm/OLD/Agent.cc b/comm/OLD/Agent.cc new file mode 100644 index 0000000..3e4bf8a --- /dev/null +++ b/comm/OLD/Agent.cc @@ -0,0 +1,386 @@ +/* + * The Unix Channel + * + * by Michel Beaudouin-Lafon + * + * Copyright 1990-1993 + * Laboratoire de Recherche en Informatique (LRI) + * + * Agents, by Stephane Chatty + * + * $Id$ + * $CurLog$ + */ + +#include "Agent.h" + +/*?class UchAgent +The class \typ{UchAgent} derives from \typ{UchStream}. +It is associated to a channel set to monitor the connected agents in parallel. +Whenever a new agent connects, the virtual function \fun{HandleNew} is called. +This function must return a pointer to an object of class \typ{UchRemoteAgent}. + +Because the agents may be on different machines with different architectures, +the byte swapping is always done by the ``server'' (ie. the agent that did not initiate +the communication), by convention. (NOTE: byte swapping is not currently implemented) +?*/ + +/*?class UchRemoteAgent +Class \typ{UchRemoteAgent} 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{UchRemoteAgent}. +Instances of \fun{UchRemoteAgent} only know that they may belong to an instance of \typ{UchAgent}. +Thus the virtual function \fun{Delete} is redefined to achieve a clean removal from the server's list. +?*/ + +/*? +Construct an empty server port. +?*/ +UchAgent :: UchAgent () +: UchStream (), + RemoteAgents (), + ChanSet (0) +{ +} + +/*?nodoc?*/ +UchAgent :: UchAgent (const UchAgent& sp) +: UchStream (sp), + RemoteAgents (), + 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)}. +?*/ +UchAgent :: UchAgent (UchAddress* a) +: UchStream (a, 0), + RemoteAgents (), + ChanSet (0) +{ +} + +/*?nodoc?*/ +UchAgent :: ~UchAgent () +{ + UchRemoteAgent* cl = 0; + + while (cl = RemoteAgents.First ()) + cl->Delete (); + ChanSet = 0; // will delete it if necessary +} + +/*?nodoc?*/ +UchChannel* +UchAgent :: Copy () const +{ + return new UchAgent (*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 +UchAgent :: RemoveRemoteAgent (UchRemoteAgent* cl) +{ + if (RemoteAgents.Remove (cl)) { + cl->MyLocalAgent = 0; + HandleRemove (cl); + } else + Error (ErrWarn, "UchAgent::RemoveRemoteAgent", "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{UchRemoteAgent} 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{UchAgent} 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 +UchAgent :: Setup (UchMultiplexer* cs) +{ + if (Listen () < 0) { + SysError (ErrWarn, "UchAgent::Setup"); + return FALSE; + } + SetMode (IORead); + if (!cs) + cs = new UchMultiplexer; + ChanSet = cs; + ChanSet->Add (this); + + return TRUE; +} + +/*? +This function removes the agent's registration channel 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. +Unless you added your own channels to this channel set, +this will be the case if there is no remote agent currently connected. +This is the only way to exit properly from \fun{Run}. +?*/ +void +UchAgent :: Unlisten () +{ + if (ChanSet) + ChanSet->Remove (*this); +} + + +/*?nodoc?*/ +void +UchAgent :: HandleRead () +{ +// cannot redefine Accept to return UchRemoteAgent*, so this does not work : +// UchRemoteAgent* cl = Accept (); +// need to delete the channel created by Accept, so this does not work either : +// UchRemoteAgent* cl = new UchRemoteAgent (Accept ()); +// so we do this (less elegant ...) : + UchChannel* ch = Accept (); + if (! ch) { + SysError (ErrWarn, "UchAgent::HandleRead: Accept"); + return; + } + UchRemoteAgent* ra = new UchRemoteAgent (this, ch); + delete ch; + + ra->SetMode (IOReadSelect); + RemoteAgents.Append (ra); + if (ChanSet) + ChanSet->Add (ra); +} + +/*? +This virtual function is called whenever a new agent connects to this one. +?*/ +void +UchAgent :: HandleNew (UchRemoteAgent*) +{ +} + +/*? +This virtual function is called whenever a remote agent is removed (\fun{RemoveRemoteAgent}). +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 +UchAgent :: HandleRemove (UchRemoteAgent*) +{ +} + +// default error functions +// +/*?nextdoc?*/ +bool +UchAgent :: 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{RemoveRemoteAgent} 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 +UchAgent :: 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 +UchAgent :: Run () +{ + if (! ChanSet) + if (! Setup ()) + Error (ErrFatal, "UchAgent::Run", "could not setup"); + if (ChanSet) + ChanSet->LoopScan (); +} + +/*? +Send a message to all the agents currently connected to this one. +If \var{flush} is TRUE, the output buffer of each remote agent is flushed. +?*/ +void +UchAgent :: Broadcast (UchMessage& msg, bool flush) +{ + CcuListIterOf li (RemoteAgents); + while (++li) + (*li)->Send (msg, flush); +} + +/*? +Send a message to all the agents currently connected to this one, except \var{exclude}. +If \var{flush} is TRUE, the output buffer of each client is flushed. +?*/ +void +UchAgent :: Broadcast (UchMessage& msg, UchRemoteAgent* excl, bool flush) +{ + CcuListIterOf li (RemoteAgents); + while (++li) + if (*li != excl) + (*li)->Send (msg, flush); +} + +/*?nextdoc?*/ +void +UchAgent :: Broadcast (UchMsgBuffer& buf, bool flush) +{ + CcuListIterOf li (RemoteAgents); + 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 +UchAgent :: Broadcast (UchMsgBuffer& buf, UchRemoteAgent* excl, bool flush) +{ + CcuListIterOf li (RemoteAgents); + while (++li) + if (*li != excl) + (*li)->Send (buf, flush); +} + +#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* +UchAgent :: GetChanSet () +{ +} +#endif + +UchRemoteAgent* +UchAgent :: Contact (UchAddress* a) +{ + if (!a) + return 0; + UchRemoteAgent* ra = new UchRemoteAgent (this, a); + if (ra) + ChanSet->Add (ra); + return ra; +} + +/*?nodoc?*/ +UchRemoteAgent :: UchRemoteAgent (UchAgent* a) +: UchMsgStream (), + MyLocalAgent (a) +{ +} + +/*?nodoc?*/ +UchRemoteAgent :: UchRemoteAgent (const UchRemoteAgent& cl) +: UchMsgStream (cl), + MyLocalAgent (cl.MyLocalAgent) +{ +} + +/*? +Construct a new agent connected to this one on channel \var{ch}. +?*/ +UchRemoteAgent :: UchRemoteAgent (UchAgent* a, UchChannel* ch) +: UchMsgStream (), + MyLocalAgent (a) +{ + UchChannel::Open (ch->FilDes ()); +} + +UchRemoteAgent :: UchRemoteAgent (UchAgent* a, UchAddress* addr) +: UchMsgStream (addr, 0), + MyLocalAgent (a) +{ +} + + +/*?nodoc?*/ +UchRemoteAgent :: ~UchRemoteAgent () +{ + if (MyLocalAgent) { + UchAgent* s = MyLocalAgent; + s->Error (ErrWarn, "~UchRemoteAgent", "remote agent still connected; deleting anyway ...\n"); + s->RemoveRemoteAgent (this); + if (s->ChanSet) + s->ChanSet->Remove (*this); // calls the destructor if no more refs + } +} + +/*?nodoc?*/ +UchChannel* +UchRemoteAgent :: Copy () const +{ + return new UchRemoteAgent (*this); +} + +/*? +This function must be used to delete a remote agent 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 +UchRemoteAgent :: Delete () +{ + if (MyLocalAgent) { + UchAgent* s = MyLocalAgent; + s->RemoveRemoteAgent (this); + if (s->ChanSet) + s->ChanSet->Remove (*this); // calls the destructor if no more refs + } +} + + + +#ifdef DOC +//fake entries for inline functions + +/*? +Return the server corresponding to a given client. +?*/ +UchAgent* +UchRemoteAgent :: GetAgent () +{ +} + +#endif /* DOC */ + -- cgit v1.1