Table of contents
- Introduction
- Background
- Optional
- Purpose of the Template Server-Client Framework
- Class Diagram
- TCP Classes
- Server side
- Client side
- General
- UDP Classes
- Conclusion
- 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
- Utility Library used in EpServerEngine
- The Lock framework used in EpServerEngine
- 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.
class MyServer: public BaseServer
class MyServer:public BaseServer
{
...
virtual BaseServerWorker* createNewWorker();
...
};
#include "MyServer.h"
#include "MyServerWorker.h"
...
BaseServerWorker* MyServer::createNewWorker()
{
return new MyServerWorker();
}
...
- Create your Server class (ex.
MyServer
) - Make your class as a sub-class of this
BaseServer
- 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) - Optional: If more functionality is needed for your server, implement in your Server class (ex.
MyServer
). - 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")); if(m_server.StartServer())
{
}
...
if(m_server.IsServerStarted()) {
m_server.StopServer(); }
...
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.
class MyServerWorker: public BaseServerWorker
class MyServerWorker:public BaseServerWorker
{
...
virtual BasePacketParser* createNewPacketParser();
...
};
#include "MyServerWorker.h"
#include "MyPacketParser.h"
...
BasePacketParser* MyServerWorker::createNewPacketParser()
{
return new MyPacketParser();
}
...
- Create your Server Worker class (ex.
MyServerWorker
) - Make your class as a sub-class of this
BaseServerWorker
- 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) - Optional: If more functionality is needed for your server worker, implement in your Server Worker class (ex. MyServerWorker).
- 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?
class MyClient: public BaseClient
class MyClient:public BaseClient
{
...
virtual BasePacketParser* createNewPacketParser();
...
};
#include "MyClient.h"
#include "MyPacketParser.h"
...
BasePacketParser* MyClient::createNewPacketParser()
{
return new MyPacketParser();
}
...
- Create your Client class (e.g.,
MyClient
) - Make your class as a sub-class of this
BaseClient
- 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.) - Optional: If more functionality is needed for your client, implement in your Client class (e.g.,
MyClient
). - 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")); if(m_client.Connect())
{
}
...
if(m_client.IsConnected()) {
m_client.Disconnect(); }
...
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?
class MyPacketParser: public BasePacketParser
class MyPacketParser:public BasePacketParser
{
...
virtual void ParsePacket(const Packet &packet );
...
};
#include "MyPacketParser.h"
...
void MyPacketParser::ParsePacket(const Packet &packet )
{
}
...
- Create your Packet Parser class (e.g.,
MyPacketParser
) - Make your class as a sub-class of this
BasePacketParser
- Implement the
ParsePacket
function (which is a pure virtual function in the BasePacketParser
class). - Optional: If more functionality is needed for your packet parser, implement in your Packet Parser class (ex.
MyPacketParser
). - 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?
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 , sizeof(SendPacket), , true );
- 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?
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 , true );
packetContainer.GetPacketPtr()->packetType=SEND_PACKET_TYPE_DIDSOMETHING;
CString someString=_T("Hello");
packetContainer.SetArray(reinterpret_cast<const char*>(someString.GetString()),(someString.GetLength()+1)*sizeof(TCHAR));
sendPacket=Packet(reinterpret_cast<const void*>(
packetContainer.GetPacketPtr()) ,packetContainer.GetPacketByteSize() ,false );
- 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.