// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // % Project : GUSI - Grand Unified Socket Interface // % File : GUSISocketMixins.nw - Useful building blocks // % Author : Matthias Neeracher // % Language : C++ // % // % $Log$ // % Revision 1.1.1.1 2001/03/03 21:50:14 chombier // % Initial import // % // % Revision 1.11 2000/10/16 04:10:12 neeri // % Add GUSISMProcess // % // % Revision 1.10 2000/05/23 07:24:58 neeri // % Improve formatting // % // % Revision 1.9 1999/08/26 05:45:09 neeri // % Fixes for literate edition of source code // % // % Revision 1.8 1999/08/02 07:02:46 neeri // % Support for asynchronous errors and other socket options // % // % Revision 1.7 1999/05/29 06:26:45 neeri // % Fixed header guards // % // % Revision 1.6 1999/04/29 05:33:18 neeri // % Fix fcntl prototype // % // % Revision 1.5 1999/03/17 09:05:13 neeri // % Added GUSITimer, expanded docs // % // % Revision 1.4 1998/10/11 16:45:24 neeri // % Ready to release 2.0a2 // % // % Revision 1.3 1998/01/25 20:53:59 neeri // % Engine implemented, except for signals & scheduling // % // % Revision 1.2 1997/11/13 21:12:13 neeri // % Fall 1997 // % // % Revision 1.1 1996/12/16 02:12:42 neeri // % TCP Sockets sort of work // % // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // // \chapter{Mixin Classes for Sockets} // // This section contains some building block classes for sockets: // // \begin{itemize} // \item [[GUSISMBlocking]] implements the blocking/nonblocking flag. // \item [[GUSISMState]] implements a state variable. // \item [[GUSISMInputBuffer]] provides a [[GUSIBuffer]] for input. // \item [[GUSISMOutputBuffer]] provides a [[GUSIBuffer]] for output. // \item [[GUSISMAsyncError]] provides storage for asynchronous errors. // \item [[GUSISMProcess]] maintains a link to the process instance. // \end{itemize} // // // = #ifndef _GUSISocketMixins_ #define _GUSISocketMixins_ #ifdef GUSI_INTERNAL #include "GUSISocket.h" #include "GUSIBuffer.h" #include #include // \section{Definition of [[GUSISocketMixins]]} // // [[GUSISMBlocking]] implements the [[fBlocking]] flags and the [[DoIoctl]] and // [[DoFcntl]] variants to manipulate it. These two functions work like their // [[GUSISocket]] member function counterparts, but handle the return value // differently: The POSIX function result is stored in [[*result]], while the // return value indicates whether the request was handled. // // = class GUSISMBlocking { public: GUSISMBlocking(); bool fBlocking; bool DoFcntl(int * result, int cmd, va_list arg); bool DoIoctl(int * result, unsigned int request, va_list arg); }; // [[GUSISMState]] captures the state of a socket over its lifetime. It starts out // as [[Unbound]]. [[bind]] will put it into [[Unconnected]] state, though few // socket classes care about this distinction. [[listen]] will put it into // [[Listening]] state. [[accept]] starts a [[Connected]] new socket. // [[connect]] puts an [[Unconnected]] socket into [[Connecting]] state from // where it emerges [[Connected]]. [[fReadShutdown]] and [[fWriteShutdown]] record // shutdown promises. // // = class GUSISMState { public: enum State { Unbound, Unconnected, Listening, Connecting, Connected, Closing }; GUSISMState(); State fState; bool fReadShutdown; bool fWriteShutdown; void Shutdown(int how); }; // [[GUSISMInputBuffer]] defines the input buffer and some socket options that go // with it. [[DoGetSockOpt]] and [[DoSetSockOpt]] work the same way as // [[DoFcntl]] and [[DoIoctl]] above. // // = class GUSISMInputBuffer { public: GUSIRingBuffer fInputBuffer; GUSISMInputBuffer(); bool DoGetSockOpt( int * result, int level, int optname, void *optval, socklen_t * optlen); bool DoSetSockOpt( int * result, int level, int optname, void *optval, socklen_t optlen); bool DoIoctl(int * result, unsigned int request, va_list arg); }; // [[GUSISMOutputBuffer]] defines the output buffer and some socket options that go // with it. // // = class GUSISMOutputBuffer { public: GUSIRingBuffer fOutputBuffer; GUSISMOutputBuffer(); bool DoGetSockOpt( int * result, int level, int optname, void *optval, socklen_t * optlen); bool DoSetSockOpt( int * result, int level, int optname, void *optval, socklen_t optlen); }; // [[GUSISMAsyncError]] stores asynchronous errors and makes them available via // [[getsockopt]]. [[GetAsyncError]] returns the error and resets the stored value. // // = class GUSISMAsyncError { public: GUSISMAsyncError(); int fAsyncError; int SetAsyncPosixError(int error); int SetAsyncMacError(OSErr error); int GetAsyncError(); bool DoGetSockOpt( int * result, int level, int optname, void *optval, socklen_t * optlen); }; // [[GUSISMProcess]] stores a link to the global [[GUSIProcess]] instance, which is useful for completion routines. // // = class GUSISMProcess { public: GUSISMProcess(); GUSIProcess * Process(); private: GUSIProcess * fProcess; }; // \section{Implementation of [[GUSISocketMixins]]} // // Because all the member functions are simple and called in few places, it // makes sense to inline them. // // All sockets start out blocking. // // = inline GUSISMBlocking::GUSISMBlocking() : fBlocking(true) {} // For historical reasons, there is both an [[ioctl]] and a [[fcntl]] interface // to the blocking flag. // // = inline bool GUSISMBlocking::DoFcntl(int * result, int cmd, va_list arg) { switch(cmd) { case F_GETFL : return (*result = fBlocking ? 0: FNDELAY), true; case F_SETFL : fBlocking = !(va_arg(arg, int) & FNDELAY); return (*result = 0), true; } return false; } inline bool GUSISMBlocking::DoIoctl(int * result, unsigned int request, va_list arg) { if (request == FIONBIO) { fBlocking = !*va_arg(arg, int *); return (*result = 0), true; } return false; } // Sockets start out as unconnected. // // = inline GUSISMState::GUSISMState() : fState(Unbound), fReadShutdown(false), fWriteShutdown(false) {} // = inline void GUSISMState::Shutdown(int how) { if (!(how & 1)) fReadShutdown = true; if (how > 0) fWriteShutdown = true; } // Buffers initially are 8K. // // = inline GUSISMInputBuffer::GUSISMInputBuffer() : fInputBuffer(8192) {} // [[getsockopt]] is used to obtain the buffer size. // // = inline bool GUSISMInputBuffer::DoGetSockOpt( int * result, int level, int optname, void *optval, socklen_t *) { if (level == SOL_SOCKET && optname == SO_RCVBUF) { *(int *)optval = (int)fInputBuffer.Size(); return (*result = 0), true; } return false; } // [[setsockopt]] modifies the buffer size. // // = inline bool GUSISMInputBuffer::DoSetSockOpt( int * result, int level, int optname, void *optval, socklen_t) { if (level == SOL_SOCKET && optname == SO_RCVBUF) { fInputBuffer.SwitchBuffer(*(int *)optval); return (*result = 0), true; } return false; } // [[ioctl]] returns the number of available bytes. // // = inline bool GUSISMInputBuffer::DoIoctl(int * result, unsigned int request, va_list arg) { if (request == FIONREAD) { *va_arg(arg, long *) = fInputBuffer.Valid(); return (*result = 0), true; } return false; } // [[GUSISMOutputBuffer]] works identically to the input buffer. // // = inline GUSISMOutputBuffer::GUSISMOutputBuffer() : fOutputBuffer(8192) {} // [[getsockopt]] is used to obtain the buffer size. // // = inline bool GUSISMOutputBuffer::DoGetSockOpt( int * result, int level, int optname, void *optval, socklen_t *) { if (level == SOL_SOCKET && optname == SO_SNDBUF) { *(int *)optval = (int)fOutputBuffer.Size(); return (*result = 0), true; } return false; } // [[setsockopt]] is modifies the buffer size. // // = inline bool GUSISMOutputBuffer::DoSetSockOpt( int * result, int level, int optname, void *optval, socklen_t) { if (level == SOL_SOCKET && optname == SO_SNDBUF) { fOutputBuffer.SwitchBuffer(*(int *)optval); return (*result = 0), true; } return false; } // = inline GUSISMAsyncError::GUSISMAsyncError() : fAsyncError(0) { } // The central member functions of [[GUSISMAsyncError]] are [[SetAsyncXXXError]] and // [[GetAsyncError]]. // // = inline int GUSISMAsyncError::SetAsyncPosixError(int error) { if (error) { fAsyncError = error; GUSI_MESSAGE(("GUSISMAsyncError::SetAsyncPosixError %d\n", fAsyncError)); return -1; } return 0; } inline int GUSISMAsyncError::GetAsyncError() { int err = fAsyncError; fAsyncError = 0; return err; } // For some reason, the CW Pro 4 compilers generated bad code for this in some combination, so // we make it out of line. // // = inline int GUSISMAsyncError::SetAsyncMacError(OSErr error) { if (error) { fAsyncError = GUSIMapMacError(error); GUSI_MESSAGE(("GUSISMAsyncError::SetAsyncMacError %d -> %d\n", error, fAsyncError)); return -1; } return 0; } // [[DoGetSockOpt]] only handles [[SO_ERROR]] (hi Philippe!). // // = inline bool GUSISMAsyncError::DoGetSockOpt( int * result, int level, int optname, void *optval, socklen_t * optlen) { if (level == SOL_SOCKET && optname == SO_ERROR) { *(int *)optval = GetAsyncError(); *optlen = sizeof(int); return (*result = 0), true; } return false; } // = inline GUSISMProcess::GUSISMProcess() : fProcess(GUSIProcess::Instance()) { } inline GUSIProcess * GUSISMProcess::Process() { return fProcess; } #endif /* GUSI_INTERNAL */ #endif /* _GUSISocketMixins_ */