Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / Win32

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

4.83/5 (14 votes)
16 Jul 2015MIT8 min read 52.9K   823  
How to create a server-client network quickly using template server-client framework, EpServerEngine (C++ and Windows Winsock).

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). If you worry about this 10K problem, please check out EpserverEngine.cs, which is a asynchronous light-weight IOCP TCP template server framework in C#.  

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

Image 1

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.

C++
class MyServer: public BaseServer   
C++
// MyServer.h
class MyServer:public BaseServer
{
...
virtual BaseServerWorker* createNewWorker();
...
};
// MyServer.cpp
#include "MyServer.h"
#include "MyServerWorker.h"
...
BaseServerWorker* MyServer::createNewWorker()
{
return new MyServerWorker();
}
...  
  1. Create your Server class (ex. MyServer)  
  2. Make your class as a sub-class of this BaseServer   
  3.  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) 
  4. Optional: If more functionality is needed for your server, implement in your Server class (ex. MyServer). 
  5. Done!   
How to start/stop the server  

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

C++
#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.

C++
class MyServerWorker: public BaseServerWorker 
C++
// MyServerWorker.h
class MyServerWorker:public BaseServerWorker
{
   ...
   virtual BasePacketParser* createNewPacketParser();
   ...
};
// MyServerWorker.cpp
#include "MyServerWorker.h"
#include "MyPacketParser.h"
...
BasePacketParser* MyServerWorker::createNewPacketParser()
{
   return new MyPacketParser();
}
...    
  1. Create your Server Worker class (ex. MyServerWorker)  
  2. Make your class as a sub-class of this BaseServerWorker  
  3.  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)     
  4. Optional: If more functionality is needed for your server worker, implement in your Server Worker class (ex. MyServerWorker). 
  5. 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?   
C++
class MyClient: public BaseClient 
C++
// MyClient.h
class MyClient:public BaseClient
{
   ...
   virtual BasePacketParser* createNewPacketParser();
   ...
};
// MyClient.cpp
#include "MyClient.h"
#include "MyPacketParser.h"
...
BasePacketParser* MyClient::createNewPacketParser()
{
   return new MyPacketParser();
}
... 
  1. Create your Client class (e.g., MyClient
  2. Make your class as a sub-class of this BaseClient   
  3.  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.)          
  4. Optional: If more functionality is needed for your client, implement in your Client class (e.g., MyClient). 
  5. Done!   
How to connect/disconnect to/from the server  

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

C++
#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?   
C++
class MyPacketParser: public BasePacketParser 
C++
// 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
}
... 
  1. Create your Packet Parser class (e.g., MyPacketParser)   
  2. Make your class as a sub-class of this BasePacketParser 
  3.  Implement the ParsePacket function (which is a pure virtual function in the BasePacketParser class).   
  4. Optional: If more functionality is needed for your packet parser, implement in your Packet Parser class (ex. MyPacketParser).  
  5. 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  
C++
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.

C++
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    
C++
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.

C++
...
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. 

Also check out EpServerEngine.cs, which is a asynchronous light-weight IOCP TCP template server framework in C#.

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