// networking.cpp // // Copyright (C) 2004-2020 MicroNeil Research Corporation. // // This software is released under the MIT license. See LICENSE.TXT. #include "networking.hpp" 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 /////////////////////////////////////////////////// #if defined(WIN32) || defined(WIN64) //////////////////////////////////////////////////////////////////////////////// //// Being Windows specific code WSADATA WSSTartData; // Socket library data structure. // Error description handling for humans. std::string Networking::DescriptiveError(std::string Msg, int Errno) { // Form a descriptive error w/ errno. std::string s = ""; // Message string. switch(Errno) { // Assign the appropriate message. case WSA_INVALID_HANDLE: s = "WSA_INVALID_HANDLE"; break; case WSA_NOT_ENOUGH_MEMORY: s = "WSA_NOT_ENOUGH_MEMORY"; break; case WSA_INVALID_PARAMETER: s = "WSA_INVALID_PARAMETER"; break; case WSA_OPERATION_ABORTED: s = "WSA_OPERATION_ABORTED"; break; case WSA_IO_INCOMPLETE: s = "WSA_IO_INCOMPLETE"; break; case WSA_IO_PENDING: s = "WSA_IO_PENDING"; break; case WSAEINTR: s = "WSAEINTR"; break; case WSAEBADF: s = "WSAEBADF"; break; case WSAEACCES: s = "WSAEACCES"; break; case WSAEFAULT: s = "WSAEFAULT"; break; case WSAEINVAL: s = "WSAEINVAL"; break; case WSAEMFILE: s = "WSAEMFILE"; break; case WSAEWOULDBLOCK: s = "WSAEWOULDBLOCK"; break; case WSAEINPROGRESS: s = "WSAEINPROGRESS"; break; case WSAEALREADY: s = "WSAEALREADY"; break; case WSAENOTSOCK: s = "WSAENOTSOCK"; break; case WSAEDESTADDRREQ: s = "WSAEDESTADDRREQ"; break; case WSAEMSGSIZE: s = "WSAEMSGSIZE"; break; case WSAEPROTOTYPE: s = "WSAEPROTOTYPE"; break; case WSAENOPROTOOPT: s = "WSAENOPROTOOPT"; break; case WSAEPROTONOSUPPORT: s = "WSAEPROTONOSUPPORT"; break; case WSAESOCKTNOSUPPORT: s = "WSAESOCKTNOSUPPORT"; break; case WSAEOPNOTSUPP: s = "WSAEOPNOTSUPP"; break; case WSAEPFNOSUPPORT: s = "WSAEPFNOSUPPORT"; break; case WSAEAFNOSUPPORT: s = "WSAEAFNOSUPPORT"; break; case WSAEADDRINUSE: s = "WSAEADDRINUSE"; break; case WSAEADDRNOTAVAIL: s = "WSAEADDRNOTAVAIL"; break; case WSAENETDOWN: s = "WSAENETDOWN"; break; case WSAENETUNREACH: s = "WSAENETUNREACH"; break; case WSAENETRESET: s = "WSAENETRESET"; break; case WSAECONNABORTED: s = "WSAECONNABORTED"; break; case WSAECONNRESET: s = "WSAECONNRESET"; break; case WSAENOBUFS: s = "WSAENOBUFS"; break; case WSAEISCONN: s = "WSAEISCONN"; break; case WSAENOTCONN: s = "WSAENOTCONN"; break; case WSAESHUTDOWN: s = "WSAESHUTDOWN"; break; case WSAETOOMANYREFS: s = "WSAETOOMANYREFS"; break; case WSAETIMEDOUT: s = "WSAETIMEDOUT"; break; case WSAECONNREFUSED: s = "WSAECONNREFUSED"; break; case WSAELOOP: s = "WSAELOOP"; break; case WSAENAMETOOLONG: s = "WSAENAMETOOLONG"; break; case WSAEHOSTDOWN: s = "WSAEHOSTDOWN"; break; case WSAEHOSTUNREACH: s = "WSAEHOSTUNREACH"; break; case WSAENOTEMPTY: s = "WSAENOTEMPTY"; break; case WSAEPROCLIM: s = "WSAEPROCLIM"; break; case WSAEUSERS: s = "WSAEUSERS"; break; case WSAEDQUOT: s = "WSAEDQUOT"; break; case WSAESTALE: s = "WSAESTALE"; break; case WSAEREMOTE: s = "WSAEREMOTE"; break; case WSASYSNOTREADY: s = "WSASYSNOTREADY"; break; case WSAVERNOTSUPPORTED: s = "WSAVERNOTSUPPORTED"; break; case WSANOTINITIALISED: s = "WSANOTINITIALISED"; break; case WSAEDISCON: s = "WSAEDISCON"; break; case WSAENOMORE: s = "WSAENOMORE"; break; case WSAECANCELLED: s = "WSAECANCELLED"; break; case WSAEINVALIDPROCTABLE: s = "WSAEINVALIDPROCTABLE"; break; case WSAEINVALIDPROVIDER: s = "WSAEINVALIDPROVIDER"; break; case WSAEPROVIDERFAILEDINIT: s = "WSAEPROVIDERFAILEDINIT"; break; case WSASYSCALLFAILURE: s = "WSASYSCALLFAILURE"; break; case WSASERVICE_NOT_FOUND: s = "WSASERVICE_NOT_FOUND"; break; case WSATYPE_NOT_FOUND: s = "WSATYPE_NOT_FOUND"; break; case WSA_E_NO_MORE: s = "WSA_E_NO_MORE"; break; case WSA_E_CANCELLED: s = "WSA_E_CANCELLED"; break; case WSAEREFUSED: s = "WSAEREFUSED"; break; case WSAHOST_NOT_FOUND: s = "WSAHOST_NOT_FOUND"; break; case WSATRY_AGAIN: s = "WSATRY_AGAIN"; break; case WSANO_RECOVERY: s = "WSANO_RECOVERY"; break; case WSANO_DATA: s = "WSANO_DATA"; break; case WSA_QOS_RECEIVERS: s = "WSA_QOS_RECEIVERS"; break; case WSA_QOS_SENDERS: s = "WSA_QOS_SENDERS"; break; case WSA_QOS_NO_SENDERS: s = "WSA_QOS_NO_SENDERS"; break; case WSA_QOS_NO_RECEIVERS: s = "WSA_QOS_NO_RECEIVERS"; break; case WSA_QOS_REQUEST_CONFIRMED: s = "WSA_QOS_REQUEST_CONFIRMED"; break; case WSA_QOS_ADMISSION_FAILURE: s = "WSA_QOS_ADMISSION_FAILURE"; break; case WSA_QOS_POLICY_FAILURE: s = "WSA_QOS_POLICY_FAILURE"; break; case WSA_QOS_BAD_STYLE: s = "WSA_QOS_BAD_STYLE"; break; case WSA_QOS_BAD_OBJECT: s = "WSA_QOS_BAD_OBJECT"; break; case WSA_QOS_TRAFFIC_CTRL_ERROR: s = "WSA_QOS_TRAFFIC_CTRL_ERROR"; break; case WSA_QOS_GENERIC_ERROR: s = "WSA_QOS_GENERIC_ERROR"; break; case WSA_QOS_ESERVICETYPE: s = "WSA_QOS_ESERVICETYPE"; break; case WSA_QOS_EFLOWSPEC: s = "WSA_QOS_EFLOWSPEC"; break; case WSA_QOS_EPROVSPECBUF: s = "WSA_QOS_EPROVSPECBUF"; break; case WSA_QOS_EFILTERSTYLE: s = "WSA_QOS_EFILTERSTYLE"; break; case WSA_QOS_EFILTERTYPE: s = "WSA_QOS_EFILTERTYPE"; break; case WSA_QOS_EFILTERCOUNT: s = "WSA_QOS_EFILTERCOUNT"; break; case WSA_QOS_EOBJLENGTH: s = "WSA_QOS_EOBJLENGTH"; break; case WSA_QOS_EFLOWCOUNT: s = "WSA_QOS_EFLOWCOUNT"; break; case WSA_QOS_EPOLICYOBJ: s = "WSA_QOS_EPOLICYOBJ"; break; case WSA_QOS_EFLOWDESC: s = "WSA_QOS_EFLOWDESC"; break; case WSA_QOS_EPSFLOWSPEC: s = "WSA_QOS_EPSFLOWSPEC"; break; case WSA_QOS_EPSFILTERSPEC: s = "WSA_QOS_EPSFILTERSPEC"; break; case WSA_QOS_ESDMODEOBJ: s = "WSA_QOS_ESDMODEOBJ"; break; case WSA_QOS_ESHAPERATEOBJ: s = "WSA_QOS_ESHAPERATEOBJ"; break; case WSA_QOS_RESERVED_PETYPE: s = "WSA_QOS_RESERVED_PETYPE"; break; #ifdef WSA_QOS_EUNKOWNPSOBJ case WSA_QOS_EUNKOWNPSOBJ: s = "WSA_QOS_EUNKOWNPSOBJ"; break; #endif } Msg.append(" "); // Add a space to the existing message. if(0 < s.length()) { // If we know the message for Errno Msg.append(s); // then append it. } else { // If we don't know what Errno means 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()); } return Msg; }; // Networking Constructor ////////////////////////////////////////////////////// // Handles any necessary setup of Network Module resources. Networking::Networking() { // Upon initialization, if(0 != WSAStartup(MAKEWORD (2,0), &WSSTartData)) { // startup the Winsock2.0 DLL. throw InitializationError( // If that fails then throw! "Networking::Networking() if(0 != WSAStartup(MAKEWORD (2,0), &WSSTartData))" ); } } // Networking Destructor /////////////////////////////////////////////////////// // Handles any necessary cleanup of Network Module resources. Networking::~Networking() { // Upon shutdown, WSACleanup(); // shutdown the Winsock DLL. } //// Emd Windows specific code //////////////////////////////////////////////////////////////////////////////// #else //////////////////////////////////////////////////////////////////////////////// //// Begin GNU specific code // Error description handling for humans. std::string Networking::DescriptiveError(std::string Msg, int Errno) { // Form a descriptive error w/ errno. Msg.append(" "); Msg.append(strerror(Errno)); return Msg; }; // Networking Constructor ////////////////////////////////////////////////////// // Handles any necessary setup of Network Module resources. Networking::Networking() { // Upon initialization, // Nothing So Far... // nothing special required. } // Networking Destructor /////////////////////////////////////////////////////// // Handles any necessary cleanup of Network Module resources. Networking::~Networking() { // GNU sockets cleanup, // Nothing So Far... // nothing specail to required. } //// End GNU specific code //////////////////////////////////////////////////////////////////////////////// #endif //////////////////////////////////////////////////////////////////////////////// //// Platform Agnostic Stuff //// Useful Internal Bits & Pieces ///////////////////////////////////////////// const int LowestOctetMask = 0x000000FF; // The bits to look at. const int OneOctetInBits = 8; // The bits to shift. void splitIP( // Split an IP into octets. unsigned long A, // The address in host format. int& a0, // Reference to the first octet. int& a1, // Reference to the second octet. int& a2, // Reference to the third octet. int& a3 // Reference to the forth octet. ){ a3 = A & LowestOctetMask; A >>= OneOctetInBits; // Get the lowest order octet & move. a2 = A & LowestOctetMask; A >>= OneOctetInBits; // Get the next lowest octet & move. a1 = A & LowestOctetMask; A >>= OneOctetInBits; // Get the next lowest octet & move. a0 = A & LowestOctetMask; // Get the highest octet. That's IT! } //// IP4Address methods //////////////////////////////////////////////////////// IP4Address::operator unsigned long int() const { // Assign to unsigned long int. return IP; // Return it. } 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 std::string(stringbfr); // Return a string. } //// SocketAddress methods ///////////////////////////////////////////////////// // getAddress(str, len) const char* SocketAddress::getAddress(char* str) { // Get the IP address into a cstring. if(NULL == str) { // If the caller did not provide a str = IPStringBuffer; // buffer to use then we will use ours. } int a0, a1, a2, a3; // Grab a bunch of handy integers. getAddress(a0, a1, a2, a3); // Get the address as octets. sprintf(str, "%d.%d.%d.%d", a0, a1, a2, a3); // Format as dotted decimal notation. return str; // Return the output buffer. } // getAddress(int& a0, int& a1, int& a2, int& a3) void SocketAddress::getAddress(int& a0, int& a1, int& a2, int& a3) { // Get the IP address into 4 ints unsigned long A = getAddress(); // Get the address. splitIP(A, a0, a1, a2, a3); // Split it into octets. } //// TCPListener methods /////////////////////////////////////////////////////// TCPListener::TCPListener(unsigned short Port) { // Set up localhost on this Port. LocalAddress.setPort(Port); // Establish the port. LocalAddress.setAddress(LOCALHOST); // Set the address to LOCALHOST. MaxPending = DefaultMaxPending; // Use the default inbound queue size. ReuseAddress = true; // ReuseAddress on by default. OpenStage1Complete = false; // This stage of open() not yet done. OpenStage2Complete = false; // This stage of open() not yet done. // Create a socket... LastError = 0; Handle = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); // Create the socket. if(INVALID_SOCKET == Handle) { // If that operation failed then LastError = Network.getLastError(); // grab the error code and throw Networking::SocketCreationError( // throw. Network.DescriptiveError( "TCPListener::TCPListener().socket()", LastError)); } } TCPListener::TCPListener(SocketAddress& WhereToBind) { // Set up specific "name" for listening. LocalAddress = WhereToBind; // Make my Local address as provided. MaxPending = DefaultMaxPending; // Use the default inbound queue size. ReuseAddress = true; // ReuseAddress on by default. OpenStage1Complete = false; // This stage of open() not yet done. OpenStage2Complete = false; // This stage of open() not yet done. // Create a socket... LastError = 0; Handle = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); // Create the socket. if(INVALID_SOCKET == Handle) { // If that operation failed then LastError = Network.getLastError(); // grab the error code and throw Networking::SocketCreationError( // throw. Network.DescriptiveError( "TCPListener::TCPListener().socket()", LastError)); } } // open() void TCPListener::open() { // Open when ready. if(OpenSucceeded) return; // If open already, we're done. LastError = 0; // Clear the last error. bool SuccessFlag = true; // Start optimistically. // Set SO_REUSEADDR if turned on if(!OpenStage1Complete) { // Do this stage only once. int ReuseAddress_Flag = (ReuseAddress? 1:0); // Setup an appropriate integer flag. int result = // Set SO_REUSEADDR before bind(). setsockopt( Handle, SOL_SOCKET, SO_REUSEADDR, (char*) &ReuseAddress_Flag, sizeof(ReuseAddress_Flag)); 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( "TCPListener::open().setsockopt(SO_REUSEADDR)", LastError)); } OpenStage1Complete = true; // Stage 1 complete now. } // End of open() stage 1 // Next we bind it... if(!OpenStage2Complete) { // Do this stage only once. int result = // Bind our socket to the LocalAddress. bind( Handle, LocalAddress.getPtr_sockaddr(), LocalAddress.getAddressSize()); 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::SocketBindError( // throw. Network.DescriptiveError( "TCPListener::open().bind()", LastError)); } OpenStage2Complete = true; // Stage 2 complete now. } // End of open() stage 2 // Then we put it in a listening state... int result = listen(Handle, MaxPending); // Listen for up to MaxPending at once. if(0 > result) { // If an error occurred then SuccessFlag = false; // we did not succeed. LastError = Network.getLastError(); // Capture the error information and throw Networking::SocketListenError( // throw. Network.DescriptiveError( "TCPListener::open().listen()", LastError)); } OpenSucceeded = SuccessFlag; // So, did we succeed? } // acceptClient() TCPClient* TCPListener::acceptClient() { // Accept a client connection. LastError = 0; // Clear the last error. socklen_t rsize = RemoteAddress.getAddressSize(); // Size as an int for accept(). hSocket NewHandle = // Accept a new connection if available. accept( Handle, // use our handle, of course,... RemoteAddress.getPtr_sockaddr(), // and store the remote hosts &rsize); // address for us. if(INVALID_SOCKET == NewHandle) { // If there was an error then LastError = Network.getLastError(); // capture the error value. if(!Network.WouldBlock(LastError)) { // If it's not a EWOULDBLOCK error throw Networking::SocketAcceptError( // then we need to throw. Network.DescriptiveError( "TCPListener::acceptClient().accept()", LastError)); } else { // EWOULDBLOCK errors are normal in 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)); } } // If things have gone well we can do what we came for. return new TCPClient(*this, NewHandle, RemoteAddress); // Create the new TCPClient object. } //// TCPClient methods ///////////////////////////////////////////////////////// int TCPClient::transmit(const char* bfr, int size) { // How to send a buffer of data. if(0 == size) return 0; // Nothing to send, send nothing. if(0 == bfr) // Watch out for null buffers. throw Networking::SocketWriteError("TCPClient::transmit() NULL Bfr!"); 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. 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. 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). } else { // If this was a different kind of error throw Networking::SocketWriteError( // then throw! Network.DescriptiveError( "TCPClient::transmit().send()", LastError)); } } return ByteCount; // Usually: return the sent byte count. } int TCPClient::receive(char* bfr, int size) { // How to receive a buffer of data. if(ReadBufferIsEmpty()) { // If the read buffer is empty then fillReadBuffer(); // fill it first. } // Optimize our transfer to the smaller if(DataLength < size) { // of what we have or the size of the size = DataLength; // provided buffer. This way we ony check } // one value in our copy loop ;-) int RemainingDataLength = size; // Capture the length of data to xfer. while(0 < RemainingDataLength) { // While we have work to do *bfr = *ReadPointer; // copy each byte from our ReadBuffer, bfr++; ReadPointer++; // move the pointers to the next byte, DataLength--; // update our ReadBuffers's DataLength, RemainingDataLength--; // and count down the bytes left to xfer. } return size; // When done, say how much we moved. } int TCPClient::delimited_receive(char* bfr, int size, char delimiter) { // How to receive delimited data. if(ReadBufferIsEmpty()) { // If the read buffer is empty then fillReadBuffer(); // fill it first. } // Optimize our transfer to the smaller if(DataLength < size) { // of what we have or the size of the size = DataLength; // provided buffer. This way we ony check } // one value in our copy loop ;-) int Count = 0; // Keep our byte count in scope. bool DelimiterNotReached = true; // Watching for our deliimiter. while((Count < size) && DelimiterNotReached) { // While there is work to do... *bfr = *ReadPointer; // copy each byte from our ReadBuffer, DelimiterNotReached = (delimiter != (*bfr)); // check for the delimiter character, bfr++; ReadPointer++; // move the pointers to the next byte, DataLength--; // update our ReadBuffers's DataLength, Count++; // and count up the bytes we have moved. } return Count; // When done, say how much we moved. } //// TCPHost methods /////////////////////////////////////////////////////////// // Constructors... TCPHost::TCPHost(unsigned short Port) { // Will connect to localhost on Port. RemoteAddress.setPort(Port); // Connect to Port on RemoteAddress.setAddress(LOCALHOST); // Localhost. ReadPointer = ReadBuffer; // Set the read position to zero. DataLength = 0; // There is no data yet. ReuseAddress = false; // ReuseAddress off by default. OpenStage1Complete = false; // Stage 1 of open() not done yet. // Create a socket to use. LastError = 0; // Clear our last error value. Handle = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); // Create the socket. if(0 > Handle) { // If that operation failed then LastError = Network.getLastError(); // grab the error code and throw Networking::SocketCreationError( // throw. Network.DescriptiveError( "TCPHost::TCPHost().socket()", LastError)); } } TCPHost::TCPHost(SocketAddress& Remote) { // Will connect to Remote address/port. RemoteAddress = Remote; // Capture the provided address. ReadPointer = ReadBuffer; // Set the read position to zero. DataLength = 0; // There is no data yet. ReuseAddress = false; // ReuseAddress off by default. OpenStage1Complete = false; // Stage 1 of open() not done yet. // Create a socket to use. LastError = 0; // Clear our last error value. Handle = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); // Create the socket. if(0 > Handle) { // If that operation failed then LastError = Network.getLastError(); // grab the error code and throw Networking::SocketCreationError( // throw. Network.DescriptiveError( "TCPHost::TCPHost().socket()", LastError)); } } // Methods... void TCPHost::open() { // We provide open(). if(OpenSucceeded) return; // If open already, we're done. LastError = 0; // Clear our LastError value. bool SuccessFlag = true; // Begin optimistically. // Set Socket Options if(!OpenStage1Complete) { // If we haven't done this yet: // 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( Handle, SOL_SOCKET, SO_REUSEADDR, (char*) &ReuseAddress_Flag, sizeof(ReuseAddress_Flag)); 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_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)); } } OpenStage1Complete = true; // Skip this section from now on. } // Done with stage 1. // Connect the socekt to the Host. int result = // Connect to the remote host connect( // using the socket we just Handle, // stored in Handle and RemoteAddress.getPtr_sockaddr(), // the Remote address. RemoteAddress.getAddressSize()); if(0 > result) { // If there was an error then SuccessFlag = false; // we did not succeed. LastError = Network.getLastError(); // Record the error data. if(Network.IsConnected(LastError)) { // If we actually did succeed then SuccessFlag = true; // say so. (Silly Winsock!) } else // But if that's not the case check if( // to see if something bad happened - !Network.WouldBlock(LastError) && // not just would-block, or !Network.InProgress(LastError) // in progress... ) { // If it was something other than throw Networking::SocketConnectError( // WouldBlock or InProgress then Network.DescriptiveError( // throw! "TCPHost::open().connect()", LastError)); } // If it was WouldBlock then it's } // considered to be ok. OpenSucceeded = SuccessFlag; // So, are we open now? } int TCPHost::transmit(const char* bfr, int size) { // How to send a buffer of data. LastError = 0; // No errors yet. if(0 == size) return 0; // Nothing to send, send nothing. if(0 == bfr) // Watch out for null buffers. throw Networking::SocketWriteError("TCPHost::transmit() NULL Bfr!"); if(0 > size) // Watch out for bad sizes. throw Networking::SocketWriteError("TCPHost::transmit() 0 > size!"); int ByteCount = send(Handle, bfr, size, MSG_NOSIGNAL); // Try to send and capture the count. if(0 > ByteCount) ByteCount = 0; // Mask error results as 0 bytes sent. if(size > ByteCount) { // If we didn't send it all check it out. LastError = Network.getLastError(); // Grab the error code. if(Network.WouldBlock(LastError)) { // If the error was WouldBlock then return ByteCount; // it was a partial snd - return. } else { // If this was a different kind of error throw Networking::SocketWriteError( // then throw! Network.DescriptiveError( "TCPHost::transmit().send()", LastError)); } } return ByteCount; // Ultimately return the byte count. } int TCPHost::receive(char* bfr, int size) { // How to receive a buffer of data. if(ReadBufferIsEmpty()) { // If the read buffer is empty then fillReadBuffer(); // fill it first. } // Optimize our transfer to the smaller if(DataLength < size) { // of what we have or the size of the size = DataLength; // provided buffer. This way we ony check } // one value in our copy loop ;-) int RemainingDataLength = size; // Capture the length of data to xfer. while(0 < RemainingDataLength) { // While we have work to do *bfr = *ReadPointer; // copy each byte from our ReadBuffer, bfr++; ReadPointer++; // move the pointers to the next byte, DataLength--; // update our ReadBuffers's DataLength, RemainingDataLength--; // and count down the bytes left to xfer. } return size; // When done, say how much we moved. } int TCPHost::delimited_receive(char* bfr, int size, char delimiter) { // How to receive delimited data. if(ReadBufferIsEmpty()) { // If the read buffer is empty then fillReadBuffer(); // fill it first. } // Optimize our transfer to the smaller if(DataLength < size) { // of what we have or the size of the size = DataLength; // provided buffer. This way we ony check } // one value in our copy loop ;-) int Count = 0; // Keep our byte count in scope. bool DelimiterNotReached = true; // Watching for our deliimiter. while((Count < size) && DelimiterNotReached) { // While there is work to do... *bfr = *ReadPointer; // copy each byte from our ReadBuffer, DelimiterNotReached = (delimiter != (*bfr)); // check for the delimiter character, bfr++; ReadPointer++; // move the pointers to the next byte, DataLength--; // update our ReadBuffers's DataLength, Count++; // and count up the bytes we have moved. } return Count; // When done, say how much we moved. } // End Platform Agnostic Stuff //////////////////////////////////////////////////////////////////////////////// } // End namespace codedweller