Click here to Skip to main content
Click here to Skip to main content

EpServerEngine - A lightweight Template Server-Client Framework using C++ and Windows Winsock

By , 22 Aug 2013
Rate this:
Please Sign up or sign in to vote.

Table of contents

  1. Introduction 
  2. Background
    1. Optional
  3. Purpose of the Template Server-Client Framework 
  4. Class Diagram 
  5. TCP Classes
    1. Server side
    2. Client side
    3. General
  6. UDP Classes
  7. Conclusion
  8. Reference 

Introduction

Sometimes it is very frustrating to write a server/client applications. For some of us, they might be tired of writing same code over and over with the bunch of initialization, and for the others, initialization code for the server/client (Winsock) itself might be too complicated. There was a time for me to write a lot of server/client applications for small network, and I have decided to build simple template server-client framework, and decided to share with everyone. Honestly say, I would not recommend this for the case where the performance is a really big issue (I have not done any performance test nor compared with other implementations), but rather use this for building quick prototypes or testing the packet communication. Moreover, take this as guideline for building your own template server-client framework. 

** Warning: This framework is not free from the 10K problem, since it is creating one thread per connection. (There are many frameworks out there which use IOCP to avoid the 10K problem.) This may be sufficient for small network where performance is not very important( which was most of my case).  

Background  

For detailed information related to "Winsock", please refer to MSDN by Microsoft. Microsoft provides easy guides on how to create a client and a server using Winsock library. I highly recommend you to follow this tutorial using Microsoft Visual Studio.

Optional   

  1. Utility Library used in EpServerEngine
  2. The Lock framework used in EpServerEngine 
  3. Object Management used in EpServerEngine 

Purpose of the Template Server-Client Framework

As a software developer, many people will agree that writing same code over and over is very lame job to do. It is even worse when if the code is just bunch of nasty initialization. This template server-client framework do all those jobs for you, and you only have to design and implement your packet structure and packet processing routine for the server and client, and leave the rest of the jobs to the framework (initialization/packet waiting/creating the worker threads/mutual exclusion for the network, etc.).

Class Diagram

TCP Classes

Server side  

BaseServer class    

This is a base server class which is responsible for the following:

  • Starts the server 
  • Stops the server  
  • Sets up the port to use   
  • Broadcast the messages to all clients, connected.  
  • Disconnects all the client, connected (for emergency use)   
Including above functionality, internally it also creates a listening thread, which accepts the clients, and creates a worker thread for each client connected, to communicate with the client that is connected.
What do I have to do to create my own server?   

This is very simple.

  1. Create your Server class (ex. MyServer)  
  2. Make your class as a sub-class of this BaseServer   
  3. class MyServer: public BaseServer   
  4.  Implement "createNewWorker" function (which is pure virtual function in "BaseServer" class) which returns your Server Worker class that you implemented. (Please see the "BaseServerWorker" class section for more detail) 
  5. // MyServer.h
    class MyServer:public BaseServer
    {
    ...
    virtual BaseServerWorker* createNewWorker();
    ...
    };
    // MyServer.cpp
    #include "MyServer.h"
    #include "MyServerWorker.h"
    ...
    BaseServerWorker* MyServer::createNewWorker()
    {
    return new MyServerWorker();
    }
    ...  
  6. Optional: If more functionality is needed for your server, implement in your Server class (ex. MyServer). 
  7. Done!   
How to start/stop the server  

You can very easily start/stop your server as below:

#include "MyServer.h"
... 
MyServer m_server=MyServer(_T("1020")); // 1020 is the port number 
if(m_server.StartServer()) 
{ 
   // server started 
}   
...  
if(m_server.IsServerStarted()) // check if server is started 
{  
   m_server.StopServer();  // Disconnect all clients connected and stop listening 
}  
...       

For more detail, please refer to the EpServerEngine's Wiki page, here.

BaseServerWorker class    

This is a base server worker class which is responsible for the following:

  • Start a new thread to communicate with a client. 
  • Send/Receive the messages to/from a client  
Including above functionality, internally it also creates a worker thread for each packet, received, to parse the packet and act upon to the packet, received.
What do I have to do to create my own worker?  

This is very simple job as creating the server above.

  1. Create your Server Worker class (ex. MyServerWorker)  
  2. Make your class as a sub-class of this BaseServerWorker  
  3. class MyServerWorker: public BaseServerWorker 
  4.  Implement "createNewPacketParser" function (which is pure virtual function in "BaseServerWorker" class) which returns your Packet Parser class that you implemented. (Please see the "BasePacketParser" class section for more detail)     
  5. // MyServerWorker.h
    class MyServerWorker:public BaseServerWorker
    {
       ...
       virtual BasePacketParser* createNewPacketParser();
       ...
    };
    // MyServerWorker.cpp
    #include "MyServerWorker.h"
    #include "MyPacketParser.h"
    ...
    BasePacketParser* MyServerWorker::createNewPacketParser()
    {
       return new MyPacketParser();
    }
    ...    
  6. Optional: If more functionality is needed for your server worker, implement in your Server Worker class (ex. MyServerWorker). 
  7. Done!   
