/* ---- This file is part of SECONDO. Copyright (C) 2004, University in Hagen, Department of Computer Science, Database Systems for New Applications. SECONDO is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. SECONDO is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with SECONDO; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ---- //paragraph [10] title: [{\Large \bf ] [}] //paragraph [21] table1column: [\begin{quote}\begin{tabular}{l}] [\end{tabular}\end{quote}] //paragraph [22] table2columns: [\begin{quote}\begin{tabular}{ll}] [\end{tabular}\end{quote}] //paragraph [23] table3columns: [\begin{quote}\begin{tabular}{lll}] [\end{tabular}\end{quote}] //paragraph [24] table4columns: [\begin{quote}\begin{tabular}{llll}] [\end{tabular}\end{quote}] //[--------] [\hline] //characters [1] verbatim: [$] [$] //characters [2] formula: [$] [$] //characters [4] teletype: [\texttt{] [}] //[ae] [\"a] //[oe] [\"o] //[ue] [\"u] //[ss] [{\ss}] //[<=] [\leq] //[<] [\lt ] //[>] [\gt ] //[#] [\neq] //[tilde] [\verb|~|] 1 Header File: Socket I/O February 2002 Ulrich Telle 1.1 Overview TCP (Transaction Control Protocol) is one of the most common protocols on the Internet. It has two basic communicators: clients and servers. Servers offer services to the network; clients connect to these services through ports. Sockets are the basis of network communication. Client software uses sockets to connect to servers. Servers use sockets to process network connections. TCP sockets provide very reliable connections using a special open communication flow similar to low-level stream I/O. Implementing clients and servers are fairly straightforward. Servers build a socket for listening on a port for incoming client connect requests. Clients also build a socket, connect to the server socket and then exchange messages. The set of classes defined here supports a subset of the TCP/IP protocol suite. Currently only IP version 4 is supported, but support for IP version 6 could be incorporated. Implementations of sockets are mostly based on socket libraries provided by the operating system. Local domain sockets are directly supported only in Unix. For 32-bit-Windows this socket module provides a very efficient implementation of local sockets using shared memory and semaphore objects. This allows very fast inter-process communication on one computer. *NOTE*: Different operating systems require different initialization and deinitialization of the socket interface. This is done transparently by instantiating a static instance of an internal class hidden in the implementation. The constructor is called before the main function is entered, thereby ensuring that the operating systems's socket interfaces is properly installed. The destructor is called on normal termination of the program. 1.3 Interface methods The class ~Socket~ is the heart of this module. Network communication is done by using methods of this class. Additionally to creating and destroying sockets, reading and writing on these sockets there are methods to identify the client and the server thus allowing a basic form of authentication and access control through the classes ~SocketRule~ and ~SocketRuleSet~. It is possible to specify a list of rules where each rule consists of an IP address, an IP address mask and an access policy allowing or denying access. When an IP address is checked against a set of rules, for each rule a bitwise *and* operation is performed on the given IP address and the IP address mask of a rule. The result is compared with the IP address of the rule. If the result matches, the decision whether access should be allowed or denied is based on the access policy of the rule and the default access policy of the rule set. Finally, the class ~SocketAddress~ hides details of the internet addresses from the user. The class ~Socket~ provides the following methods: [23] Creation/Removal & Input/Output & Information \\ [--------] Socket & Read & IsLibraryInitialized \\ [tilde]Socket & Write & IsOk \\ CreateLocal & GetSocketStream & GetErrorText \\ CreateGlobal & & GetSocketAddress \\ Accept & CancelAccept & GetPeerAddress \\ CreateClient & Close & GetHostname \\ Connect & ShutDown & GetIP \\ GetDescriptor & & \\ The class ~SocketRule~ provides the following methods: [23] Creation/Removal & Checking & I/O settings \\ [--------] SocketRule & Match & SetDelimiter \\ [tilde]SocketRule & Allowed & GetDelimiter \\ & Denied & \\ The class ~SocketRuleSet~ provides the following methods: [23] Creation/Removal & Checking & Persistence \\ [--------] SocketRuleSet & AddRule & LoadFromFile \\ [tilde]SocketRuleSet & Ok & StoreToFile \\ The class ~SocketAddress~ provides the following methods: [22] Creation/Removal & Address handling \\ [--------] SocketAddress & SetAddress \\ [tilde]SocketAddress & GetSocketString \\ operator= & GetIPAddress \\ & GetPort \\ & operator== \\ The class ~SocketBuffer~ implements the *streambuf* interface of the C++ standard library for socket streams. This greatly simplifies reading and writing to and from sockets. Since this class is used internally only, the class interface is not described here. */ #ifndef SOCKET_IO_H #define SOCKET_IO_H /* 1.2 Imports and definitions */ #include "SecondoConfig.h" #include #include #include #include #ifdef SECONDO_WIN32 #include #include #else #include #include #include #include // for getprotobynumber() #endif // Debugging and tracing UDPsockets - begin // (Uncomment the following line for detailed tracing) //#define __TRACE_UDP__ #ifdef __TRACE_UDP__ #define __UDP_POS__ __FILE__ << ".." << __PRETTY_FUNCTION__ << "@" << __LINE__ #define __UDP_ENTER__ std::cerr << "ENTER " << __UDP_POS__ << std::endl; #define __UDP_EXIT__ std::cerr << "EXIT " << __UDP_POS__ << std::endl; #define __UDP_MSG(X) std::cerr << __UDP_POS__ << ": " << X << std::endl; #else #undef __POS__ #define __POS__ __FILE__ #define __UDP_ENTER__ #define __UDP_EXIT__ #define __UDP_MSG(X) #endif // Debugging and tracing UDPsockets - end #define DEFAULT_CONNECT_MAX_ATTEMPTS 100 /* Defines the default for how many attempts are made at most to connect a client socket to a server socket. */ #define DEFAULT_RECONNECT_TIMEOUT 1 // seconds /* Defines the default time interval between two connect attempts. */ #define DEFAULT_LISTEN_QUEUE_SIZE 5 /* Defines the default capacity of the listener queue. */ #define LINGER_TIME 10 // seconds /* Defines how long the kernel should try to send data still waiting in the socket buffer after the socket was closed. */ #define WAIT_FOREVER ((time_t)-1) /* Specifies an indefinite time period. Specifying this value for a time out period is identical to blocking I/O mode. */ #define ENABLE_BROADCAST 1 /* Defines the flag for enabling broadcast messages on UDP sockets. */ #ifdef SECONDO_WIN32 #include typedef SOCKET SocketDescriptor; #else typedef int SocketDescriptor; #define INVALID_SOCKET (-1) #endif /* Is a type definition for a socket handle. If a socket is not valid, the handle value equals "INVALID\_SOCKET"[4]. */ /* 1.3 Class "Socket"[1] The class "Socket"[1] defines an abstract socket interface. The concrete operating system dependent socket classes are implemented as derived classes. Since ~Socket~ is an abstract base class, instances of ~Socket~ are created through the use of special static "factory" methods. *NOTE*: Since signals present portability problems, this code does not support signals (like SIGIO, etc.), which is why there is no interface to the ~fcntl~ function. */ class SocketBuffer; /* Is a forward declaration of the stream buffer class for socket stream support. */ class SDB_EXPORT Socket { public: enum SocketDomain { SockAnyDomain, SockLocalDomain, SockGlobalDomain }; /* Is an enumeration of domain types: * ~SockAnyDomain~ -- the domain type is chosen automatically * ~SockLocalDomain~ -- the domain type is local (i.e. Unix domain sockets) * ~SockGlobalDomain~ -- the domain type is global (i.e. INET sockets) */ static bool IsLibraryInitialized(); /* Checks whether the operating socket interface was successfully initialized. */ Socket() { state = SS_CLOSE; } /* Initializes a ~Socket~ instance as an invalid socket in closed state. */ virtual ~Socket() {} /* Destroys a socket. */ static Socket* Connect( const std::string& address, const std::string& port, const SocketDomain domain = SockAnyDomain, const int maxAttempts = DEFAULT_CONNECT_MAX_ATTEMPTS, const time_t timeout = DEFAULT_RECONNECT_TIMEOUT, std::ostream* traceInStream=0, std::ostream* traceOutStream=0, bool destroyStreams = false ); /* Establishes a connection to a server. This method will do at most ~maxAttempts~ attempts to connect to the server, with an interval of ~timeout~ seconds between the attempts. The address of the server is specified by ~address~ and ~port~, both as strings. The type of the connection is specified by ~domain~. The following values of this parameter are recognized: * ~SockAnyDomain~ -- the domain is chosen automatically * ~SockLocalDomain~ -- local domain (connection with a server on the same host) * ~SockGlobalDomain~ -- internet domain If ~SockAnyDomain~ is specified, a local connection is chosen when either the port was omitted in the specification of the address or hostname is *localhost*; a global connection is used in all other cases. This method always creates a new socket object and returns a pointer to it. If a connection to the server was not established, this socket contains an error code describing reason of failure. So the returned socket should be first checked by its ~IsOk~ method. */ static Socket* CreateLocal( const std::string& address, const int listenQueueSize = DEFAULT_LISTEN_QUEUE_SIZE, std::ostream* traceInStream=0, std::ostream* traceOutStream=0, bool destroyStreams = false); /* Creates and opens a socket in the local domain at the server side. The parameter ~address~ specifies the name to be assigned to the socket. The parameter ~listenQueueSize~ specifies the size of the listen queue. This method always creates a new socket object and returns a pointer to it. If a connection to the server was not established, this socket contains an error code describing reason of failure. So the returned socket should be first checked by its ~IsOk~ method. */ static Socket* CreateGlobal( const std::string& address, const std::string& port, const int listenQueueSize = DEFAULT_LISTEN_QUEUE_SIZE, std::ostream* traceInStream=0, std::ostream* traceOutStream=0, bool destroyStreams = false); /* Creates and opens a socket in the global (internet) domain at the server side. The parameter ~address~ specifies the name to be assigned to the socket. The parameter ~listenQueueSize~ specifies the size of the listen queue. This method always creates a new socket object and returns a pointer to it. If a connection to the server was not established, this socket contains an error code describing reason of failure. So the returned socket should be first checked by its ~IsOk~ method. */ virtual SocketDescriptor GetDescriptor() = 0; /* Returns the socket descriptor of the socket. This socket descriptor may be inherited by a child process later on. */ static Socket* CreateClient( const SocketDescriptor sd, std::ostream* traceInStream=0, std::ostream* traceOutStream=0, bool destroyStreams=false ); /* (Re)creates a socket instance for the socket descriptor ~sd~, created by the ~Accept~ method. This method is provided to allow passing socket descriptors to child processes. *NOTE*: While in Unix-like systems socket handles are inherited by child processes automatically, this is not the case in Windows systems. Usually you have to take special measure to make a socket handle inheritable and to make it accessible by a child process. The parent process must not close the socket before the child process has finished using the socket. */ virtual int Read( void* buf, size_t minSize, size_t maxSize, time_t timeout = WAIT_FOREVER ) = 0; /* Receives incoming data, transferring it from the socket into the buffer ~buf~. The maximal size of the buffer is given by ~maxSize~. The function returns the number of bytes actually read from the socket, which ranges from ~minSize~ to ~maxSize~. The function does not return to the caller before at least ~minSize~ bytes were received or a time out occurred. When the return value is less than ~minSize~, a time out has occurred. When the return value is less than zero, an error has occurred. Usually it means that the socket has disconnected. */ virtual bool Read( void* buf, size_t size ) = 0; /* Receives incoming data, transferring it from the socket into the buffer ~buf~. The function does not return to the caller before exactly ~size~ bytes were received or an error occurred. The function returns "true"[4] when the transfer was successful. */ virtual bool Write( void const* buf, size_t size ) = 0; /* Sends the data contained in buffer ~buf~ over the socket. The buffer is assumed to contain ~size~ bytes. The function returns "true"[4] when all bytes could be transferred successfully. In case of an error "false"[4] is returned. */ virtual bool IsOk() = 0; /* Checks whether the socket is correctly initialized and ready for operation. */ virtual std::string GetErrorText() = 0; /* Returns an error message text for the last error occurred. */ virtual std::string GetSocketAddress() const = 0; /* Returns the IP address of the socket in string representation. */ virtual std::string GetPeerAddress() const = 0; /* Returns the IP address of the socket to which this socket is connected in string representation. */ virtual Socket* Accept( std::ostream* traceInStream=0, std::ostream* traceOutStream=0, bool destroyStreams=false) = 0; /* Is called by a server to establish a pending client connection. When the client executes the ~Connect~ method and accesses the server's accept port, this method will create a new socket, which can be used for communication with the client. The function returns a pointer to a new socket that controls the communication between client and server. The new socket must be released by the server once it has finished using it. If the operation failed a "NULL"[4] pointer is returned. The function ~Accept~ blocks until a connection will be established and therefore cannot be used to detect activity on multiple sockets. */ virtual bool CancelAccept() = 0; /* Cancels an accept operation and closes the socket. */ virtual bool Close() = 0; /* Closes the socket. *NOTE*: The operating system decrements the associated reference counter of the socket by one. The TCP/IP connection is closed when the reference counter reaches zero. */ virtual bool ShutDown() = 0; /* Shuts down the socket. Thereafter read and write operations on the socket are prohibited. All future attempts to read or write data from/to the socket will be refused. But all previously initiated operations are guaranteed to be completed. The function returns "true"[4] if the operation was successfully completed, "false"[4] otherwise. */ static std::string GetHostname( const std::string& ipAddress ); /* Tries to get the fully qualified host name corresponding to the IP address ~ipAddress~ which is given in string representation. If the method fails the string ""[2] will be returned. */ static int GetIP( const std::string& address ); /* Gets the IP address of the host. "address" parameter should contain either symbolic host name (for example "robinson"), or a string with IP address (for example "195.239.208.225") */ std::iostream& GetSocketStream(); /* Returns a reference to the I/O stream associated with the socket. An I/O stream is available only for sockets created by the methods ~Connect~ and ~Accept~. */ virtual void setTraceStreams( std::ostream* traceInStream, std::ostream* traceOutStream, bool destroyStreams)=0; protected: enum { SS_OPEN, SS_SHUTDOWN, SS_CLOSE } state; /* Defines the socket state. */ SocketBuffer* ioSocketBuffer = nullptr; // Socket stream buffer std::iostream* ioSocketStream = nullptr; // Socket I/O stream }; extern std::string GetProcessName(); /* Returns the current host name combined with an identifier of the current process. */ /* 1.1 Class "SocketAddress"[1] Class "SocketAddress"[1] represents a socket address. Socket addresses combine an IP address with a port number. The IP address identifies a host, while the port number identifies a service available on the host. Typically used by TCP/IP clients to indicate a machine and service they are connecting to. */ class SDB_EXPORT SocketAddress { public: SocketAddress(); /* Initializes a socket address, which consists of an IP address and a port number. The IP address is set to the wildcard address ("INADDR\_ANY"[4]). The port number is set to zero. */ SocketAddress( const SocketAddress& sockAddr ); /* Creates a socket address that is an identical copy of ~sockAddr~. */ SocketAddress( const std::string& ipAddr, uint16_t portNo = 0 ); /* Initializes a socket address converting the string representation of an IP address ~ipAddr~ into the internal binary representation. The port number is set to ~portNo~. */ virtual ~SocketAddress(); /* Destroys a socket address. */ SocketAddress& operator=( const SocketAddress& sockAddr ); /* Changes ~self~ into an identical copy of the socket address referenced by ~sockAddr~. */ bool operator==( const SocketAddress& sockAddr ) const; /* Compares ~self~ with socket address ~sockAddr~. The method returns "true"[4] if both objects are equal, otherwise it returns "false"[4]. */ void SetAddress( const std::string& ipAddr, uint16_t portNo = 0 ); /* Sets the socket address converting the string representation of an IP address ~ipAddr~ into the internal binary representation. The port number is set to ~portNo~. */ void SetAddress( const std::string& ipAddr, const std::string& portNo ); /* Sets the socket address converting the string representation of an IP address ~ipAddr~ into the internal binary representation. The string representation ~portNo~ of the port number is also converted. */ std::string GetSocketString() const; /* Returns the IP address of the socket including the port number as a string. The port number is appended to the IP address after inserting a colon as a delimiter, i.e. ~132.176.69.10:1234~. */ std::string GetIPAddress() const; /* Returns the IP address portion of the socket address in string format. */ uint16_t GetPort() const; /* Returns the port number portion of the socket address in host byte order. */ protected: int sa_len; union { struct sockaddr sock; struct sockaddr_in sock_inet; } u; }; /* 1.1 Class "SocketRule"[1] */ class SDB_EXPORT SocketRule { public: enum Policy { DENY, ALLOW }; /* Is an enumeration of access policies: * ~ALLOW~ -- specifies that access should be granted when a host address matches the rule. * ~DENY~ -- specifies that access should be denied when a host address matches the rule. */ SocketRule(); /* Creates an empty rule. */ SocketRule( const std::string& strIpAddr, const std::string& strIpMask, const Policy setAllowDeny = ALLOW ); /* Creates a rule initialized by the given IP address ~strIpAddr~ and IP address mask ~strIpMask~ and the access policy ~setAllowDeny~. */ virtual ~SocketRule() {}; /* Destroys a rule. */ bool Match( const SocketAddress& host ); /* Checks whether the IP address ~host~ matches the rule. The access policy is ignored. */ bool Allowed( const SocketAddress& host ); /* Checks whether access should be allowed for the IP address ~host~. */ bool Denied( const SocketAddress& host ); /* Checks whether access should be denied for the IP address ~host~. */ static void SetDelimiter( const char newDelimiter = '/' ); /* Sets the delimiter used between IP address, IP address mask and access policy when a rule is sent onto an output stream. */ static char GetDelimiter(); /* Returns the current delimiter used between IP address, IP address mask and access policy when a rule is sent onto an output stream. */ friend std::ostream& operator <<( std::ostream& os, SocketRule& r ); /* Allows to send a rule to an output stream. */ protected: Policy allowDeny; // Access policy in_addr ipAddr; // IP address of the rule in_addr ipMask; // IP address mask static char delimiter; // Output delimiter }; /* 1.1 Class "SocketRuleSet"[1] */ class SDB_EXPORT SocketRuleSet { public: SocketRuleSet( SocketRule::Policy initDefaultPolicy = SocketRule::DENY ); /* Creates an empty rule set with a default access policy ~initDefaultPolicy~. */ virtual ~SocketRuleSet() {}; /* Destroys a rule set. */ void AddRule( const std::string& strIpAddr, const std::string& strIpMask, SocketRule::Policy allowDeny = SocketRule::ALLOW ); /* Adds a rule consisting of the IP address ~strIpAddr~, the IP address mask ~strIpMask~ and the access policy ~allowDeny~ to the rule set. */ bool Ok( const SocketAddress& host ); /* Checks whether access should be granted for the IP address ~host~. */ bool LoadFromFile( const std::string& ruleFileName ); /* Loads a set of rules from the file with name ~ruleFileName~. The method returns "true"[4] when the file could be read successfully. */ bool StoreToFile( const std::string& ruleFileName ); /* Stores a set of rules into the file with name ~ruleFileName~. The method returns "true"[4] when the file could be written successfully. */ friend std::ostream& operator <<(std::ostream& os, SocketRuleSet& r); /* Allows to send a set of rules to an output stream. */ protected: std::vector rules; // Set of rules SocketRule::Policy defaultPolicy; // Access policy for set }; /* 1.1 Class "SocketBuffer"[1] The class "SocketBuffer"[1] implements the standard *streambuf* protocol for sockets. Separate buffers for reading and writing are implemented. */ class SDB_EXPORT SocketBuffer : public std::streambuf { public: SocketBuffer( Socket& socket); /* Creates a socket buffer associated with the socket ~socket~. */ ~SocketBuffer(); /* Destroys a socket buffer. */ bool is_open() const { return socketHandle != 0; } /* Checks whether the stream buffer is ready for operation. */ SocketBuffer* close(); /* Closes the stream buffer. */ virtual pos_type seekoff(off_type, std::ios_base::seekdir, std::ios_base::openmode /*__mode*/ = std::ios_base::in | std::ios_base::out) { return EOF; } /* Disallows seeking in the stream buffer since a TCP stream is strictly sequential. */ std::streamsize xsputn( const char* s, const std::streamsize n ); /* Allows faster writing onto the socket of a string consisting on ~n~ characters. */ std::streamsize xsgetn( char* s, const std::streamsize n ); /* Allows faster reading from the socket of a string consisting on ~n~ characters. */ protected: virtual int overflow( int ch = EOF ); /* Writes the data in the output buffer to the associated socket. */ virtual int uflow(); virtual int underflow(); /* Tries to read data from the associated socket, when the input buffer is empty. */ virtual int sync(); /* Flushes all output data from the buffer to the associated socket. */ virtual int pbackfail( int ch = EOF ); std::streamsize showmanyc() { std::cerr << "showmanyc called!" << std::endl; return 0; } std::streampos seekpos ( std::streampos __attribute__((unused)) sp, std::ios_base::openmode __attribute__((unused)) which = std::ios_base::in | std::ios_base::out ) { std::cerr << "streampos called!" << std::endl; return EOF; } std::streambuf * setbuf ( char __attribute__((unused)) * s, std::streamsize __attribute__((unused)) n ) { std::cerr << "setbuf called!" << std::endl; return this; }; void imbue ( const std::locale __attribute__((unused)) & loc ) { std::cerr << "imbue called!" << std::endl; } /* Disallows to unget a character. */ private: SocketBuffer( const SocketBuffer&); SocketBuffer& operator=( const SocketBuffer& ); Socket* socketHandle; // Handle of associated socket int bufferSize; // Size of the I/O buffer char* inBuffer; // Input buffer char* outBuffer; // Output buffer }; /* 1.2 UDP-Sockets UDP (User Datagram Protocol) is a connectionless transmission network protocol. Its advantage is, that it is faster than TCP. Its disadvantage is, that there is no guarantee of correctness, corect ordering or reliabilty of deliverance of data sent. Instead of sending data streams between two connected sockets, UDP allows to send ~datagrams~ to remote sockets and receive such data. Initialization and finalization is already done by the TCP/IP part in this file. */ #ifndef UDP_MAXBUF #define UDP_MAXBUF 1048576 // set bufferlength to 1MB #endif struct addrinfo* CloneAddrInfo(const struct addrinfo *orig); /* A function to create a clone of a ~struct addrinfo~ */ /* 1.2.1 Class ~UDPaddress~ */ class SDB_EXPORT UDPaddress { public: UDPaddress(); /* Creates a new ~UDPaddress~ automatically determining the own IP-address and some standard port number. */ UDPaddress(const struct addrinfo *addr); /* Creates a ~UDPaddress~ from a given ~struct addrinfo~, that may provide information on IP-version, IP address, port number, flags, etc. */ UDPaddress(const struct sockaddr_in *addr); UDPaddress(const struct sockaddr_in6 *addr); UDPaddress(const struct sockaddr_storage *addr); /* Creates a ~UDPaddress~ from a given valid ~struct sockaddr\_in~/~sockaddr\_in6~/~sockaddr\_storage~ */ UDPaddress(const UDPaddress &addr); /* Copy constructor. Creates a ~UDPaddress~ from another given UDPaddress ~addr~. */ UDPaddress(const std::string ip, const std::string port, const short int IPver = AF_UNSPEC); /* Creates a ~UDPaddress~ using the given IP address, port number, and ip-version. IP-version is determined automatically. Valid values for IPver are AF\_INET, AF\_INET6, or AF\_UNSPEC (default). When AF\_UNSPEC is used, the IP-version is determined automatically from ~ip~ which may be an IP-address or a fully qualified cononic host name. */ ~UDPaddress(); inline std::string getFamily() const { if(myFamily == AF_INET){ return "IPv4"; } else if(myFamily == AF_INET6){ return "IPv6"; } return "unknown"; }; /* Return the protocol family (IPv4, IPv6) associated with this address as a string. */ inline int getFamilyI() const { return myFamily; }; inline int getSockTypeI() const { return SOCK_DGRAM; }; inline size_t getAddrLenI() const { return myAddrlen; }; inline const struct sockaddr *getAddrI() const { return myAddr; }; /* Get code for IP-family, socket type, protocol type, address type length, and address. Used for calls of ~socket()~, connect(), etc. */ inline std::string getIP() const { return myIP; }; /* Return the associated IP address in string format. Two versions: as integer (for socket()) and text. */ inline std::string getHostName() const { return myCanonname; }; /* Return the fully qualified host name as a string. */ inline std::string getPort() const { return myPort; }; /* Return the associated port number in string format. */ inline bool isOk() const { return myOk; }; /* Returns true, iff the instance has been created successfully. Error descriptions can be accessed by means of ~getErrorText()~. */ std::string getErrorText() const { return myErrorMsg; }; /* Returns the error description. */ UDPaddress& operator=(const UDPaddress& addr); /* The assignment operator */ std::ostream& Print(std::ostream& o) const; protected: bool myOk; std::string myErrorMsg; int myFamily; size_t myAddrlen; struct sockaddr *myAddr; std::string myIP; std::string myPort; std::string myCanonname; bool updateMemberVariables(); bool updateMemberVariables(const struct addrinfo *myAddrInfo); static std::string getErrorCodeStr(const int resGetaddrinfo); }; std::ostream& operator<<(std::ostream &o, const struct sockaddr_in &a); std::ostream& operator<<(std::ostream &o, const struct sockaddr_in6 &a); std::ostream& operator<<(std::ostream &o, const struct sockaddr_storage &a); std::ostream& operator<<(std::ostream &o, const struct sockaddr &a); std::ostream& operator<<(std::ostream &o, const UDPaddress &a); std::ostream& operator<<(std::ostream &o, const struct addrinfo &a); std::ostream& operator<<(std::ostream &o, const struct protoent &p); /* 1.2.1 Class ~UDPsocket~ */ enum UDPSocketState {UDPVOID=0, // Invalid socket (not yet created) UDPFRESH=1, // newly created but unbound socket UDPNORECV=2, // bound socket: no receive - send only UDPNOSEND=3, // bound socket: no send - receive only UDPNONE=4, // bound socket: may neither send, nor receive UDPALL=5}; // bound socket: may send and receive class SDB_EXPORT UDPsocket { public: UDPsocket(); /* Initially clears ~ok~ and ~errorMsg~. Create a fresh ~UDPsocket~ in state UDPVOID. */ UDPsocket(const UDPaddress &address); /* Initially clears ~ok~ and ~errorMsg~. Create a ~UDPsocket~ using the data from UDPaddress. The socket will be in state UDPVOID (if ~address~ in invalid) or UDPFRESH. */ UDPsocket(const UDPsocket &sock); /* Copy constructor */ ~UDPsocket(); bool bind(); /* Initially clears ~ok~ and ~errorMsg~. Bind the socket to a local port as referenced by protected member ~myAddress~. If successful, the socket state is changed to UDPALL. On failure, state will be UDPFRESH. Status transitions: ---- UDPVOID --> UDPVOID {UDPFRESH, UDPNORECV, UDPNOSEND, UDPNONE, UDPALL} --> UDPALL ---- Returns ~true~ iff the socket is bound at the end of this function. */ bool connect(const UDPaddress &remote); /* Initially clears ~ok~ and ~errorMsg~. Connect to a ~remote~ address. UDPsocket do not need to be connected to work with them, but they are allowed to do so. Returns true, if remote is ~status~ remains unchanged. ~status~ remains unchanged. */ int writeTo(const UDPaddress &receiver, const std::string &message); /* Initially clears ~ok~ and ~errorMsg~. Send datagram ~message~ to the address specified by ~receiver~. Returns the amount of transmitted bytes. If <0, an error occured. ---- UDPFRESH --> UDPALL UDPVOID, UDPNORECV, UDPNOSEND, UDPNONE, UDPALL: unchanged ---- */ int write(const std::string &message); /* Initially clears ~ok~ and ~errorMsg~. Send datagram ~message~ to the connected remote socket. Requires the socket to be bound to a local port and connected to a remote socket. Returns the amount of transmitted bytes. If <0, an error occured. ---- UDPVOID, UDPFRESH, UDPNORECV, UDPNOSEND, UDPNONE, UDPALL: unchanged ---- */ std::string readFrom(UDPaddress &sender, const double timeoutSecs); /* Initially clears ~ok~ and ~errorMsg~. Binds the UDPsocket to the specified port (if necessary) and waits for data for up to ~timeoutSecs~ seconds. Negative ~timeoutSecs~ results in blocking. ~sender~ is changed to the address of the sender. If a timeout occurs, ~sender~ remains unmodified. If the socket is not already bound to a local port, this is done. The received message is returend as a string. If an error or timeout occurs, the result is an empty string. ---- UDPVOID, UDPALL --> UDPVOID UDPFRESH, UDPNORECV, UDPNOSEND, UDPNONE, UDPALL: unchanged ---- */ std::string read(const double timeoutSecs); /* Initially clears ~ok~ and ~errorMsg~. Receive a datagram message (return value) to the connected remote socket. Requires the socket to be bound to a local port and connected to a remote socket. Waits for data for up to ~timeoutSecs~ seconds. Negative ~timeoutSecs~ results in blocking. The received message is returend as a string. If an error or timeout occurs, the result is an empty string and an according error message is passed. ---- UDPVOID, UDPALL --> UDPVOID UDPFRESH, UDPNORECV, UDPNOSEND, UDPNONE, UDPALL: unchanged ---- */ bool close(); /* Initially clears ~ok~ and ~errorMsg~. Close the socket. Returns true, iff the socket was closed and reaches status UDPFRESH. ---- UDPVOID --> UDPVOID UDPFRESH, UDPNORECV, UDPNOSEND, UDPNONE, UDPALL --> UDPFRESH ---- */ bool shutdown(UDPSocketState how); /* Initially clears ~ok~ and ~errorMsg~. Shut down the socket. Returns the state of ~ok~. ~how~ must be from UDPNORECV, UDPNOSEND, UDPNONE, UDPALL. Returns ~false~ for invalid argument or if status is in UDPVOID, UDPFRESH. ---- UDPVOID --> UDPVOID UDPFRESH --> UDPFRESH UDPNORECV, UDPNOSEND, UDPNONE, UDPALL --> ---- */ inline bool isOk() const { return ok; }; /* Check whether some error has occured on this socket. If ~true~, call ~getErrorText()~ to get the last error message. Does not change ~status~, ~ok~ or ~errorMsg~. */ inline std::string getErrorText() const { return errorMsg; }; /* Return the last error message for this UDPsocket. Does not change ~status~, ~ok~ or ~errorMsg~. */ inline UDPaddress getAddress() const { return myAddress; }; /* Return this socket's address. Does not change ~status~, ~ok~ or ~errorMsg~. */ inline UDPaddress getPartnerAddress() const { return partnerAddress; }; /* Return this socket's partner's address. If the socket is not connected, for the result address !isOk() will hold. Does not change ~status~, ~ok~ or ~errorMsg~. */ inline bool isConnected() const { return connected; }; /* Returns true, iff this socket is connected to a remote host. Only if it is connected, methods ~write()~ and ~read()~ can be used. Use methods ~writeTo()~ and ~readFrom()~, otherwise. Does not change ~status~, ~ok~ or ~errorMsg~. */ inline UDPSocketState getStatus() const { return status; }; /* Returns the sockets's state. Does not change ~status~, ~ok~ or ~errorMsg~. */ UDPsocket& operator=(const UDPsocket& sock); /* Assignment operator */ std::ostream& Print(std::ostream& o) const; protected: UDPSocketState status; // socket state bool connected; // true, iff in connected mode bool ok; // false, iff an error occured during the // last member function call. Should be set // set to true at the beginning of such // functions. SocketDescriptor mySocket; // socket handles UDPaddress partnerAddress; // current communication partner UDPaddress myAddress; // address info used to create // the socketHandle std::string errorMsg; // last error message }; std::ostream& operator<<(std::ostream &o, const UDPsocket &s); #endif