//============================================================================== | //============================================================================== | ||||
// 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. | |||||
} |