5,423,696 members and growing! (18,276 online)
Email Password   helpLost your password?
Web Development » Internet / Network » General     Intermediate License: The Code Project Open License (CPOL)

Generic TCP/IP Client/Server

By Gil.Y

Generic TCP/IP Client/Server
C# 2.0, C#Windows, .NET, .NET 3.0, .NET 2.0, Win2K, WinXP, Win2003, VistaVS2005, Visual Studio, Architect, Dev

Posted: 22 Jun 2007
Updated: 11 Aug 2007
Views: 50,246
Bookmarked: 88 times
Announcements
Want a new Job?



Search    
Advanced Search
Sitemap
28 votes for this Article.
Popularity: 5.75 Rating: 3.97 out of 5
3 votes, 10.7%
1
3 votes, 10.7%
2
0 votes, 0.0%
3
5 votes, 17.9%
4
17 votes, 60.7%
5
Note: This is an unedited contribution. If this article is inappropriate, needs attention or copies someone else's work without reference then please Report This Article

Download FILES.zip - 686.1 KB

Screenshot - designview.jpg

Introduction

In this article I hope to show a very useful architecture for building a network server over TCP/IP communication.
I know there are a ready TCP and UDP servers you can buy and there are some more you can download but the problem
is that those you can buy usually gives you a "black box". they also cost money (the good ones cost alot of money).
Another problem is that they were designed for heavy and resourceful machines to work properly.
The others you can download seem to go stright into a certain architecture which is usually incorrect or least to
say they use an architecture which I believe is not the best we can use to milk the machine.
One of my supervisors once told me that the enemy of the "good" is the "excellent", well obviously he was correct!
But I still want the excellent, don't you feel the same ?

Well enough chit-chat let's examine the problem:

Sockets can use three modes for communicating:

a. Synchronous communication - using the blockmode which halts execution until somthing happens in the socket.
b. Synchronous communication - without using blockmode - but this mean we will get errors we gonna have to catch.
c. Asynchrounous communiction - which magicly opens a thread per socket activity thus not halting execution.

And offcourse it is possible to mix synchronus calls with asynchronous calls if you seriouly feel you don't
have enough problems to sustain a tremandous amount of humor in your life.

The first examination is why and when would we want to use the async communition?

Since async calls opens a thread for every socket operation it forms a great solution for client programs,
It will manage the communication without halting other procedures, it is tested and it is absoultly not suitable for servers.
Why? The server using async calls will open a thread for every socket method which will mean our server going to
end up having way too many threads than needed and further than that adding threads at some point will make the entire
list of the other threads running slower and slower until the hardware will require an upgrade. now that will happen
no matter what logic we will implement; but the question is when is it going to happen ? the more threads we open the sooner we need a hardware upgrade, so for now we can determine that using async calls is out of the question if we want a low resource waste with maximal optimization in our server.

So at our goals is to use the smallest amount of threads as long as our Latency definition (AKA: Lag) is meeting our design requirements.
Yes, I know some of you would say somthing like: "what are you talking about, most servers work with async communication!".
They are correct - so if things are as I explianed in the previous paragraph why so many servers use the async architecture ?
The answer is in an assumption that will not hold water and in a non-elegant workaround that brutly force things to work as I will explain.
Behind this architecture lays an assumtion that clients keep communicating to the server making the threads behind close and open
all the time since an operation like reciving data or sending data is a short operation and since it's short it takes resources only
for few nano-secs and then they are released, and so on many threads can open and close without actually wasting resources in
a constant way, which is actually very true (so far).

However, if for example I will connect to such a server from my client and I will decide not to send data to that server this would mean
that that server has opened a BeginRecieve method that is waiting for my client to send somthing and as long as I don't send the thread
that waits for data remains open and does not close..
If we had 2000 clients doing that - the server will be in trouble and the architect who designed it will be either unemployed or possibly
promoted to a division manager - that depends where you work.
To workaround this problem the architecture requires a non-idle policy, so threads can close and malicous clients can not use that exploit.
One of those policies can be a timer that close idle clients other can be based on a ping mechanism and few would go for even smarter solutions
that keeps track of resources and disconnecting idle clients only when resources are getting low.
But all those are workarounds which points out that they deal with the symptoms and not the problem that causing them!
So far for the async server architecture which as I explained is non suitable.