How to use this server worker 

Basically, none!!!

You simply don't have to do anything!!!!

BaseServer will automatically create your Server Worker (e.g., MyServerWorker), when it is needed (ex. when a client connected to the server), and will create/destroy your PacketParser according to its needs by calling createNewPacketParser function.

For more detail, please refer to the EpServerEngine's Wiki page, here.

Client side   

BaseClient class  

This is a base client class which is responsible for the following:

  • Connect to a server
  • Disconnect from a server 
  • Send/Receive the messages to/from a server. 
Including above functionality, internally it also creates a worker thread for each packet, received, to parse the packet and act upon to the packet, received. 
What do I have to do to create my own client?   
  1. Create your Client class (e.g., MyClient
  2. Make your class as a sub-class of this BaseClient   
  3. class MyClient: public BaseClient 
  4.  Implement the createNewPacketParser function (which is a pure virtual function in the BaseClient class) which returns your Packet Parser class that you implemented. (Please see the BasePacketParser class section for more detail.)          
  5. // MyClient.h
    class MyClient:public BaseClient
    {
       ...
       virtual BasePacketParser* createNewPacketParser();
       ...
    };
    // MyClient.cpp
    #include "MyClient.h"
    #include "MyPacketParser.h"
    ...
    BasePacketParser* MyClient::createNewPacketParser()
    {
       return new MyPacketParser();
    }
    ... 
  6. Optional: If more functionality is needed for your client, implement in your Client class (e.g., MyClient). 
  7. Done!   
How to connect/disconnect to/from the server  

You can very easily connect/disconnect to/from your server as below:

#include "MyClient.h"
...
MyClient m_client=MyClient(_T("11.222.33.44"),_T("1020")); // 11.222.33.44 is the hostname
// for your server 1020 is the
// port number
if(m_client.Connect())
{
   // connected to the server
}
...
if(m_client.IsConnected()) // check if server is started
{
   m_client.Disconnect(); // Disconnect from the server
}
...   

For more detail, please refer to the EpServerEngine's Wiki page, here.

General  

BasePacketParser class  

This is a base packet parser class which is responsible for the following:

  • Start a new thread to parse a packet, received 
  • Send the messages to the client, which sent the packet. 
Including above functionality, internally it also creates a worker thread to parse the packet and act upon to the packet, received.
What do I have to do to create my own packet parser?   
  1. Create your Packet Parser class (e.g., MyPacketParser)   
  2. Make your class as a sub-class of this BasePacketParser 
  3. class MyPacketParser: public BasePacketParser 
  4.  Implement the ParsePacket function (which is a pure virtual function in the BasePacketParser class).   
  5. // MyPacketParser.h
    class MyPacketParser:public BasePacketParser
    {
       ...
       virtual void ParsePacket(const Packet &packet );
       ...
    };
    // MyPacketParser.cpp
    #include "MyPacketParser.h"
    ...
    void MyPacketParser::ParsePacket(const Packet &packet )
    {
       // your parsing routine
    }
    ... 
  6. Optional: If more functionality is needed for your packet parser, implement in your Packet Parser class (ex. MyPacketParser).  
  7. Done!   
How to use this packet parser 

Basically, none!!!

You simply don't have to do anything!!!!

BaseServerWorker or BaseClient will automatically create your Packet Parser (e.g.,. MyPacketParser), when it is needed (ex. when a client/server worker receives a packet).

For more detail, please refer to the EpServerEngine's Wiki page, here.

Packet class  

 This is a packet class which is responsible for the following:

  • Basic unit of data to communicate between the server and the client for EpServerEngine. 
How to use this packet class?    
  • Packet Type Declaration  
    typedef enum _sendPacketType{
       SEND_PACKET_TYPE_DOSOMETHING=0,
       SEND_PACKET_TYPE_DOSOMETHING2,
       SEND_PACKET_TYPE_DOSOMETHING3,
    }SendPacketType;
     
    struct SendPacket{
       SendPacketType packetType;
        unsigned int magicNum;
    };     

    For example, if you define the packet structure as above, you can create Packet object as below to send it to the server/client.

    SendPacket sendPacket;
    sendPacket.packetType=SEND_PACKET_TYPE_DOSOMTHING2;
    sendPacket.magicNum = 1;
    Packet packet(&sendPacket               // pointer to the data
                  , sizeof(SendPacket),     // byte size of the data
                  , true                    // flag whether to allocate the memory
                                            //   for the data within Packet Object or not.
                 ); 
  • Note that since copying memory for packet is critical on the performance in Server development, you can choose whether to allocate the memory for the Packet object, or just hold the pointer to the data within the Packet object according to the situation.  

For more detail, please refer to the EpServerEngine's Wiki page, here

PacketContainer class  

This is a packet container class which is responsible for the following:

  • Able to hold variadic length packet which has variadic length array after the packet structure. 
How to use this packet container class?    
  • Packet Type Declaration    
    typedef enum _sendPacketType{
       SEND_PACKET_TYPE_DOSOMETHING=0,
       SEND_PACKET_TYPE_DOSOMETHING2,
       SEND_PACKET_TYPE_DOSOMETHING3,
    }SendPacketType;
     
    struct SendPacket{
       SendPacketType packetType;
        unsigned int magicNum;
    };     

    For example, if you define the packet structure as above, you can create Packet Container object as below , and transfer packet data with variadic length array to Packet object to send it to the client/server.

    ...
    PacketContainer<SendPacket> packetContainer;
    Packet sendPacket;
    packetContainer=PacketContainer<SendPacket>(0     // default array size addition to SendPacket
                                               , true //   flag whether to allocation memory
                                                      //   within PacketContainer object or not
                                               );
     
    packetContainer.GetPacketPtr()->packetType=SEND_PACKET_TYPE_DIDSOMETHING;
    CString someString=_T("Hello");
        
    // Add given array after the SendPacket struct.
    packetContainer.SetArray(reinterpret_cast<const char*>(someString.GetString()),(someString.GetLength()+1)*sizeof(TCHAR));
     
    // Transfer to Packet object to send the packet with variadic array to Server/Client.
    sendPacket=Packet(reinterpret_cast<const void*>(
       packetContainer.GetPacketPtr())      // returns the pointer to the SendPacket object
                                            //   followed by array set above.
       ,packetContainer.GetPacketByteSize() // returns the byte size of SendPacket Struct
                                            //   with array.
       ,false                               // Since the memory is allocated
                                            //   in PacketContainer already,
                                            //   don't allocate in Packet Object
                                            //   for Performance increase
       );
  • Note that since copying memory for packet is critical on the performance in Server development, you can choose whether to allocate the memory for the PacketContainer object, or just hold the pointer to the data within the PacketContainer object according to the situation.   

For more detail, please refer to the EpServerEngine's Wiki page, here.  

UDP Classes

UDP classes are very similar to TCP classes, and I think it is just waste of space and your time to explain almost same classes here again.

If you need more details, please see EpServerEngine's wiki page below,

Conclusion

As I said in Introduction, this might not be the best framework as the performance-wise. However, since using this framework allow you to build the server and client very quickly. I believe this can be very helpful for developing prototype server/client application, or testing the packet communication, since this enhance you to start from server/client communication design by letting you ignore all the nasty initialization of Winsock and thread. Hope this helps to your server/client development. 

Reference

History

  • 08.22.2013: Re-distributed under MIT License  
  • 08.17.2013: Sample Source added.  
  • 01.25.2013: Download link updated. 
  • 11.17.2012: Moved to right section. 
  • 11.16.2012: Class diagram added. 
  • 11.15.2012: Submitted the article.  

License

This article, along with any associated source code and files, is licensed under The MIT License

About the Author

Woong Gyu La
Software Developer
Korea (Republic Of) Korea (Republic Of)
Woong Gyu La had been working as a software developer for over 8 years.
His personal interests are improving his personal projects,
 
EpLibrary (Visual C++ Utility Library)
https://github.com/juhgiyo/EpLibrary[^]
 
EpOraLibrary (Oracle OCI Wrapper Library for Visual C++)
https://github.com/juhgiyo/EpOraLibrary[^]
 
EpServerEngine (Visual C++ WinSock Server/Client Engine)
https://github.com/juhgiyo/EpServerEngine[^]
 
EpWinService (Windows Service Management Solution)
https://github.com/juhgiyo/EpWinService[^]
 
And other projects can be found at
https://github.com/juhgiyo?tab=repositories[^]
 
Finally, my other articles can be found at
http://www.codeproject.com/Articles/juhgiyo#articles[^]
 
You can contact me at juhgiyo@gmail.com[^]
Follow on   Twitter   Google+   LinkedIn

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web02 | 2.8.140415.2 | Last Updated 22 Aug 2013
Article Copyright 2012 by Woong Gyu La
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid