SampleSocketPort.cpp

#include "SampleSocketPort.h"

SampleSocketPort::SampleSocketPort(SocketService *pService, TCPSocket & tcpSocket) :
                                SocketPort(pService, tcpSocket) {
        tpport_t port;
        InetHostAddress ia = getPeer( & port );
        cerr << "connecting from " << ia.getHostname() << ":" << port << endl;

        // Set up non-blocking reads
        setCompletion( false );

        //1.9.3 THIS LINE DOES NOT SEEM TO BE REQUIRED ANYMORE!
        //This sorts out a bug which prevents connections after a disconnect
        //setDetectOutput(true);

        m_bOpen = true;
        m_bDoDisconnect = false;
        m_bTimedOut = false;
        m_bReceptionStarted = false;
        m_nLastBytesAvail = 0;
        m_pBuf = new char[MAX_RXBUF];
}


SampleSocketPort::~SampleSocketPort()
{
        endSocket();
        delete [] m_pBuf;
}

void SampleSocketPort::pending(void)
{
//cerr << "Pending called " << endl;
        if(!m_bOpen)
                return;

        // Read all available bytes into our buffer
        int nBytesAvail = peek(m_pBuf, MAX_RXBUF);
//cerr << "Pending .. " << nBytesAvail << endl;

        if(!m_bReceptionStarted)
        {       //Start the receive timer
                ResetReadTimeout(MAX_RXTIMEOUT);        //Got 'n' seconds to get all the data else we timeout
                m_bReceptionStarted = true;
        }
        else {
                if(m_bTimedOut) //The receive timer has expired...this is a timeout condition
                {
                        ResetReadTimeout(MAX_RXTIMEOUT); //Clear the timeout flag
                        m_nLastBytesAvail = 0;          //Reset the flags
                        m_bReceptionStarted = false;
                        OnRxTimeout();  //Do whatever 'we' do for a timeout (probably a flush or disconnect)...
                        return;
                }
        }

        if(m_nLastBytesAvail == nBytesAvail)    //Check if any more data has been received since last time
        {                                                                               //No point in parsing unless this has changed!
                //Maybe yield in here!
                //Thread::yield();
                if(nBytesAvail == 0)            //If we have been called with 0 bytes available (twice now)
                {                                                       //a disconnection has occurred
                        if(!m_bDoDisconnect) {
                                CloseSocket();  //Force the close
                        }
                }
                return;
        }

        //Depending on your application you may want to attempt to process the extra data
        //(or change your MAX_RXBUF).
        //
        //Here I just flush the whole lot, because I assume a 'legal' client wont send more than
        //we can receive....maybe someone is trying to flood / overrun us!
        if(nBytesAvail > MAX_RXBUF) {
                cerr << "TCP/IP overflow..." << endl;
                FlushRxData();
                m_nLastBytesAvail = 0;
                m_bReceptionStarted = false;
                return;
        }
        m_nLastBytesAvail = nBytesAvail;

        //In this loop you may parse the received data to determine whether a whole
        //'packet' has arrived. What you do in here depends on what data you are sending.
        //Here we will just look for a /r/n terminator sequence.
        for(int i=0; i < nBytesAvail; i++) {

/***************************SHOULD BE CUSTOMISED*******************/

                if(m_pBuf[i] == '\r') {
                        if(i+1 < nBytesAvail) {
                                if(m_pBuf[i+1] == '\n')
                                {       //Terminator sequence found

                                        /**************************************************************/
                                        // COMPULSORY ... Clear the flag and count..
                                        // do this when you have received a good packet
                                        m_nLastBytesAvail = 0;
                                        m_bReceptionStarted = false;
                                        /**************************************************************/

                                        // Now receive the data into a buffer and call our receive function
                                        int nLen = i+2;
                                        char *pszRxData = new char[nLen+1];     //Allow space for terminator
                                        receive(pszRxData, nLen);               //Receive the data
                                        pszRxData[nLen] = '\0';         //Terminate it
                                        OnDataReceived(pszRxData, nLen);
                                        delete [] pszRxData;
                                        return;
                                }
                        }
                }
/***************************END CUSTOMISATION*******************/

        }
}

void SampleSocketPort::disconnect(void)
{
        if(m_bOpen) {
                m_bDoDisconnect = true;
                CloseSocket();
        }
}

void SampleSocketPort::expired(void)
{
        if(m_bDoDisconnect && m_bOpen) {
                CloseSocket();
        }
        else if(m_bOpen && m_bReceptionStarted) {
                //Timer must have expired because the rx data has not all been received
                m_bTimedOut = true;
        }
}


