| @@ -20,11 +20,359 @@ | |||
| //============================================================================== | |||
| // See networking.hpp for notes. | |||
| // See networking.inline.hpp for inlined methods & functions. | |||
| #include "networking.hpp" | |||
| Networking Network; // Finally creating the Network instance. | |||
| namespace codedweller { | |||
| Networking Network; // Creating _THE_ Network instance. | |||
| #if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64) | |||
| int Networking::getLastError() { // In windows you get the last error | |||
| return WSAGetLastError(); // from WSAGetLastError(); | |||
| } | |||
| int Networking::setNonBlocking(hSocket socket) { // Set a winsock to non-blocking | |||
| unsigned long nonblocking = 1; // Create a flag... | |||
| int result = 0; | |||
| if(0 != ioctlsocket(socket, FIONBIO, &nonblocking)) { // Set the state of the socket. | |||
| result = -1; // If that fails then return -1. | |||
| } | |||
| return result; // Show 'em my motto! | |||
| } | |||
| int Networking::closeSocket(hSocket socket) { // Close a socket in winsock | |||
| return closesocket(socket); // wraps closesocket(). | |||
| } | |||
| bool Networking::WouldBlock(int ErrorCode) { // ErrorCode matches [WSA]EWOULDBLOCK. | |||
| return (WSAEWOULDBLOCK == ErrorCode); | |||
| } | |||
| bool Networking::InProgress(int ErrorCode) { // ErrorCode matches [WSA]EINPROGRESS. | |||
| return( // [WSA]EALREADY also returns true. | |||
| WSAEINPROGRESS == ErrorCode || // In fact, on Win* platforms we could | |||
| WSAEALREADY == ErrorCode || // get any of these when retesting | |||
| WSAEWOULDBLOCK == ErrorCode || // open() for a connection. | |||
| WSAEINVAL == ErrorCode | |||
| ); | |||
| } | |||
| bool Networking::IsConnected(int ErrorCode) { // ErrorCode matches [WSA]EISCONN. | |||
| return(WSAEISCONN == ErrorCode); | |||
| } | |||
| #else | |||
| //// GNU platform | |||
| int Networking::getLastError() { // In GNU you get the last error | |||
| return errno; // from errno; | |||
| } | |||
| int Networking::setNonBlocking(hSocket socket) { // Set a socket to non-blocking | |||
| int flags, result; // Grab a place to hold the flags. | |||
| flags = fcntl(socket, F_GETFL, 0); // Get the current flags. | |||
| result = fcntl(socket, F_SETFL, flags | O_NONBLOCK); // Set the NONBLOCK flag & return. | |||
| return result; // Return the result. | |||
| } | |||
| int Networking::closeSocket(hSocket socket) { // Close a socket in GNU | |||
| return close(socket); // wraps close(). | |||
| } | |||
| bool Networking::WouldBlock(int ErrorCode) { // ErrorCode matches [WSA]EWOULDBLOCK. | |||
| return (EWOULDBLOCK == ErrorCode); | |||
| } | |||
| bool Networking::InProgress(int ErrorCode) { // ErrorCode matches [WSA]EINPROGRESS. | |||
| return( // [WSA]EALREADY also returns true. | |||
| EINPROGRESS == ErrorCode || | |||
| EALREADY == ErrorCode | |||
| ); | |||
| } | |||
| bool Networking::IsConnected(int ErrorCode) { // ErrorCode matches [WSA]EISCONN. | |||
| return(EISCONN == ErrorCode); | |||
| } | |||
| #endif | |||
| // End Platform Specific | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // Begin Platform Agnostic | |||
| //// class IP4Address ////////////////////////////////////////////////////////// | |||
| IP4Address::IP4Address():IP(0){} // Blank constructor IP = 0.0.0.0 | |||
| IP4Address::IP4Address(const unsigned long int newIP):IP(newIP){} // Constructor given unsigned long | |||
| IP4Address::IP4Address(const IP4Address& newIP):IP(newIP.IP){} // Constructor given an IP4Address | |||
| IP4Address::IP4Address(const char* newIP) { (*this) = newIP; } // Construcing with a cstring. | |||
| IP4Address::IP4Address(const std::string& newIP) { (*this) = newIP; } // Constructing with a cppstring. | |||
| IP4Address& IP4Address::operator=(const unsigned long int Right) { // Convert from unsigned long int. | |||
| IP = Right; | |||
| return *this; | |||
| } | |||
| IP4Address& IP4Address::operator=(const char* Right) { // Convert from c string. | |||
| IP = ntohl(inet_addr(Right)); | |||
| return *this; | |||
| } | |||
| IP4Address& IP4Address::operator=(const std::string& Right) { // Convert from cpp string. | |||
| IP = ntohl(inet_addr(Right.c_str())); | |||
| return *this; | |||
| } | |||
| bool IP4Address::operator<(const IP4Address Right) const { // < Comparison. | |||
| return (IP < Right.IP); | |||
| } | |||
| bool IP4Address::operator>(const IP4Address Right) const { // > Comparison. | |||
| return (IP > Right.IP); | |||
| } | |||
| bool IP4Address::operator==(const IP4Address Right) const { // == Comparison. | |||
| return (IP == Right.IP); | |||
| } | |||
| bool IP4Address::operator!=(const IP4Address Right) const { // != Comparison. | |||
| return (IP != Right.IP); | |||
| } | |||
| bool IP4Address::operator<=(const IP4Address Right) const { // <= Comparison. | |||
| return (IP <= Right.IP); | |||
| } | |||
| bool IP4Address::operator>=(const IP4Address Right) const { // >= Comparison. | |||
| return (IP >= Right.IP); | |||
| } | |||
| //// class SocketAddress /////////////////////////////////////////////////////// | |||
| void SocketAddress::clear() { | |||
| memset(&Address, 0, sizeof(Address)); // Zero out the address strcuture | |||
| Address.sin_family = AF_INET; // Internet Address Family ip4 | |||
| Address.sin_addr.s_addr = htonl(INADDR_ANY); // Any IP address | |||
| Address.sin_port = 0; // Zero means any port. | |||
| } | |||
| SocketAddress::SocketAddress() { // Constructor sets up w/ wildcards | |||
| clear(); // Conveniently, we can use clear() :-) | |||
| } | |||
| struct sockaddr_in* SocketAddress::getPtr_sockaddr_in() { // Returns a pointer to sockaddr_in. | |||
| return &Address; // Simply return it's address. | |||
| } | |||
| struct sockaddr* SocketAddress::getPtr_sockaddr() { // Returns a pointer to sockaddr. | |||
| return (struct sockaddr*) &Address; | |||
| } | |||
| socklen_t SocketAddress::getAddressSize() { | |||
| return sizeof(Address); // Return the size of the structure. | |||
| } | |||
| void SocketAddress::setAddress(unsigned long ipAddress) { // Set the IP address from an unsigned int | |||
| Address.sin_addr.s_addr = htonl(ipAddress); // Convert to network order and assign. | |||
| } | |||
| void SocketAddress::setAddress(char* ipString) { // Set the IP address from a cstring | |||
| Address.sin_addr.s_addr = inet_addr(ipString); // Convert to number and assign. | |||
| } | |||
| unsigned long SocketAddress::getAddress() { // Get the IP address as an unsigned int | |||
| return ntohl(Address.sin_addr.s_addr); // Convert to host order and return. | |||
| } | |||
| void SocketAddress::setPort(unsigned short port) { // Set the port address from an int | |||
| Address.sin_port = htons(port); // Convert to network order and set. | |||
| } | |||
| void SocketAddress::setPort(char* port) { // Set the port address from a cstring | |||
| setPort(atoi(port)); // Convert to int and set. | |||
| } | |||
| unsigned short SocketAddress::getPort() { // Get the port address as an unsigned int | |||
| return ntohs(Address.sin_port); // Convert to host order and return. | |||
| } | |||
| const char* SocketAddress::getPort(char* str) { // Get the port address into a cstring. | |||
| if(NULL == str) { // If the caller did not provide a | |||
| str = PortStringBuffer; // buffer to use then we will use ours. | |||
| } | |||
| sprintf(str,"%d",getPort()); // Get the port and convert to cstring. | |||
| return str; // Return the string we got. | |||
| } | |||
| //// class Socket ////////////////////////////////////////////////////////////// | |||
| Socket::Socket() : // When starting up we are | |||
| Handle(INVALID_SOCKET), OpenSucceeded(false) { // not yet valid. | |||
| } | |||
| Socket::~Socket() { // When shutting down, be sure | |||
| if(INVALID_SOCKET != Handle) { // any open socket is closed without | |||
| Network.closeSocket(Handle); // throwing any exceptions. | |||
| } | |||
| } | |||
| void Socket::close() { // When we close, | |||
| if(INVALID_SOCKET != Handle) { // If the handle is open then | |||
| if(Network.closeSocket(Handle)) { // close the handle and check for error. | |||
| LastError = Network.getLastError(); // If there was an error record it. | |||
| if(!Network.WouldBlock(LastError)) { // If the error was not WOULDBLOCK | |||
| throw Networking::ControlError( // then throw a ControlError exception. | |||
| Network.DescriptiveError( | |||
| "Socket::close()", LastError)); | |||
| } | |||
| } else { // If there was no error then | |||
| LastError = 0; // reset the LastError value. | |||
| } | |||
| Handle = INVALID_SOCKET; // and reset the handle to INVALID. | |||
| NonBlocking = false; // The default is Blocking. | |||
| OpenSucceeded = false; // After close, forget we opened. | |||
| } | |||
| } | |||
| hSocket Socket::getHandle() { // Returns the current Socket handle. | |||
| return Handle; | |||
| } | |||
| bool Socket::isNonBlocking() { // Returns true if socket is NonBlocking | |||
| return NonBlocking; | |||
| } | |||
| void Socket::makeNonBlocking() { // Sets the socket to NonBlocking mode. | |||
| if(0 > Network.setNonBlocking(Handle)) { // Feed the call through Network. | |||
| LastError = Network.getLastError(); // If it didn't work, go get the error. | |||
| NonBlocking = false; // We are NOT NonBlocking. | |||
| throw Networking::ControlError( // Throw a control error. | |||
| Network.DescriptiveError( | |||
| "Socket::makeNonBlocking()", LastError)); | |||
| } else { | |||
| NonBlocking = true; // If we didn't throw, we're ON. | |||
| } | |||
| } | |||
| bool Socket::isReuseAddress() { return ReuseAddress; } // True if socket is set SO_REUSEADDR. | |||
| bool Socket::isReuseAddress(bool set) { return (ReuseAddress = set); } // Changes SO_REUSEADDR setting. | |||
| bool Socket::isOpen() { // True if the socket is open. | |||
| return( | |||
| INVALID_SOCKET != Handle && // A valid handle and | |||
| true == OpenSucceeded // a successful open operation | |||
| ); // means we're open. | |||
| } | |||
| int Socket::getLastError() { // Returns the last error for this socket. | |||
| return LastError; | |||
| } | |||
| //// class TCPClient /////////////////////////////////////////////////////////// | |||
| TCPClient::TCPClient(TCPListener& L, hSocket H, SocketAddress& A) : // How to create a TCPClient. | |||
| MyListener(L) { // Capture our listener. | |||
| Handle = H; // Capture the new socket handle. | |||
| RemoteAddress = A; // Capture the client address. | |||
| ReadPointer = ReadBuffer; // Set the read position to zero. | |||
| DataLength = 0; // There is no data yet. | |||
| OpenSucceeded = true; // We're getting an open socket. | |||
| } | |||
| TCPClient::~TCPClient() { // When destroying a TCPClient | |||
| try{ if(isOpen()) close(); } catch(...) {} // silently close any open connections. | |||
| } | |||
| void TCPClient::open() { // We provide open() as unsupported. | |||
| throw Networking::NotSupportedError( // Throw an exception if this is called. | |||
| Network.DescriptiveError( | |||
| "TCPClient::open()", LastError)); | |||
| } | |||
| bool TCPClient::ReadBufferIsEmpty() { // True if the ReadBuffer is empty. | |||
| return (0 >= DataLength); // We can check that with DataLength. | |||
| } | |||
| void TCPClient::fillReadBuffer() { // Fills the buffer from the socket. | |||
| LastError = 0; // Clear the LastError value. | |||
| ReadPointer = ReadBuffer; // Reset the ReadPointer. | |||
| DataLength = recv(Handle, ReadBuffer, sizeof(ReadBuffer), MSG_NOSIGNAL); // Try to read some data. | |||
| if(0 >= DataLength) { // If there was an error then | |||
| LastError = Network.getLastError(); // Grab the last error code. | |||
| DataLength = 0; // Correct the DataLength. | |||
| if(Network.WouldBlock(LastError)) { // If the error was WouldBlock then | |||
| return; // simply return - it's ok. | |||
| } else { // If it was a different error | |||
| throw Networking::SocketReadError( // then throw a ReadError. | |||
| Network.DescriptiveError( | |||
| "TCPClient::fillReadBuffer()", LastError)); | |||
| } | |||
| } // If we succeeded then our ReadBuffer | |||
| } // assembly is in good shape. | |||
| bool TCPClient::isNonBlocking() { // Provided for MessagePort. | |||
| return Socket::isNonBlocking(); | |||
| } | |||
| unsigned long TCPClient::getRemoteIP() { // Get remote IP as long. | |||
| return RemoteAddress.getAddress(); | |||
| } | |||
| const char* TCPClient::getRemoteIP(char* str) { // Get IP as string. | |||
| return RemoteAddress.getAddress(str); | |||
| } | |||
| unsigned short TCPClient::getRemotePort() { // Get remote Port as unsigned short. | |||
| return RemoteAddress.getPort(); | |||
| } | |||
| const char* TCPClient::getRemotePort(char* str) { // Get Port as string. | |||
| return RemoteAddress.getPort(str); | |||
| } | |||
| //// class TCPHost ///////////////////////////////////////////////////////////// | |||
| TCPHost::~TCPHost() { // When destroying a TCPHost | |||
| try{ if(isOpen()) close(); } catch(...) {} // silently close any open connection. | |||
| } | |||
| bool TCPHost::ReadBufferIsEmpty() { // True if the ReadBuffer is empty. | |||
| return (0 >= DataLength); // We can check that with DataLength. | |||
| } | |||
| void TCPHost::fillReadBuffer() { // Fills the buffer from the socket. | |||
| LastError = 0; // Clear the LastError value. | |||
| ReadPointer = ReadBuffer; // Reset the ReadPointer. | |||
| DataLength = recv(Handle, ReadBuffer, sizeof(ReadBuffer), MSG_NOSIGNAL); // Try to read some data. | |||
| if(0 >= DataLength) { // If there was an error then | |||
| LastError = Network.getLastError(); // Grab the last error code. | |||
| DataLength = 0; // Correct the DataLength. | |||
| if(Network.WouldBlock(LastError)) { // If the error was WouldBlock then | |||
| return; // simply return - it's ok. | |||
| } else { // If it was a different error | |||
| throw Networking::SocketReadError( // then throw a ReadError. | |||
| Network.DescriptiveError( | |||
| "TCPHost::fillReadBuffer()", LastError)); | |||
| } | |||
| } // If we succeeded then our ReadBuffer | |||
| } // assembly is in good shape. | |||
| bool TCPHost::isNonBlocking() { // Provided for MessagePort. | |||
| return Socket::isNonBlocking(); | |||
| } | |||
| //// class TCPListener ///////////////////////////////////////////////////////// | |||
| TCPListener::~TCPListener() { // When destroying a TCPListener | |||
| try{ close(); } catch(...) {} // silently close if not already done. | |||
| } | |||
| //// Platform Specific Stuff /////////////////////////////////////////////////// | |||
| @@ -146,7 +494,7 @@ string Networking::DescriptiveError(string Msg, int Errno) { | |||
| Msg.append(s); // then append it. | |||
| } | |||
| else { // If we don't know what Errno means | |||
| ostringstream ErrNoMsg; // then say so and pass on Errno as | |||
| std::ostringstream ErrNoMsg; // then say so and pass on Errno as | |||
| ErrNoMsg << " UNKNOWN ErrorNumber = " << Errno; // well so someone can figure it out. | |||
| Msg.append(ErrNoMsg.str()); | |||
| } | |||
| @@ -181,7 +529,7 @@ Networking::~Networking() { | |||
| // Error description handling for humans. | |||
| string Networking::DescriptiveError(string Msg, int Errno) { // Form a descriptive error w/ errno. | |||
| std::string Networking::DescriptiveError(std::string Msg, int Errno) { // Form a descriptive error w/ errno. | |||
| Msg.append(" "); Msg.append(strerror(Errno)); | |||
| return Msg; | |||
| }; | |||
| @@ -232,13 +580,13 @@ IP4Address::operator unsigned long int() const { | |||
| return IP; // Return it. | |||
| } | |||
| IP4Address::operator string() const { // Assign to a string. | |||
| IP4Address::operator std::string() const { // Assign to a string. | |||
| char stringbfr[IPStringBufferSize]; // Grab a temporary buffer. | |||
| memset(stringbfr, 0, sizeof(stringbfr)); // Null out it's space. | |||
| int a0, a1, a2, a3; // Grab some integers. | |||
| splitIP(IP, a0, a1, a2, a3); // Split the IP in the IP4Address. | |||
| sprintf(stringbfr, "%d.%d.%d.%d", a0, a1, a2, a3); // Format the octets. | |||
| return string(stringbfr); // Return a string. | |||
| return std::string(stringbfr); // Return a string. | |||
| } | |||
| //// SocketAddress methods ///////////////////////////////////////////////////// | |||
| @@ -340,7 +688,7 @@ void TCPListener::open() { | |||
| if(!OpenStage2Complete) { // Do this stage only once. | |||
| int result = // Bind our socket to the LocalAddress. | |||
| ::bind( | |||
| bind( | |||
| Handle, | |||
| LocalAddress.getPtr_sockaddr(), | |||
| LocalAddress.getAddressSize()); | |||
| @@ -393,30 +741,30 @@ TCPClient* TCPListener::acceptClient() { | |||
| return NULL; // non blocking mode so we return | |||
| } // NULL when we see them. | |||
| } | |||
| // Set SO_NOSIGPIPE if needed | |||
| if( // On some systems we may have to | |||
| 0 != SO_NOSIGPIPE && // use SO_NOSIPIPE but if they offer | |||
| 0 == MSG_NOSIGNAL // MSG_NOSIGNAL we prefer that instead. | |||
| ) { | |||
| int TurnedOn = 1; // Prepare to turn this option on. | |||
| int result = // Set SO_NOSIGPIPE. | |||
| setsockopt( | |||
| NewHandle, | |||
| SOL_SOCKET, | |||
| SO_NOSIGPIPE, | |||
| (char*) &TurnedOn, | |||
| sizeof(TurnedOn)); | |||
| if(0 > result) { // If there was an error then | |||
| LastError = Network.getLastError(); // Capture the error information | |||
| Network.closeSocket(NewHandle); // close the handle (avoid leaks) | |||
| throw Networking::SocketSetSockOptError( // and throw a descriptive exception. | |||
| Network.DescriptiveError( | |||
| "TCPListener::acceptClient().setsockopt(SO_NOSIGPIPE)", LastError)); | |||
| } | |||
| } | |||
| // Set SO_NOSIGPIPE if needed | |||
| if( // On some systems we may have to | |||
| 0 != SO_NOSIGPIPE && // use SO_NOSIPIPE but if they offer | |||
| 0 == MSG_NOSIGNAL // MSG_NOSIGNAL we prefer that instead. | |||
| ) { | |||
| int TurnedOn = 1; // Prepare to turn this option on. | |||
| int result = // Set SO_NOSIGPIPE. | |||
| setsockopt( | |||
| NewHandle, | |||
| SOL_SOCKET, | |||
| SO_NOSIGPIPE, | |||
| (char*) &TurnedOn, | |||
| sizeof(TurnedOn)); | |||
| if(0 > result) { // If there was an error then | |||
| LastError = Network.getLastError(); // Capture the error information | |||
| Network.closeSocket(NewHandle); // close the handle (avoid leaks) | |||
| throw Networking::SocketSetSockOptError( // and throw a descriptive exception. | |||
| Network.DescriptiveError( | |||
| "TCPListener::acceptClient().setsockopt(SO_NOSIGPIPE)", LastError)); | |||
| } | |||
| } | |||
| // If things have gone well we can do what we came for. | |||
| @@ -433,14 +781,14 @@ int TCPClient::transmit(const char* bfr, int size) { | |||
| if(0 > size) // Watch out for bad sizes. | |||
| throw Networking::SocketWriteError("TCPClient::transmit() 0 > size!"); | |||
| LastError = 0; // No errors yet. | |||
| int ByteCount = 0; // No bytes sent yet this pass. | |||
| LastError = 0; // No errors yet. | |||
| int ByteCount = 0; // No bytes sent yet this pass. | |||
| ByteCount = send(Handle, bfr, size, MSG_NOSIGNAL); // Try to send and capture the count. | |||
| LastError = Network.getLastError(); // Grab any error code. | |||
| bool AnErrorOccurred = (0 > ByteCount); // How to know if an error occurred. | |||
| const int NoBytesSent = 0; // This is our "Would Block" result. | |||
| LastError = Network.getLastError(); // Grab any error code. | |||
| bool AnErrorOccurred = (0 > ByteCount); // How to know if an error occurred. | |||
| const int NoBytesSent = 0; // This is our "Would Block" result. | |||
| if(AnErrorOccurred) { // If there was an error check it out. | |||
| if(Network.WouldBlock(LastError)) { // If the error was "Would Block" then | |||
| return NoBytesSent; // return no bytes sent (try again). | |||
| @@ -450,7 +798,7 @@ int TCPClient::transmit(const char* bfr, int size) { | |||
| "TCPClient::transmit().send()", LastError)); | |||
| } | |||
| } | |||
| return ByteCount; // Usually: return the sent byte count. | |||
| } | |||
| @@ -544,12 +892,12 @@ void TCPHost::open() { | |||
| LastError = 0; // Clear our LastError value. | |||
| bool SuccessFlag = true; // Begin optimistically. | |||
| // Set Socket Options | |||
| // Set Socket Options | |||
| if(!OpenStage1Complete) { // If we haven't done this yet: | |||
| // Set SO_REUSEADDR if turned on | |||
| // Set SO_REUSEADDR if turned on | |||
| int ReuseAddress_Flag = (ReuseAddress? 1:0); // Setup an appropriate integer flag. | |||
| int result = // Set SO_REUSEADDR before bind(). | |||
| setsockopt( | |||
| @@ -566,31 +914,31 @@ void TCPHost::open() { | |||
| Network.DescriptiveError( | |||
| "TCPHost::open().setsockopt(SO_REUSEADDR)", LastError)); | |||
| } | |||
| // Set SO_NOSIGPIPE if needed | |||
| if( // On some systems we may have to | |||
| 0 != SO_NOSIGPIPE && // use SO_NOSIPIPE but if they offer | |||
| 0 == MSG_NOSIGNAL // MSG_NOSIGNAL we prefer that instead. | |||
| ) { | |||
| int TurnedOn = 1; // Prepare to turn this option on. | |||
| int result = // Set SO_NOSIGPIPE. | |||
| setsockopt( | |||
| Handle, | |||
| SOL_SOCKET, | |||
| SO_NOSIGPIPE, | |||
| (char*) &TurnedOn, | |||
| sizeof(TurnedOn)); | |||
| if(0 > result) { // If there was an error then | |||
| SuccessFlag = false; // we did not succeed. | |||
| LastError = Network.getLastError(); // Capture the error information and | |||
| throw Networking::SocketSetSockOptError( // throw. | |||
| Network.DescriptiveError( | |||
| "TCPHost::open().setsockopt(SO_NOSIGPIPE)", LastError)); | |||
| } | |||
| } | |||
| // Set SO_NOSIGPIPE if needed | |||
| if( // On some systems we may have to | |||
| 0 != SO_NOSIGPIPE && // use SO_NOSIPIPE but if they offer | |||
| 0 == MSG_NOSIGNAL // MSG_NOSIGNAL we prefer that instead. | |||
| ) { | |||
| int TurnedOn = 1; // Prepare to turn this option on. | |||
| int result = // Set SO_NOSIGPIPE. | |||
| setsockopt( | |||
| Handle, | |||
| SOL_SOCKET, | |||
| SO_NOSIGPIPE, | |||
| (char*) &TurnedOn, | |||
| sizeof(TurnedOn)); | |||
| if(0 > result) { // If there was an error then | |||
| SuccessFlag = false; // we did not succeed. | |||
| LastError = Network.getLastError(); // Capture the error information and | |||
| throw Networking::SocketSetSockOptError( // throw. | |||
| Network.DescriptiveError( | |||
| "TCPHost::open().setsockopt(SO_NOSIGPIPE)", LastError)); | |||
| } | |||
| } | |||
| OpenStage1Complete = true; // Skip this section from now on. | |||
| } // Done with stage 1. | |||
| @@ -684,3 +1032,5 @@ int TCPHost::delimited_receive(char* bfr, int size, char delimiter) { | |||
| // End Platform Agnostic Stuff | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| } // End namespace codedweller | |||
| @@ -22,11 +22,7 @@ | |||
| // The networking module abstracts network communications and provides a set | |||
| // of objects for handling most tasks. | |||
| // 20080313 _M Refactored to throw proper runtime_error exceptions. | |||
| // Include only once... | |||
| #ifndef M_Networking | |||
| #define M_Networking | |||
| #pragma once | |||
| #include <stdexcept> | |||
| #include <iostream> | |||
| @@ -34,8 +30,6 @@ | |||
| #include <sstream> | |||
| #include <cstring> | |||
| using namespace std; | |||
| #include <cstdlib> | |||
| #include <cstdio> | |||
| #include <cerrno> | |||
| @@ -47,9 +41,14 @@ using namespace std; | |||
| //// Windows headers... | |||
| #include <winsock2.h> | |||
| namespace codedweller { | |||
| typedef int socklen_t; // Posix uses socklen_t so we mimic it. | |||
| typedef SOCKET hSocket; // Winx handles Socket is opaque. | |||
| } // End namespace codedweller | |||
| #else | |||
| //// GNU Headers... | |||
| @@ -62,21 +61,27 @@ typedef SOCKET hSocket; | |||
| #include <unistd.h> | |||
| #include <fcntl.h> | |||
| namespace codedweller { | |||
| typedef int hSocket; // *nix uses int to handle a Socket. | |||
| const hSocket INVALID_SOCKET = -1; // -1 is the invalid Socket. | |||
| } // End namespace codedweller | |||
| #endif | |||
| namespace codedweller { | |||
| //// Handling SIGPIPE ////////////////////////////////////////////////////////// | |||
| #ifndef MSG_NOSIGNAL | |||
| const int MSG_NOSIGNAL = 0; // Fake this if it isn't defined. | |||
| #endif | |||
| #ifndef SO_NOSIGPIPE | |||
| const int SO_NOSIGPIPE = 0; // Fake this if it isn't defined. | |||
| #endif | |||
| //// Handling SIGPIPE ////////////////////////////////////////////////////////// | |||
| #ifndef MSG_NOSIGNAL | |||
| const int MSG_NOSIGNAL = 0; // Fake this if it isn't defined. | |||
| #endif | |||
| #ifndef SO_NOSIGPIPE | |||
| const int SO_NOSIGPIPE = 0; // Fake this if it isn't defined. | |||
| #endif | |||
| //// Tuning and Constants ////////////////////////////////////////////////////// | |||
| const unsigned long LOCALHOST = 0x7F000001; // 127.0.0.1 as an integer. | |||
| @@ -103,14 +108,14 @@ class IP4Address { | |||
| IP4Address(const IP4Address&); // Constructor given an IP4Address | |||
| IP4Address(const char* newIP); // Construcing with a cstring. | |||
| IP4Address(const string& newIP); // Constructing with a cppstring. | |||
| IP4Address(const std::string& newIP); // Constructing with a cppstring. | |||
| IP4Address& operator=(const unsigned long int Right); // Convert from unsigned long int. | |||
| IP4Address& operator=(const char* Right); // Convert from c string. | |||
| IP4Address& operator=(const string& Right); // Convert from cpp string. | |||
| IP4Address& operator=(const std::string& Right); // Convert from cpp string. | |||
| operator unsigned long int() const; | |||
| operator string() const; | |||
| operator std::string() const; | |||
| bool operator<(const IP4Address Right) const; // < Comparison. | |||
| bool operator>(const IP4Address Right) const; // > Comparison. | |||
| @@ -151,41 +156,41 @@ class Networking { | |||
| public: | |||
| class NotSupportedError : public runtime_error { // Thrown when something can't be done. | |||
| public: NotSupportedError(const string& w):runtime_error(w) {} | |||
| class NotSupportedError : public std::runtime_error { // Thrown when something can't be done. | |||
| public: NotSupportedError(const std::string& w):runtime_error(w) {} | |||
| }; | |||
| class InitializationError : public runtime_error { // Thrown if initialization fails. | |||
| public: InitializationError(const string& w):runtime_error(w) {} | |||
| class InitializationError : public std::runtime_error { // Thrown if initialization fails. | |||
| public: InitializationError(const std::string& w):runtime_error(w) {} | |||
| }; | |||
| class ControlError : public runtime_error { // Thrown if control functions fail. | |||
| public: ControlError(const string& w):runtime_error(w) {} | |||
| class ControlError : public std::runtime_error { // Thrown if control functions fail. | |||
| public: ControlError(const std::string& w):runtime_error(w) {} | |||
| }; | |||
| class SocketCreationError : public runtime_error { // Thrown if a call to socket() fails. | |||
| public: SocketCreationError(const string& w):runtime_error(w) {} | |||
| class SocketCreationError : public std::runtime_error { // Thrown if a call to socket() fails. | |||
| public: SocketCreationError(const std::string& w):runtime_error(w) {} | |||
| }; | |||
| class SocketSetSockOptError : public runtime_error { | |||
| public: SocketSetSockOptError(const string& w):runtime_error(w) {} // Thrown if a call to setsockopt() fails. | |||
| class SocketSetSockOptError : public std::runtime_error { // Thrown if a call to setsockopt() fails. | |||
| public: SocketSetSockOptError(const std::string& w):runtime_error(w) {} | |||
| }; | |||
| class SocketBindError : public runtime_error { // Thrown if a call to bind() fails. | |||
| public: SocketBindError(const string& w):runtime_error(w) {} | |||
| class SocketBindError : public std::runtime_error { // Thrown if a call to bind() fails. | |||
| public: SocketBindError(const std::string& w):runtime_error(w) {} | |||
| }; | |||
| class SocketListenError : public runtime_error { // Thrown if a call to listen() fails. | |||
| public: SocketListenError(const string& w):runtime_error(w) {} | |||
| class SocketListenError : public std::runtime_error { // Thrown if a call to listen() fails. | |||
| public: SocketListenError(const std::string& w):runtime_error(w) {} | |||
| }; | |||
| class SocketConnectError : public runtime_error { // Thrown if a call to connect() fails. | |||
| public: SocketConnectError(const string& w):runtime_error(w) {} | |||
| class SocketConnectError : public std::runtime_error { // Thrown if a call to connect() fails. | |||
| public: SocketConnectError(const std::string& w):runtime_error(w) {} | |||
| }; | |||
| class SocketAcceptError : public runtime_error { // Thrown if a call to accept() fails. | |||
| public: SocketAcceptError(const string& w):runtime_error(w) {} | |||
| class SocketAcceptError : public std::runtime_error { // Thrown if a call to accept() fails. | |||
| public: SocketAcceptError(const std::string& w):runtime_error(w) {} | |||
| }; | |||
| class SocketReadError : public runtime_error { // Thrown if a socket read call fails. | |||
| public: SocketReadError(const string& w):runtime_error(w) {} | |||
| class SocketReadError : public std::runtime_error { // Thrown if a socket read call fails. | |||
| public: SocketReadError(const std::string& w):runtime_error(w) {} | |||
| }; | |||
| class SocketWriteError : public runtime_error { // Thrown if a socket write call fails. | |||
| public: SocketWriteError(const string& w):runtime_error(w) {} | |||
| class SocketWriteError : public std::runtime_error { // Thrown if a socket write call fails. | |||
| public: SocketWriteError(const std::string& w):runtime_error(w) {} | |||
| }; | |||
| static string DescriptiveError(string Msg, int Errno); // Form a descriptive error w/ errno. | |||
| static std::string DescriptiveError(std::string Msg, int Errno); // Form a descriptive error w/ errno. | |||
| Networking(); | |||
| ~Networking(); | |||
| @@ -531,9 +536,4 @@ class UDPBroadcaster : public Socket, public MessagePort { | |||
| // End of UDPBroadcaster class | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| //// Include Inline methods and functions... | |||
| #include "networking.inline.hpp" | |||
| #endif | |||
| // End include Networking.hpp only once... | |||
| } // End namespace codedweller | |||
| @@ -1,375 +0,0 @@ | |||
| // networking.inline.hpp | |||
| // Copyright (C) 2006-2009 MicroNeil Research Corporation. | |||
| // | |||
| // This program is part of the MicroNeil Research Open Library Project. For | |||
| // more information go to http://www.microneil.com/OpenLibrary/index.html | |||
| // | |||
| // This program 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. | |||
| // | |||
| // This program 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 | |||
| // this program; if not, write to the Free Software Foundation, Inc., 59 Temple | |||
| // Place, Suite 330, Boston, MA 02111-1307 USA | |||
| //============================================================================== | |||
| // Inlined methods for Networking module. See networking.hpp for notes. | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // Platform Specific | |||
| //// Windows platform | |||
| #if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64) | |||
| inline int Networking::getLastError() { // In windows you get the last error | |||
| return WSAGetLastError(); // from WSAGetLastError(); | |||
| } | |||
| inline int Networking::setNonBlocking(hSocket socket) { // Set a winsock to non-blocking | |||
| unsigned long nonblocking = 1; // Create a flag... | |||
| int result = 0; | |||
| if(0 != ioctlsocket(socket, FIONBIO, &nonblocking)) { // Set the state of the socket. | |||
| result = -1; // If that fails then return -1. | |||
| } | |||
| return result; // Show 'em my motto! | |||
| } | |||
| inline int Networking::closeSocket(hSocket socket) { // Close a socket in winsock | |||
| return closesocket(socket); // wraps closesocket(). | |||
| } | |||
| inline bool Networking::WouldBlock(int ErrorCode) { // ErrorCode matches [WSA]EWOULDBLOCK. | |||
| return (WSAEWOULDBLOCK == ErrorCode); | |||
| } | |||
| inline bool Networking::InProgress(int ErrorCode) { // ErrorCode matches [WSA]EINPROGRESS. | |||
| return( // [WSA]EALREADY also returns true. | |||
| WSAEINPROGRESS == ErrorCode || // In fact, on Win* platforms we could | |||
| WSAEALREADY == ErrorCode || // get any of these when retesting | |||
| WSAEWOULDBLOCK == ErrorCode || // open() for a connection. | |||
| WSAEINVAL == ErrorCode | |||
| ); | |||
| } | |||
| inline bool Networking::IsConnected(int ErrorCode) { // ErrorCode matches [WSA]EISCONN. | |||
| return(WSAEISCONN == ErrorCode); | |||
| } | |||
| #else | |||
| //// GNU platform | |||
| inline int Networking::getLastError() { // In GNU you get the last error | |||
| return errno; // from errno; | |||
| } | |||
| inline int Networking::setNonBlocking(hSocket socket) { // Set a socket to non-blocking | |||
| int flags, result; // Grab a place to hold the flags. | |||
| flags = fcntl(socket, F_GETFL, 0); // Get the current flags. | |||
| result = fcntl(socket, F_SETFL, flags | O_NONBLOCK); // Set the NONBLOCK flag & return. | |||
| return result; // Return the result. | |||
| } | |||
| inline int Networking::closeSocket(hSocket socket) { // Close a socket in GNU | |||
| return close(socket); // wraps close(). | |||
| } | |||
| inline bool Networking::WouldBlock(int ErrorCode) { // ErrorCode matches [WSA]EWOULDBLOCK. | |||
| return (EWOULDBLOCK == ErrorCode); | |||
| } | |||
| inline bool Networking::InProgress(int ErrorCode) { // ErrorCode matches [WSA]EINPROGRESS. | |||
| return( // [WSA]EALREADY also returns true. | |||
| EINPROGRESS == ErrorCode || | |||
| EALREADY == ErrorCode | |||
| ); | |||
| } | |||
| inline bool Networking::IsConnected(int ErrorCode) { // ErrorCode matches [WSA]EISCONN. | |||
| return(EISCONN == ErrorCode); | |||
| } | |||
| #endif | |||
| // End Platform Specific | |||
| //////////////////////////////////////////////////////////////////////////////// | |||
| // Begin Platform Agnostic | |||
| //// class IP4Address ////////////////////////////////////////////////////////// | |||
| inline IP4Address::IP4Address():IP(0){} // Blank constructor IP = 0.0.0.0 | |||
| inline IP4Address::IP4Address(const unsigned long int newIP):IP(newIP){} // Constructor given unsigned long | |||
| inline IP4Address::IP4Address(const IP4Address& newIP):IP(newIP.IP){} // Constructor given an IP4Address | |||
| inline IP4Address::IP4Address(const char* newIP) { (*this) = newIP; } // Construcing with a cstring. | |||
| inline IP4Address::IP4Address(const string& newIP) { (*this) = newIP; } // Constructing with a cppstring. | |||
| inline IP4Address& | |||
| IP4Address::operator=(const unsigned long int Right) { // Convert from unsigned long int. | |||
| IP = Right; | |||
| return *this; | |||
| } | |||
| inline IP4Address& IP4Address::operator=(const char* Right) { // Convert from c string. | |||
| IP = ntohl(inet_addr(Right)); | |||
| return *this; | |||
| } | |||
| inline IP4Address& IP4Address::operator=(const string& Right) { // Convert from cpp string. | |||
| IP = ntohl(inet_addr(Right.c_str())); | |||
| return *this; | |||
| } | |||
| inline bool IP4Address::operator<(const IP4Address Right) const { // < Comparison. | |||
| return (IP < Right.IP); | |||
| } | |||
| inline bool IP4Address::operator>(const IP4Address Right) const { // > Comparison. | |||
| return (IP > Right.IP); | |||
| } | |||
| inline bool IP4Address::operator==(const IP4Address Right) const { // == Comparison. | |||
| return (IP == Right.IP); | |||
| } | |||
| inline bool IP4Address::operator!=(const IP4Address Right) const { // != Comparison. | |||
| return (IP != Right.IP); | |||
| } | |||
| inline bool IP4Address::operator<=(const IP4Address Right) const { // <= Comparison. | |||
| return (IP <= Right.IP); | |||
| } | |||
| inline bool IP4Address::operator>=(const IP4Address Right) const { // >= Comparison. | |||
| return (IP >= Right.IP); | |||
| } | |||
| //// class SocketAddress /////////////////////////////////////////////////////// | |||
| inline void SocketAddress::clear() { | |||
| memset(&Address, 0, sizeof(Address)); // Zero out the address strcuture | |||
| Address.sin_family = AF_INET; // Internet Address Family ip4 | |||
| Address.sin_addr.s_addr = htonl(INADDR_ANY); // Any IP address | |||
| Address.sin_port = 0; // Zero means any port. | |||
| } | |||
| inline SocketAddress::SocketAddress() { // Constructor sets up w/ wildcards | |||
| clear(); // Conveniently, we can use clear() :-) | |||
| } | |||
| inline struct sockaddr_in* SocketAddress::getPtr_sockaddr_in() { // Returns a pointer to sockaddr_in. | |||
| return &Address; // Simply return it's address. | |||
| } | |||
| inline struct sockaddr* SocketAddress::getPtr_sockaddr() { // Returns a pointer to sockaddr. | |||
| return (struct sockaddr*) &Address; | |||
| } | |||
| inline socklen_t SocketAddress::getAddressSize() { | |||
| return sizeof(Address); // Return the size of the structure. | |||
| } | |||
| inline void SocketAddress::setAddress(unsigned long ipAddress) { // Set the IP address from an unsigned int | |||
| Address.sin_addr.s_addr = htonl(ipAddress); // Convert to network order and assign. | |||
| } | |||
| inline void SocketAddress::setAddress(char* ipString) { // Set the IP address from a cstring | |||
| Address.sin_addr.s_addr = inet_addr(ipString); // Convert to number and assign. | |||
| } | |||
| inline unsigned long SocketAddress::getAddress() { // Get the IP address as an unsigned int | |||
| return ntohl(Address.sin_addr.s_addr); // Convert to host order and return. | |||
| } | |||
| inline void SocketAddress::setPort(unsigned short port) { // Set the port address from an int | |||
| Address.sin_port = htons(port); // Convert to network order and set. | |||
| } | |||
| inline void SocketAddress::setPort(char* port) { // Set the port address from a cstring | |||
| setPort(atoi(port)); // Convert to int and set. | |||
| } | |||
| inline unsigned short SocketAddress::getPort() { // Get the port address as an unsigned int | |||
| return ntohs(Address.sin_port); // Convert to host order and return. | |||
| } | |||
| inline const char* SocketAddress::getPort(char* str) { // Get the port address into a cstring. | |||
| if(NULL == str) { // If the caller did not provide a | |||
| str = PortStringBuffer; // buffer to use then we will use ours. | |||
| } | |||
| sprintf(str,"%d",getPort()); // Get the port and convert to cstring. | |||
| return str; // Return the string we got. | |||
| } | |||
| //// class Socket ////////////////////////////////////////////////////////////// | |||
| inline Socket::Socket() : // When starting up we are | |||
| Handle(INVALID_SOCKET), OpenSucceeded(false) { // not yet valid. | |||
| } | |||
| inline Socket::~Socket() { // When shutting down, be sure | |||
| if(INVALID_SOCKET != Handle) { // any open socket is closed without | |||
| Network.closeSocket(Handle); // throwing any exceptions. | |||
| } | |||
| } | |||
| inline void Socket::close() { // When we close, | |||
| if(INVALID_SOCKET != Handle) { // If the handle is open then | |||
| if(Network.closeSocket(Handle)) { // close the handle and check for error. | |||
| LastError = Network.getLastError(); // If there was an error record it. | |||
| if(!Network.WouldBlock(LastError)) { // If the error was not WOULDBLOCK | |||
| throw Networking::ControlError( // then throw a ControlError exception. | |||
| Network.DescriptiveError( | |||
| "Socket::close()", LastError)); | |||
| } | |||
| } else { // If there was no error then | |||
| LastError = 0; // reset the LastError value. | |||
| } | |||
| Handle = INVALID_SOCKET; // and reset the handle to INVALID. | |||
| NonBlocking = false; // The default is Blocking. | |||
| OpenSucceeded = false; // After close, forget we opened. | |||
| } | |||
| } | |||
| inline hSocket Socket::getHandle() { // Returns the current Socket handle. | |||
| return Handle; | |||
| } | |||
| inline bool Socket::isNonBlocking() { // Returns true if socket is NonBlocking | |||
| return NonBlocking; | |||
| } | |||
| inline void Socket::makeNonBlocking() { // Sets the socket to NonBlocking mode. | |||
| if(0 > Network.setNonBlocking(Handle)) { // Feed the call through Network. | |||
| LastError = Network.getLastError(); // If it didn't work, go get the error. | |||
| NonBlocking = false; // We are NOT NonBlocking. | |||
| throw Networking::ControlError( // Throw a control error. | |||
| Network.DescriptiveError( | |||
| "Socket::makeNonBlocking()", LastError)); | |||
| } else { | |||
| NonBlocking = true; // If we didn't throw, we're ON. | |||
| } | |||
| } | |||
| inline bool Socket::isReuseAddress() { return ReuseAddress; } // True if socket is set SO_REUSEADDR. | |||
| inline bool Socket::isReuseAddress(bool set) { return (ReuseAddress = set); } // Changes SO_REUSEADDR setting. | |||
| inline bool Socket::isOpen() { // True if the socket is open. | |||
| return( | |||
| INVALID_SOCKET != Handle && // A valid handle and | |||
| true == OpenSucceeded // a successful open operation | |||
| ); // means we're open. | |||
| } | |||
| inline int Socket::getLastError() { // Returns the last error for this socket. | |||
| return LastError; | |||
| } | |||
| //// class TCPClient /////////////////////////////////////////////////////////// | |||
| inline TCPClient::TCPClient(TCPListener& L, hSocket H, SocketAddress& A) : // How to create a TCPClient. | |||
| MyListener(L) { // Capture our listener. | |||
| Handle = H; // Capture the new socket handle. | |||
| RemoteAddress = A; // Capture the client address. | |||
| ReadPointer = ReadBuffer; // Set the read position to zero. | |||
| DataLength = 0; // There is no data yet. | |||
| OpenSucceeded = true; // We're getting an open socket. | |||
| } | |||
| inline TCPClient::~TCPClient() { // When destroying a TCPClient | |||
| try{ if(isOpen()) close(); } catch(...) {} // silently close any open connections. | |||
| } | |||
| inline void TCPClient::open() { // We provide open() as unsupported. | |||
| throw Networking::NotSupportedError( // Throw an exception if this is called. | |||
| Network.DescriptiveError( | |||
| "TCPClient::open()", LastError)); | |||
| } | |||
| inline bool TCPClient::ReadBufferIsEmpty() { // True if the ReadBuffer is empty. | |||
| return (0 >= DataLength); // We can check that with DataLength. | |||
| } | |||
| inline void TCPClient::fillReadBuffer() { // Fills the buffer from the socket. | |||
| LastError = 0; // Clear the LastError value. | |||
| ReadPointer = ReadBuffer; // Reset the ReadPointer. | |||
| DataLength = recv(Handle, ReadBuffer, sizeof(ReadBuffer), MSG_NOSIGNAL); // Try to read some data. | |||
| if(0 >= DataLength) { // If there was an error then | |||
| LastError = Network.getLastError(); // Grab the last error code. | |||
| DataLength = 0; // Correct the DataLength. | |||
| if(Network.WouldBlock(LastError)) { // If the error was WouldBlock then | |||
| return; // simply return - it's ok. | |||
| } else { // If it was a different error | |||
| throw Networking::SocketReadError( // then throw a ReadError. | |||
| Network.DescriptiveError( | |||
| "TCPClient::fillReadBuffer()", LastError)); | |||
| } | |||
| } // If we succeeded then our ReadBuffer | |||
| } // assembly is in good shape. | |||
| inline bool TCPClient::isNonBlocking() { // Provided for MessagePort. | |||
| return Socket::isNonBlocking(); | |||
| } | |||
| inline unsigned long TCPClient::getRemoteIP() { // Get remote IP as long. | |||
| return RemoteAddress.getAddress(); | |||
| } | |||
| inline const char* TCPClient::getRemoteIP(char* str) { // Get IP as string. | |||
| return RemoteAddress.getAddress(str); | |||
| } | |||
| inline unsigned short TCPClient::getRemotePort() { // Get remote Port as unsigned short. | |||
| return RemoteAddress.getPort(); | |||
| } | |||
| inline const char* TCPClient::getRemotePort(char* str) { // Get Port as string. | |||
| return RemoteAddress.getPort(str); | |||
| } | |||
| //// class TCPHost ///////////////////////////////////////////////////////////// | |||
| inline TCPHost::~TCPHost() { // When destroying a TCPHost | |||
| try{ if(isOpen()) close(); } catch(...) {} // silently close any open connection. | |||
| } | |||
| inline bool TCPHost::ReadBufferIsEmpty() { // True if the ReadBuffer is empty. | |||
| return (0 >= DataLength); // We can check that with DataLength. | |||
| } | |||
| inline void TCPHost::fillReadBuffer() { // Fills the buffer from the socket. | |||
| LastError = 0; // Clear the LastError value. | |||
| ReadPointer = ReadBuffer; // Reset the ReadPointer. | |||
| DataLength = recv(Handle, ReadBuffer, sizeof(ReadBuffer), MSG_NOSIGNAL); // Try to read some data. | |||
| if(0 >= DataLength) { // If there was an error then | |||
| LastError = Network.getLastError(); // Grab the last error code. | |||
| DataLength = 0; // Correct the DataLength. | |||
| if(Network.WouldBlock(LastError)) { // If the error was WouldBlock then | |||
| return; // simply return - it's ok. | |||
| } else { // If it was a different error | |||
| throw Networking::SocketReadError( // then throw a ReadError. | |||
| Network.DescriptiveError( | |||
| "TCPHost::fillReadBuffer()", LastError)); | |||
| } | |||
| } // If we succeeded then our ReadBuffer | |||
| } // assembly is in good shape. | |||
| inline bool TCPHost::isNonBlocking() { // Provided for MessagePort. | |||
| return Socket::isNonBlocking(); | |||
| } | |||
| //// class TCPListener ///////////////////////////////////////////////////////// | |||
| inline TCPListener::~TCPListener() { // When destroying a TCPListener | |||
| try{ close(); } catch(...) {} // silently close if not already done. | |||
| } | |||