| //============================================================================== | //============================================================================== | ||||
| // See networking.hpp for notes. | // See networking.hpp for notes. | ||||
| // See networking.inline.hpp for inlined methods & functions. | |||||
| #include "networking.hpp" | #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 /////////////////////////////////////////////////// | //// Platform Specific Stuff /////////////////////////////////////////////////// | ||||
| Msg.append(s); // then append it. | Msg.append(s); // then append it. | ||||
| } | } | ||||
| else { // If we don't know what Errno means | 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. | ErrNoMsg << " UNKNOWN ErrorNumber = " << Errno; // well so someone can figure it out. | ||||
| Msg.append(ErrNoMsg.str()); | Msg.append(ErrNoMsg.str()); | ||||
| } | } | ||||
| // Error description handling for humans. | // 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)); | Msg.append(" "); Msg.append(strerror(Errno)); | ||||
| return Msg; | return Msg; | ||||
| }; | }; | ||||
| return IP; // Return it. | 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. | char stringbfr[IPStringBufferSize]; // Grab a temporary buffer. | ||||
| memset(stringbfr, 0, sizeof(stringbfr)); // Null out it's space. | memset(stringbfr, 0, sizeof(stringbfr)); // Null out it's space. | ||||
| int a0, a1, a2, a3; // Grab some integers. | int a0, a1, a2, a3; // Grab some integers. | ||||
| splitIP(IP, a0, a1, a2, a3); // Split the IP in the IP4Address. | 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. | 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 ///////////////////////////////////////////////////// | //// SocketAddress methods ///////////////////////////////////////////////////// | ||||
| if(!OpenStage2Complete) { // Do this stage only once. | if(!OpenStage2Complete) { // Do this stage only once. | ||||
| int result = // Bind our socket to the LocalAddress. | int result = // Bind our socket to the LocalAddress. | ||||
| ::bind( | |||||
| bind( | |||||
| Handle, | Handle, | ||||
| LocalAddress.getPtr_sockaddr(), | LocalAddress.getPtr_sockaddr(), | ||||
| LocalAddress.getAddressSize()); | LocalAddress.getAddressSize()); | ||||
| return NULL; // non blocking mode so we return | return NULL; // non blocking mode so we return | ||||
| } // NULL when we see them. | } // 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. | // If things have gone well we can do what we came for. | ||||
| if(0 > size) // Watch out for bad sizes. | if(0 > size) // Watch out for bad sizes. | ||||
| throw Networking::SocketWriteError("TCPClient::transmit() 0 > size!"); | 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. | 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(AnErrorOccurred) { // If there was an error check it out. | ||||
| if(Network.WouldBlock(LastError)) { // If the error was "Would Block" then | if(Network.WouldBlock(LastError)) { // If the error was "Would Block" then | ||||
| return NoBytesSent; // return no bytes sent (try again). | return NoBytesSent; // return no bytes sent (try again). | ||||
| "TCPClient::transmit().send()", LastError)); | "TCPClient::transmit().send()", LastError)); | ||||
| } | } | ||||
| } | } | ||||
| return ByteCount; // Usually: return the sent byte count. | return ByteCount; // Usually: return the sent byte count. | ||||
| } | } | ||||
| LastError = 0; // Clear our LastError value. | LastError = 0; // Clear our LastError value. | ||||
| bool SuccessFlag = true; // Begin optimistically. | bool SuccessFlag = true; // Begin optimistically. | ||||
| // Set Socket Options | |||||
| // Set Socket Options | |||||
| if(!OpenStage1Complete) { // If we haven't done this yet: | 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 ReuseAddress_Flag = (ReuseAddress? 1:0); // Setup an appropriate integer flag. | ||||
| int result = // Set SO_REUSEADDR before bind(). | int result = // Set SO_REUSEADDR before bind(). | ||||
| setsockopt( | setsockopt( | ||||
| Network.DescriptiveError( | Network.DescriptiveError( | ||||
| "TCPHost::open().setsockopt(SO_REUSEADDR)", LastError)); | "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. | OpenStage1Complete = true; // Skip this section from now on. | ||||
| } // Done with stage 1. | } // Done with stage 1. | ||||
| // End Platform Agnostic Stuff | // End Platform Agnostic Stuff | ||||
| //////////////////////////////////////////////////////////////////////////////// | //////////////////////////////////////////////////////////////////////////////// | ||||
| } // End namespace codedweller |
| // The networking module abstracts network communications and provides a set | // The networking module abstracts network communications and provides a set | ||||
| // of objects for handling most tasks. | // 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 <stdexcept> | ||||
| #include <iostream> | #include <iostream> | ||||
| #include <sstream> | #include <sstream> | ||||
| #include <cstring> | #include <cstring> | ||||
| using namespace std; | |||||
| #include <cstdlib> | #include <cstdlib> | ||||
| #include <cstdio> | #include <cstdio> | ||||
| #include <cerrno> | #include <cerrno> | ||||
| //// Windows headers... | //// Windows headers... | ||||
| #include <winsock2.h> | #include <winsock2.h> | ||||
| namespace codedweller { | |||||
| typedef int socklen_t; // Posix uses socklen_t so we mimic it. | typedef int socklen_t; // Posix uses socklen_t so we mimic it. | ||||
| typedef SOCKET hSocket; // Winx handles Socket is opaque. | typedef SOCKET hSocket; // Winx handles Socket is opaque. | ||||
| } // End namespace codedweller | |||||
| #else | #else | ||||
| //// GNU Headers... | //// GNU Headers... | ||||
| #include <unistd.h> | #include <unistd.h> | ||||
| #include <fcntl.h> | #include <fcntl.h> | ||||
| namespace codedweller { | |||||
| typedef int hSocket; // *nix uses int to handle a Socket. | typedef int hSocket; // *nix uses int to handle a Socket. | ||||
| const hSocket INVALID_SOCKET = -1; // -1 is the invalid 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 | #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 ////////////////////////////////////////////////////// | //// Tuning and Constants ////////////////////////////////////////////////////// | ||||
| const unsigned long LOCALHOST = 0x7F000001; // 127.0.0.1 as an integer. | const unsigned long LOCALHOST = 0x7F000001; // 127.0.0.1 as an integer. | ||||
| IP4Address(const IP4Address&); // Constructor given an IP4Address | IP4Address(const IP4Address&); // Constructor given an IP4Address | ||||
| IP4Address(const char* newIP); // Construcing with a cstring. | 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 unsigned long int Right); // Convert from unsigned long int. | ||||
| IP4Address& operator=(const char* Right); // Convert from c string. | 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 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. | ||||
| bool operator>(const IP4Address Right) const; // > Comparison. | bool operator>(const IP4Address Right) const; // > Comparison. | ||||
| public: | 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(); | ||||
| ~Networking(); | ~Networking(); | ||||
| // End of UDPBroadcaster class | // End of UDPBroadcaster class | ||||
| //////////////////////////////////////////////////////////////////////////////// | //////////////////////////////////////////////////////////////////////////////// | ||||
| //// Include Inline methods and functions... | |||||
| #include "networking.inline.hpp" | |||||
| #endif | |||||
| // End include Networking.hpp only once... | |||||
| } // End namespace codedweller |
| // 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. | |||||
| } |