bool SampleSocketPort::CloseSocket(void)
{
        if(m_bOpen && m_bDoDisconnect)
        {                                                                       //This is where the disconnection really occurs
                m_bOpen = false;                                //If m_bDoDisconnect == true we know this has been called
                OnConnectionClosed();                   //through the timer, so 'delete this' is safe!
                delete this;
        }
        else if(m_bOpen) {
                m_bDoDisconnect = true;                 //Just set the timer and the flag so we can
                setTimer(DISCONNECT_MS);                //disconnect safely, in DISCONNECT_MS
        }
        return(true);
}


ssize_t SampleSocketPort::DoSend(void *buf, size_t len)
{
        //If we are disconnecting, just pretend all the bytes were sent
        if(m_bDoDisconnect)
                return((ssize_t)len);

        ssize_t nSent = send(buf, len);
        while(!isPending(Socket::pendingOutput, 0))             //Wait for output to complete
        {
                if(m_bDoDisconnect || !m_bOpen) {
                        //If we are disconnecting, just pretend all the bytes were sent
                        return((ssize_t)len);
                }
                //I like to yield whenever waiting for things...
                //this is optional and may not suit your implementation!
                Thread::yield();
        }
        return(nSent);
}

bool SampleSocketPort::WriteData(const char *szTxData, const size_t nByteCount)
{
        //First calculate how many bytes we are to send
        ssize_t nLen = nByteCount;

        if(nLen == -1)
                nLen = (ssize_t)strlen(szTxData);

        size_t nBytesToSend = nLen;

        while(m_bOpen && nLen) {
                nLen -= DoSend((void *)&(szTxData[nBytesToSend - nLen]), nLen);
        }

//      If we are sending a terminator.....uncomment the following lines
//      char chTerminator = '\n';
//      while(DoSend((void *)&chTerminator, 1) != 1);

        return(true);
}



#define WITH_EXAMPLE

#ifdef WITH_EXAMPLE


/************ THE FOLLOWING CODE DEMONSTRATES THE USE OF THE ABOVE CLASS ********************
 ****
 ****   To test it, compile with:
 ****
 ****   g++ SampleSocketPort.cpp -lccgnu -lpthread -ldl -oSampleSocketPort -ggdb -I/usr/local/include/cc++/
 ****   Run the program.
 ****
 ****   From another terminal telnet to port 3999 of the server
 ****
 ****           'telnet localhost 3999'
 ****
 ****   Anything you type should be sent back to you in reverse!
 ****
 ****   To test the corrupt data detection, send a control code (like ^D),
 ****   if the terminating charcters are not detected within the specified time
 ****   the receive timeout will occur.
 ****
 ****/


//define the following to include the example classes and functions

int g_nOpenPorts = 0;                   //Dirty global to allow us to quit simply

class ReverserPort : public SampleSocketPort
{
public:
        ReverserPort(SocketService *pService, TCPSocket & tcpSocket) :
                        SampleSocketPort(pService, tcpSocket) {
                g_nOpenPorts++;
        }
        virtual ~ReverserPort() {
                g_nOpenPorts--;
        }
        virtual void OnConnectionClosed(void)
        { cerr << "Connection Closed!" << endl; }

        virtual void OnDataReceived(char *pszData, unsigned int nByteCount) {
                //Reverse the data and send it back

                size_t nLen = strlen(pszData);
                char *szToSend = new char[nLen+1];

                //No need to reverse the \r\n or \0
                size_t nIndex = nLen-3;

                size_t i;
                for(i=0; i < nLen - 2; i++) {
                        szToSend[i] = pszData[nIndex - i];
                }
                szToSend[i++] = '\r';
                szToSend[i++] = '\n';
                szToSend[nLen] = '\0';

                WriteData(szToSend, nLen);
                delete [] szToSend;
        }

};

class ReverserServer : public SampleSocketServiceServer
{
public:
        ReverserServer(InetHostAddress & machine, int port) :
        TCPSocket(machine, port), Thread(), SampleSocketServiceServer(machine, port) {
        }
        virtual ~ReverserServer() {
        }
        virtual SocketPort *CreateSocketPort(SocketService *pService, TCPSocket & Socket) {
                return(new ReverserPort(pService, Socket));
        }
};


int main(void)
{
        InetHostAddress LocalHost;
        LocalHost = htonl(INADDR_ANY);
        ReverserServer *Server = NULL;
        try {
                Server = new ReverserServer(LocalHost, 3999);
                Server->StartServer();
        }
        catch(...) {
                cerr << "Failed to start server" << endl;
                return(false);
        }
        cerr << "Waiting for connections...type \"quit\" to exit." << endl;

        char cmd[255];

        cin.getline(cmd, 255);


        while(strcmp(cmd, "quit") != 0) {
                cin.getline(cmd, 255);
        }

        Server->StopServer();
        delete Server;
        return 0;
}

#endif  //WITH_EXAMPLE


Generated on Sat May 16 22:57:36 2009 for GNU CommonC++ by  doxygen 1.5.8