Another aproach would be to use Sync calls in blockmode to our sockets - this means that execution will halt, So to workaround this we can open a thread per client. But again we shall have many threads running in our system and that is somthing we already killed as a server solution.

The last option remaining is to use the sync calls without blockmode. the problem is that 2000 clients connected to
our machine will need to be scanned repeatingly and that would take too long to scan.

The remedy
So the remedy is to use few threads to scan our clients - for example every 100 will be scaned by a thread.
However we don't have the slightest idea reagrding how long would it take to scan 100 clients in diffrent machines.
Another problem is that constanting 100 is an arbitar definition which means we will not be dynamic.

What I did in that point is creating a class which I called ServerNode, this ServerNode holds clients and return latency information
of the scan (how long did it take to scan all the clients in the node).
In the server base I created a property which I named RequiredLatencyPerClient, from that point things begun to be much clearer.

When the server ClientsList recieves a new client the server scan all nodes and decide to which node to add the new client.
If no node latency is meeting the RequiredLatencyPerClient the server will create a new node and will add the new client to that node.

Foreman Design Pattern - Is that a new design pattern ? (If not I got a new and very generic idea instead - I will call it the "Wheel"!).

Well so far so good but while writing the code I couldn't help thinking that I somehow doing somthing which is very generic in many situations,
The idea that we got a client that needs to be orginized in nodes in order to maintain a certain latency is somthing that can
be useful in many situations, So I decided it would be even better to take that work pattern out of the server and implement it independently
so it can be used dynamicly and in other situations as well.

I seeked the books and the internet to see if there is a known design pattern that actually do that - and found that there isn't so
if no one objects I taking the right to call it The Foreman Design Pattern.

But... there is somthing that bothers me, what if some clients need diffrent latency than others ? hmm..
and what if I want to be able to change a client latency dynamiclly ?

So scrathing my head I finally realized;
The simple solution was staring at me all the time! The Foreman will not have a RequiredLatencyPerClient.
Instead each client, which from now on we shall call a worker
(since we are tinkering with a design pattern and not just a communication server).
Anyway each worker will have a latency information - and the Foreman will put the worker in a suitable node according to it's latency requirments.

Based on the Foreman Design Pattern I can build a very cool communication server.

So, after building the foreman I begun with writing a simple code to wrap the socket communication options. I opened a project and called it NetworkSocketManager - that will be used in two other project, a server and a client. the NetworkSocketManager will let us choose between socketing work methodology and it wrap all Synchronic and Asynchronic communication options (including the block mode).

Having done with that part, I coded a simple client that contains the NetworkSocketManager, The project is called NetworkClient.

Now the client itself is also very nice & friendly since it already implements a basic application level transmition control, any data that is going to be sent is being warrped in an envelope which contains:
| prefix length = 1 digit | Prefix = length of data | data |So when reciving the event for recieve message is going to happen only when full data accumulated through the socket.
The situation of getting partial data can happen if I didn't implement the application level transmition control if for example the server out buffer is set to 1024 and the recieve buffer is set to 256 - this would cause partial data comming or full data comming in 4 application level packets, But as I said this is already solved inside.

If you wish to have encryption on your data - make sure to encrypt before you pass on the data to the send and if you wish to manipulate your message before sending it (for example if you need integration with anohter server) you will have to inherit the communication class and overide the communication functions to your needs.

The next task was to build a server that uses the already mentioned ForemanDP, So... I opened another project and called it NetworkServer. in it I derived the ForemapDP worker class to CommunicationWorker class and server class derives from Foreman BasicServer. and walla. If everything so far is understood - you will be able to understand my code and why is it so powerful.

The fact that this is a powerful code implentation lays in the fact that all the programmer needs to do is to derive a worker and a server to have an out of the box, running and opened sourced Client/Server architecture which uses a correct resource methodology.

Comparing to Classic IOCP

As we can see the model based on the foreman will "milk" the machine better since the definition of required worker (client) latency has a meaning regarding who need more focus from the machine. to be even more exact, the clients that require latency < 100 are treated in a diffrent thread.
This is what makes the diffrence from the classic IOCP, where all clients are treated in the same threadpool - there is a waste there since a client that require latency of 1000 (1 sec) will cause the other queued clients to hold until it is finished processing.

