// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // % Project : GUSI - Grand Unified Socket Interface // % File : GUSIOpenTransport.nw- OpenTransport sockets // % Author : Matthias Neeracher // % Language : C++ // % // % $Log$ // % Revision 1.21 2001/03/20 08:12:55 neeri // % Further select repairs // % // % Revision 1.20 2001/03/20 02:35:21 neeri // % Refined new select() implementation // % // % Revision 1.19 2001/03/09 09:25:21 neeri // % Fixed select logic bugs, listener queue // % // % Revision 1.18 2001/01/17 08:58:06 neeri // % Releasing 2.1.4 // % // % Revision 1.17 2000/10/16 04:07:23 neeri // % Fix accept code // % // % Revision 1.16 2000/06/01 06:31:10 neeri // % Reset shutdown flags on connect, refine test for data available, fix memory leak in UDP sendto // % // % Revision 1.15 2000/05/23 07:13:19 neeri // % Improve formatting, implement immediate close and sorrect linger handling // % // % Revision 1.14 2000/03/15 07:19:53 neeri // % Fix numerous race conditions // % // % Revision 1.13 2000/03/06 06:10:01 neeri // % Reorganize Yield() // % // % Revision 1.12 1999/12/14 06:28:29 neeri // % Read pending data while closing // % // % Revision 1.11 1999/12/13 02:44:19 neeri // % Fix SO_LINGER, read results for disconnected sockets // % // % Revision 1.10 1999/10/15 02:48:50 neeri // % Make disconnects orderly // % // % Revision 1.9 1999/09/09 07:20:29 neeri // % Fix numerous bugs, add support for interface ioctls // % // % Revision 1.8 1999/09/03 06:31:36 neeri // % Needed more mopups // % // % Revision 1.7 1999/08/26 05:43:09 neeri // % Supply missing Unbind // % // % Revision 1.6 1999/08/02 07:02:45 neeri // % Support for asynchronous errors and other socket options // % // % Revision 1.5 1999/07/19 06:17:44 neeri // % Fix nonblocking connect // % // % Revision 1.4 1999/06/28 06:04:59 neeri // % Support interrupted calls // % // % Revision 1.3 1999/05/30 03:06:41 neeri // % MPW compiler compatibility, fix select for datagram sockets // % // % Revision 1.2 1999/04/29 05:33:19 neeri // % Fix fcntl prototype // % // % Revision 1.1 1999/03/17 09:05:11 neeri // % Added GUSITimer, expanded docs // % // %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // // \chapter{Open Transport socket infrastructure} // // A [[GUSIOTSocket]] defines a class of Open Transport sockets. Since most // families differ only in a few details, like address representation, we // abstract the typical differences in a strategy class [[GUSIOTStrategy]]. // // = #ifndef _GUSIOpenTransport_ #define _GUSIOpenTransport_ #ifdef GUSI_INTERNAL #include "GUSISocket.h" #include "GUSISocketMixins.h" #include "GUSIFactory.h" #include #include #undef O_ASYNC #undef O_NDELAY #undef O_NONBLOCK #undef SIGHUP #undef SIGURG #undef AF_INET #undef TCP_KEEPALIVE #undef TCP_NODELAY #undef TCP_MAXSEG #include #include // \section{Definition of [[GUSIOTStrategy]]} // // A [[GUSIOTStrategy]] contains all the tricky parts of each Open Transport // family. // // = class GUSIOTStrategy { public: // [[CreateConfiguration]] creates an appropriate [[OTConfiguration]]. This // method is not virtual, as it relies on the strategy method // [[ConfigPath]]. // // = OTConfiguration * CreateConfiguration(); // [[PackAddress]] converts a socket address into an OT address, and // [[UnpackAddress]] performs the reverse step. [[CopyAddress]] copies an address. // // = virtual int PackAddress( const void * address, socklen_t len, TNetbuf & addr, bool non_null = false) = 0; virtual int UnpackAddress(const TNetbuf & addr, void * address, socklen_t * len) = 0; virtual int CopyAddress(const TNetbuf & from, TNetbuf & to); // [[EndpointInfo]] returns a data structure storing endpoint parameters. We only // need one copy per socket type. // // = TEndpointInfo * EndpointInfo() { return &fEndpointInfo; } protected: // = virtual const char * ConfigPath() = 0; // = TEndpointInfo fEndpointInfo; // \section{Implementation of [[GUSIOTStrategy]]} // // [[GUSIOTStrategy]] is mostly abstract, except for the [[CreateConfiguration]] // and [[CopyAddress]] methods. // // = OTConfiguration * fConfig; GUSIOTStrategy() : fConfig(nil) {} virtual ~GUSIOTStrategy(); }; // \section{Definition of [[GUSIOTFactory]] and descendants} // // A [[GUSIOTFactory]] is an abstract class combining a socket creation // mechanism with a strategy instance. To clarify our intent, we isolate // the latter in [[GUSIOTStrategy]]. // // = class GUSIOTFactory : public GUSISocketFactory { public: static bool Initialize(); protected: virtual GUSIOTStrategy *Strategy(int domain, int type, int protocol) = 0; private: // \section{Implementation of [[GUSIOTFactory]] and descendants} // // // = static bool sOK; }; // = class GUSIOTStreamFactory : public GUSIOTFactory { public: GUSISocket * socket(int domain, int type, int protocol); }; // = class GUSIOTDatagramFactory : public GUSIOTFactory { public: GUSISocket * socket(int domain, int type, int protocol); }; // \section{Definition of [[GUSIOT]]} // // Open Transport allocates and deallocates many data structures, which we // simplify with a smart template. Allocation works via class allocation // operators, which is a bit weird admittedly. // // = template class GUSIOT : public T { public: void * operator new(size_t, EndpointRef ref) { OSStatus err; return OTAlloc(ref, tag, T_ALL, &err); } void * operator new(size_t, EndpointRef ref, int fields) { OSStatus err; return OTAlloc(ref, tag, fields, &err); } void operator delete(void * o) { if (o) OTFree(o, tag); } }; template class GUSIOTAddr : public GUSIOT { public: int Pack(GUSIOTStrategy * strategy, const void * address, socklen_t len, bool non_null=false) { return strategy->PackAddress(address, len, addr, non_null); } int Unpack(GUSIOTStrategy * strategy, void * address, socklen_t * len) { return strategy->UnpackAddress(addr, address, len); } int Copy(GUSIOTStrategy * strategy, GUSIOTAddr * to) { return strategy->CopyAddress(addr, to->addr); } }; typedef GUSIOTAddr GUSIOTTBind; typedef GUSIOTAddr GUSIOTTCall; typedef GUSIOTAddr GUSIOTTUnitData; typedef GUSIOTAddr GUSIOTTUDErr; typedef GUSIOT GUSIOTTDiscon; typedef GUSIOT GUSIOTTOptMgmt; // \section{Definition of [[GUSIOTSocket]] and descendants} // // Open Transport sockets are rather lightweight, since OT is rather similar // to sockets already. // // = class GUSIOTSocket : public GUSISocket, protected GUSISMBlocking, protected GUSISMState, protected GUSISMAsyncError { public: // = virtual int bind(void * name, socklen_t namelen); // [[getsockname]] and [[getpeername]] unpack the stored addresses. // Note that the reaction to [[nil]] addresses is a bit different. // // = virtual int getsockname(void * name, socklen_t * namelen); // [[shutdown]] just delegates to [[GUSISMState]]. // // = virtual int shutdown(int how); // [[fcntl]] handles the blocking support. // // = virtual int fcntl(int cmd, va_list arg); // [[ioctl]] deals with blocking support and with [[FIONREAD]]. // // = virtual int ioctl(unsigned int request, va_list arg); // Before we call [[select]], we check whether there is data currently available and turn // off the data flags otherwise. // // = virtual bool pre_select(bool wantRead, bool wantWrite, bool wantExcept); // [[getsockopt]] and [[setsockopt]] are available for a variety of options. // // = virtual int getsockopt(int level, int optname, void *optval, socklen_t * optlen); // = virtual int setsockopt(int level, int optname, void *optval, socklen_t optlen); // Open Transport sockets implement socket style calls. // // = virtual bool Supports(ConfigOption config); protected: GUSIOTSocket(GUSIOTStrategy * strategy); // \section{Implementation of [[GUSIOTSocket]]} // // Open Transport may call this routine for dozens and dozens of different // reasons. Pretty much every call results in a wakeup of the threads attached // to the socket. We save some of the more interesting events in bitsets. // in [[MopupEvents]]. // // = uint16_t fNewEvent; uint16_t fCurEvent; uint16_t fEvent; uint32_t fNewCompletion; uint32_t fCurCompletion; uint32_t fCompletion; friend pascal void GUSIOTNotify(GUSIOTSocket *, OTEventCode, OTResult, void *); // To avoid race conditions with the notifier, we sometimes need a lock. // // = class Lock { public: Lock(EndpointRef end) : fEndpoint(end) { OTEnterNotifier(fEndpoint); } ~Lock() { OTLeaveNotifier(fEndpoint); } private: EndpointRef fEndpoint; }; // For some events, we have to take a followup action at a more convenient time. // // = virtual void MopupEvents(); // [[GUSIOTSocket]] creates an asynchronous endpoint for the appropriate // provider. // // = GUSIOTStrategy * fStrategy; EndpointRef fEndpoint; linger fLinger; UInt32 fDeadline; // The destructor tears down the connection as gracefully as possible. It also respects // the linger settings. // // = virtual void close(); virtual ~GUSIOTSocket(); // [[Clone]] creates another socket of the same class. // // = virtual GUSIOTSocket * Clone() = 0; // At the time the socket function [[bind]] is called, we are not really ready // yet to call [[OTBind]], but if we don't call it, we can't report whether the // address was free. // // = GUSIOTTBind * fSockName; int BindToAddress(GUSIOTTBind * addr); // Open Transport takes unbinding a lot more serious than MacTCP. // // = void Unbind(); friend class GUSIOTStreamSocket; friend class GUSIOTDatagramSocket; }; // = class GUSIOTStreamSocket : public GUSIOTSocket { public: // [[Clone]] creates another socket of the same class. // // = virtual GUSIOTSocket * Clone(); // = virtual void close(); virtual bool Close(UInt32 now); ~GUSIOTStreamSocket(); // Stream sockets include a mopup action for connect and disconnect. // // = virtual void MopupEvents(); // [[listen]] is a bit embarassing, because we already committed ourselves // to a queue length of [[0]], so we have to unbind and rebind ourselves. // // = virtual int listen(int qlen); // = virtual int getpeername(void * name, socklen_t * namelen); // [[accept]] may become quite complex, because connections could nest. The // listening socket calls [[OTListen]], queues candidates by their // [[fNextListener]] field, and then trys calling [[OTAccept]] on the first // candidate. // // = virtual GUSISocket * accept(void * address, socklen_t * addrlen); // [[connect]] is comparatively simple. // // = virtual int connect(void * address, socklen_t addrlen); // Data transfer is simple as well. Here is the version for stream protocols. // // = virtual ssize_t recvfrom(const GUSIScatterer & buffer, int flags, void * from, socklen_t * fromlen); // = virtual ssize_t sendto(const GUSIGatherer & buffer, int flags, const void * to, socklen_t tolen); // [[select]] for stream sockets intermingles data information and connection // information as usual. // // = virtual bool select(bool * canRead, bool * canWrite, bool * except); // [[shutdown]] for stream sockets has to send an orderly disconnect. // // = virtual int shutdown(int how); protected: GUSIOTStreamSocket(GUSIOTStrategy * strategy); // Since all we need to know is in the [[GUSIOTStrategy]], it often suffices // simply to create a [[GUSIOTSocket]]. Stream and datagram sockets differ // merely in the descendant they create. // // = friend class GUSIOTStreamFactory; // = friend pascal void GUSIOTNotify(GUSIOTSocket *, OTEventCode, OTResult, void *); // The peer address for a [[GUSIOTStreamSocket]] is stored in a [[GUSIOTTCall]] // structure. // // = GUSIOTTCall * fPeerName; // = GUSIOTStreamSocket * fNextListener; }; // = class GUSIOTDatagramSocket : public GUSIOTSocket { public: // [[Clone]] creates another socket of the same class. // // = virtual GUSIOTSocket * Clone(); // = ~GUSIOTDatagramSocket(); // = virtual int getpeername(void * name, socklen_t * namelen); // A datagram socket can [[connect]] as many times as it wants. // // = virtual int connect(void * address, socklen_t addrlen); // Datagram protocols use slightly different calls for data transfers. // // = virtual ssize_t recvfrom(const GUSIScatterer & buffer, int flags, void * from, socklen_t * fromlen); // [[sendto]] needs either a valid [[to]] address or a stored peer address set by // [[connect]]. // // = virtual ssize_t sendto(const GUSIGatherer & buffer, int flags, const void * to, socklen_t tolen); // [[select]] for datagram sockets returns data information only. // // = virtual bool select(bool * canRead, bool * canWrite, bool * except); protected: GUSIOTDatagramSocket(GUSIOTStrategy * strategy); // = friend class GUSIOTDatagramFactory; // Datagram sockets might be bound at rather arbitrary times. // // = int BindIfUnbound(); // The peer address for a [[GUSIOTDatagramSocket]] is stored in a [[GUSIOTTBind]] // structure. // // = GUSIOTTBind * fPeerName; }; #endif /* GUSI_INTERNAL */ #endif /* _GUSIOpenTransport_ */