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