// 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 std::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 std::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. }