Click here to Skip to main content
15,885,278 members
Articles / Desktop Programming / MFC

CSPServer, State-based Protocol Server Class

Rate me:
Please Sign up or sign in to vote.
4.88/5 (14 votes)
11 Mar 20038 min read 145K   1.4K   71  
Class framework for creating client/server protocol servers
// File: SampleServer.cpp

#include <afx.h>
#include <conio.h>

#include "spserver.h"
#pragma comment(lib,"wsock32.lib")

#define assert ASSERT
#ifdef _DEBUG
#define new DEBUG_NEW
#endif

//----------------------------------------------------------------
// CSPServer Subclass

class CMySPServer: public CSPServer {
	typedef CSPServer inherited;
public:
	CMySPServer(CSocketIO *s);
protected:

// Required virtual overrides

	virtual void Go();

// Other virtual overrides

	virtual void SendWelcome();
	virtual void Cleanup();
    virtual void SendCommandError(const char *cmdline);

    //virtual void SessionTrace(const char *szTag, const char *s);
    //virtual BOOL CheckAbort();
    //virtual BOOL PreprocessLine(char *s);
    //virtual BOOL QueryAllowTimeoutDisconnect();
    //virtual void OnWaitReceivedDataAbandoned();
    //virtual void OnReceiveByteError(const int error);

private:
	static TSPDispatch Dispatch[];
	BOOL SPD_HELLO(char *args);
	BOOL SPD_LOGIN(char *args);
	BOOL SPD_SHOW(char *args);
	BOOL SPD_HELP(char *args);
	BOOL SPD_QUIT(char *args);

    CString sClientDomain;
    CString sClientIp;

};

//----------------------------------------------------------------
// Dispatch Structure

CSPServer::TSPDispatch CMySPServer::Dispatch[] = {
	SPCMD(CMySPServer, "HELLO", SPD_HELLO),
	SPCMD(CMySPServer, "LOGIN", SPD_LOGIN),
	SPCMD(CMySPServer, "SHOW",  SPD_SHOW),
	SPCMD(CMySPServer, "HELP",  SPD_HELP),
	SPCMD(CMySPServer, "QUIT",  SPD_QUIT),
	{0}
};

//----------------------------------------------------------------
// Constructor

CMySPServer::CMySPServer(CSocketIO *s)
 : CSPServer(s, Dispatch)
{
	Done = FALSE;

    // Get client information (i.e., for logging/filtering)
    Control->GetPeerInfo(sClientDomain,sClientIp);

	// Start() starts the thread and call Go()
	Start();
}

void CMySPServer::SendCommandError(const char *cmdline)
{
    Send("!command not understood: %s\r\n",cmdline);
}

BOOL CMySPServer::SPD_HELLO(char *args)
{
    Send("--> HELLO(%s)\r\n",args);
	return TRUE;
}

BOOL CMySPServer::SPD_LOGIN(char *args)
{
    Send("--> LOGIN(%s)\r\n",args);
	return TRUE;
}

BOOL CMySPServer::SPD_SHOW(char *args)
{
    Send("--> SHOW(%s)\r\n",args);
	return TRUE;
}

BOOL CMySPServer::SPD_QUIT(char *)
{
	Send("<click> Bye!\r\n");
	Control->Shutdown(1);
	Done       = TRUE;
	return TRUE;
}

BOOL CMySPServer::SPD_HELP(char *args)
{
	Send("+OK available commands.\r\n");
	Send("HELLO\r\n");
	Send("LOGIN\r\n");
	Send("SHOW\r\n");
	Send("HELP\r\n");
	Send("QUIT\r\n");
	Send(".\r\n");
	return TRUE;
}


//----------------------------------------------------------------
// Go() is called by thread handler

void CMySPServer::Go()
{
	// By this point, we have a new thread running

    printf("tid: %04x Starting Client: %s (%s)\n",GetThreadId(),sClientIp,sClientDomain);

	// Go() starts the thread's socket command line
	// reader and dispatcher.  Go() returns when the
	// variable Done is set TRUE or if connection drops.

	inherited::Go();

    printf("tid: %04x Exiting Client: %s (%s)\n",GetThreadId(),sClientIp,sClientDomain);
	// we are done,

	delete this;
}

//----------------------------------------------------------------
// SendWelcome() is called by inherited::Go()

void CMySPServer::SendWelcome()
{
	Send("Hello! Server ready\r\n");
}

//----------------------------------------------------------------
// Cleanup() is called when the socket has been closed (BY CLIENT)

void CMySPServer::Cleanup()
{
    printf("tid: %04x Cleanup Client: %s (%s)\n",GetThreadId(),sClientIp,sClientDomain);
}

//----------------------------------------------------------------
// Socket Listening Thread

class CServerThread : public CThread {
	typedef CThread inherited;
public:
    CServerThread(const DWORD port = 4044, const DWORD flags = 0);
    virtual void Stop();
protected:
    virtual void Go();
private:
    SOCKET serverSock;
    DWORD  serverPort;
};

CServerThread::CServerThread(const DWORD port, const DWORD flags)
{
    serverSock = INVALID_SOCKET;
    serverPort = port;
    SetStartFlags(flags);
    Start();
}

void CServerThread::Go()
{
    printf("* Opening Socket on port: %d\n",serverPort);

    serverSock = socket(PF_INET, SOCK_STREAM, 0);

    sockaddr_in sin;
    sin.sin_family      = AF_INET;
    sin.sin_port        = htons((short)serverPort);
    sin.sin_addr.s_addr = INADDR_ANY;

    if (bind(serverSock, (sockaddr *)&sin, sizeof(sin)) != 0) {
        printf("bind error %d\n",GetLastError());
        return;
    }

    if (listen(serverSock, SOMAXCONN) != 0) {
        printf("listen error %d\n",GetLastError());
        return;
    }

    printf("* Waiting for connections\n");

    while ((serverSock != INVALID_SOCKET) && !IsTerminated()) {
        sockaddr_in src;
        int x = sizeof(src);

        //---------------------------------------------------
        // Wait for connection:
        //
        // Note: Since accept will block the socket handle is
        // used as Stop() variable.  WinSock will return if
        // the socket is set to INVALID_SOCKET. See Stop()

        SOCKET t = accept(serverSock, (sockaddr *)&src, &x);
        if (serverSock == INVALID_SOCKET) break;

        //---------------------------------------------------

        if (t == INVALID_SOCKET) continue;

        printf("- New Connection!\n");
        new CMySPServer(new CSocketIO(t));

    } // while

    printf("* Exiting Server Thread\n");
}

void CServerThread::Stop()
{
    SOCKET s = InterlockedExchange((long *)&serverSock, INVALID_SOCKET);
    if (s != INVALID_SOCKET) closesocket(s);

    // Note: The thread destructor will also call Stop()
    //       So this inherited call is redundant
    inherited::Stop();
}

//----------------------------------------------------------------
// Main Block
//----------------------------------------------------------------

void main(char argc, char *argv[])
{

   WSADATA wd;
   if (WSAStartup(MAKEWORD(1, 1), &wd) != 0) {
      return;
   }

   CServerThread server(4044);

   printf("* Main Thread Keyboard Wait: Escape to Exit\n");

   while (TRUE) {
       if (kbhit() && getch() == 27) break;
       Sleep(30);
   }

   printf("* Stopping Server\n");

   server.Stop();

   printf("* Done. Exiting Main\n");

}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions