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