However ForemanDP knows how to deal with such a case :
In the case a client latency is not met a new thread (clients node) will be opened and that client will be moved to it to make sure it can recieve the latency it requires.

Using the code

Ok, So how do we get started?

Step 1.

The first thing is either to add just references or the projects into your solution. For those who need to have servers, You will need the NetworkSocketManager, ForemanDP & Network Server. For those who need to have clients, you will need the NetworkSocketManager & NetworkClient. For those who need both just add it all...

Step 2.

Server developers:
In your class write your code to contain the NetworkServerBase.
(or your class which derives it).

        private NetworkServerBase MyServer = null;
        

Next thing is to instanciate the server, you would want to put it in the Form_Load or a contsructor of your class:

         MyServer = new NetworkServerBase(new TimeSpan(1000));
         
         // This line will add a hook for events in your class
            MyServer.OnServerNotify += 
                new ForemanDP.BasicServer.DelegateServerNotification(MyServer_OnServerNotify);
                
         // Note that following line will not work becuase you need to replace the bolded area
         // with the ip you want to link.
            MyServer.AddListener(
                new NetworkServerClientListener(new TimeSpan(2000),
                new System.Net.IPEndPoint("replace this with the ip you want", 5500), 100));
        

The last line is intersting because what it actually does is adding a listener with a required latency of 2 secs to the server.

The NetworkServerClientListener is derived from NetworkClientWorker which derives from ForemanDP worker. The line of code is setting port 5500 to be a listening port and put 100 backlog space for queue to clients which needs to connect. you can added more listeners - as many as you want! by running a loop on that line or just copy paste it, whatever your needs be.

From that point forward every communication event to that port is piped to the MyServer_OnServerNotify:

        void MyServer_OnServerNotify(ForemanDP.BasicWorker worker, object data)
        {
            switch (((NetworkServerClientWorker.NetworkClientNotification)data).EventType)
            {
                case NetworkClient.NetworkClientBase.ClientEventTypeEnum.Accepted:
                    connectedUsers++;
                    break;
                case NetworkClientBase.ClientEventTypeEnum.Disconnected:
                    connectedUsers--;
                    break;
            }
        }
        

You can catch messages and anything else you need from that event.

Clients: In your class write your code to contain the NetworkClient:

            private NetworkClientBase clientCommunication = null;
            
            ---- your constuctor or Form_Load -----
            clientCommunication = new NetworkClientBase(
            new CommunicationSettings(
            CommunicationSettings.SocketOperationFlow.Asynchronic,
            CommunicationSettings.SocketOperationFlow.Asynchronic,
            CommunicationSettings.SocketOperationFlow.Asynchronic,
            CommunicationSettings.SocketOperationFlow.Asynchronic,
            CommunicationSettings.SocketOperationFlow.Asynchronic, false, 64));
            
            // and an event hooker!
            clientCommunication.OnClientEvent += 
                new NetworkClientBase.DelegateClientEventMethod(clientCommunication_OnClientEvent);
            :
            :
            :
            ---------------------------------------
            
            //and then ...
            
            private void clientCommunication_OnClientEvent(
                NetworkClientBase.ClientEventTypeEnum EventType, object EventData)
            {
                switch (EventType)
                {
                    case NetworkClientBase.ClientEventTypeEnum.None:
                        break;
                    case NetworkClientBase.ClientEventTypeEnum.Accepted:
                        break;
                    case NetworkClientBase.ClientEventTypeEnum.Connected:
                        break;
                    case NetworkClientBase.ClientEventTypeEnum.RawDataRecieved:
                        break;
                    case NetworkClientBase.ClientEventTypeEnum.MessageRecieved:
                        break;
                    :
                    :
                    :
                    and so on to catch what ever you need or want.
        

Latest Vesion Additional Info

As an example I add a simple chat implementation, in order to run it correct:

1. Simply from visual studio click the green traingle button (F5) - this will run the chat server.

2. While the ChatServer is running go back to visual studio and right click on the ChatClient project - a popup menu will apear, from the popup-menu click Debug-Start New Instance.

You can repeat step 2 to create and run as many instances as you want.

Note: The chat-client/server is just an example for how to use the entire architecture, it is not the focus of this article and it is not intended to be bug-free.

Benchmark program Addition
I have repaired much of the TCPSokect layer which was causing problems in certain
situations, if more bugs arise please let me know so I can fix them as well (In my machine
it works).

The benchmark opens a server and multiple async clients to communicate with the server.
The server will echo each incomming packet back to the client who sent it (much like ping!).

Since it was neccary to identify each async client I created another client which contains a NetworkClientBase and adds Identity to it, normally you will not need such a client unless you want to work with multiple clients without a server or a main node to manage them inside it.

Finally

Most of the programmers believe that they have the best code that only god may match in perfectness, And I am no diffrent! But in order to realy learn somthing one must put his ego very far away, So please let me know what you think and feel free to critisize.

The connecter area in the server is not in use - I didn't finish it - but unless you want a server that can initate communication to other server like proxy for example, it will not bother you. I promise to finish it if you code project folks want it.

Last thing: Some might ask why not to use WCF instead of all that ? WCF is a very advanced request-response architecture and as progressed as it is
it will not give you "the always online in real time connection".
check this out: http://msdn2.microsoft.com/en-us/netframework/aa663324.aspx
if you don't believe.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Gil.Y



Location: Israel Israel

Other popular Internet / Network articles:

Article Top
Sign Up to vote for this article
You must Sign In to use this message board.
FAQ FAQ Noise ToleranceSearch Search Messages 
 Layout  Per page   
 Msgs 1 to 25 of 119 (Total in Forum: 119) (Refresh)FirstPrevNext
Subject  Author Date 
QuestionSending different message back to client. NOT ECHOmemberKwaku Fordjour18hrs 53mins ago 
AnswerRe: Sending different message back to client. NOT ECHOmemberGil.Y13hrs 5mins ago 
GeneralRe: Sending different message back to client. NOT ECHOmemberKwaku Fordjour3hrs 17mins ago 
GeneralRe: Sending different message back to client. NOT ECHOmemberGil.Y2hrs 32mins ago 
QuestionRe: Sending different message back to client. NOT ECHOmemberKwaku Fordjour5hrs 23mins ago 
AnswerRe: Sending different message back to client. NOT ECHOmemberGil.Y2hrs 15mins ago 
GeneralRe: Sending different message back to client. NOT ECHOmemberKwaku Fordjour1 hr 48mins ago 
GeneralIPV4 and IPV6 Support for the librarymemberKwaku Fordjour4:24 1 Aug '08  
GeneralRe: IPV4 and IPV6 Support for the librarymemberGil.Y3:10 2 Aug '08  
GeneralAsynchronous Call ob Client sidememberKwaku Fordjour10:52 25 Jul '08  
AnswerRe: Asynchronous Call ob Client side [modified]memberGil.Y10:28 27 Jul '08  
GeneralRe: Asynchronous Call from Client to servermemberKwaku Fordjour20:35 28 Jul '08  
GeneralRe: Asynchronous Call from Client to servermemberGil.Y10:23 29 Jul '08  
GeneralHigh processor usage when sending large amounts of datamemberr_hyde16:06 16 Apr '08  
AnswerRe: High processor usage when sending large amounts of datamemberGil.Y16:07 17 Apr '08  
GeneralRe: High processor usage when sending large amounts of datamemberr_hyde9:49 18 Apr '08  
GeneralRe: High processor usage when sending large amounts of datamemberGil.Y2:04 19 Apr '08  
GeneralRe: High processor usage when sending large amounts of datamemberKwaku Fordjour20:58 14 Aug '08  
GeneralRe: High processor usage when sending large amounts of datamemberr_hyde14:45 16 Aug '08  
QuestionDetect connection statusmemberms.linuz22:39 1 Apr '08  
GeneralRe: Detect connection statusmemberms.linuz22:49 1 Apr '08  
GeneralRe: Detect connection statusmemberGil.Y6:30 2 Apr '08  
QuestionRe: Detect connection statusmemberms.linuz23:36 7 Apr '08