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

Protocol implementation

, 31 Jan 2005
Rate this:
Please Sign up or sign in to vote.
Add some protocol support to your server.

Screenshot

Introduction

This article is about protocol implementation.

When you create an application that needs to communicate over the Internet, it isn't that hard to implement some sort of protocol, but if you have never seen or done it yourself, it's nice to have some example or some sort of guidance.

I created some base classes. Using multithreading, you can support as many connections as you want as long as you manage them well.

You can actually see in the base server class that all it does is look for some incoming connections. The protocol is implemented in the ClientConnection class which is a helper class that does the thinking. But since these are base classes, no protocol messages are defined in the base ClientConnection class. This should be done in a new class inherited from the base one. I added a sample to show you how to add messages and handle them.

In the sample, I created a server that accepts four commands:

  • VER -> shows the version of the server.
  • DOUBLE -> doubles a predefined var (2).
  • DIV -> divides the var.
  • RES -> returns the var.

The sample does just that. But if you examine the code, it shouldn't be hard to create an FTP server or whatever server you want.

Note

I do want to point out that this article is intended to get you started. I want to show you how you can implement a protocol. The code I provide here works for basic needs. If you hack the code a little, you can do great things with it. As always, I tried to write very simple and elegant code; even though it gets a little advanced, it is very self explanatory.

Using the code

If you want to create a server using these base classes, you should inherit from MultiThreadedServerBase and then add some message handlers in this new server class. For each message you add, you must add a function that will handle the message.

The usage of the code is quite simple. Although these classes are far from complete, they can give you an idea on how you can create a fully featured server with your own protocol. If you would like to add some parameters to a message, you can do it when you receive the message callback. Just parse the complete message and you'll get your parameters.

public class MultiThreadedServerBase
{
    public const int BACKLOG = 10;

    private Thread _mainThread;

    private int _port;
    private string _localIP;
    private Socket _mainSoc;
    private ArrayList _connections;
    private MessageCollection _messageHandlers;

    // a collection of (messageHandlers) functions
    internal MessageCollection MessageHandlers
    {
        get
        {
            return _messageHandlers;
        }
    }

    public MultiThreadedServerBase(string localIP,int port)
    {
        _messageHandlers = new MessageCollection();
        _connections = new ArrayList();
        _port = port;
        _localIP = localIP;
    }

    public void Start()
    {
        // check to see if the thread is alraidy running.
        if (_mainThread != null)
        {
            new Exception("close the server before starting it again.");
        }
        else
        {
            _mainThread = new Thread(new ThreadStart(Listen));
            _mainSoc = new Socket(
                AddressFamily.InterNetwork,
                SocketType.Stream,
                ProtocolType.Tcp);

            _mainSoc.Bind(new IPEndPoint(IPAddress.Parse(_localIP),_port));
            _mainThread.Start();
        }
    }

    // this makes it possible to add other ClientConnection based classes
    // implement the protocol.
    protected virtual ClientConnection OnCreateNewConnection(Socket socket)
    {
        return new ClientConnection(_mainSoc.Accept(),this);    
    }

    private void Listen()
    {
        _mainSoc.Listen(BACKLOG);

        try
        {
            while(true)
            {
                ClientConnection client = OnCreateNewConnection(_mainSoc.Accept());
                _connections.Add(client);
            }
        }
        catch(SocketException e)
        {
            // if the client closes the connection we will get an exception.
            if (e.ErrorCode != (int)SocketErrorCodes.InterruptedFunctionCall)
                System.Diagnostics.Debug.WriteLine(
                    e.Message.ToString());
        }
    }

    public void Stop()
    {
        if (_mainSoc != null)
        {
            _mainSoc.Close();
            _mainSoc = null;

            _mainThread.Abort();
            _mainThread = null;

            foreach(ClientConnection con in _connections)
            {
                con.Dispose();
            }
        }
    }

    public void Suspend()
    {
        _mainThread.Suspend();
    }

    public void Resume()
    {
        _mainThread.Resume();
    }
}

You can see that all this does is listening for incoming connections. If there is an incoming connection, a ClientConnection class is created which will wait for data from the client. If data is received, the ClientConnection class will look in the server class whether it has an implementation for the data received event. If so the function will be invoked.

public class ClientConnection : IDisposable
{
    private const int BUFFERSIZE = 1024;

    private bool _disposed;
    private Socket _clientSocket;
    private Thread _thread;
    private MultiThreadedServerBase _server;

    protected Socket Socket
    {
        get
        {
            return _clientSocket;
        }
    }

    public Thread Thread
    {
        get
        {
            return _thread;
        }
    }

    public ClientConnection(Socket clientSocket , MultiThreadedServerBase server)
    {
        _server = server;
        _disposed = false;
        _clientSocket = clientSocket;

        _thread = new Thread(new ThreadStart(Recieve));

        // start the thread.
        try
        {
            _thread.Start();
        }
        catch(ThreadAbortException abortException)
        {
            Console.WriteLine((string)abortException.ExceptionState);
        }
    }

    ~ClientConnection()
    {
        if (!_disposed)
            Dispose();
    }

    internal void Recieve()
    {
        byte[] buffer = new byte[BUFFERSIZE];

        try
        {
            _clientSocket.Receive(buffer,0,buffer.Length,SocketFlags.None);
        }
        catch (SocketException se)
        {
            switch(se.ErrorCode)
            {
                case (int)SocketErrorCodes.ConnectionAborted :
                    Dispose();
                    break;

                case (int)SocketErrorCodes.ConnectionResetByPeer :
                    Dispose();
                    break;

                // this is thrown when i need to stop
                // if the server go's down.
                case (int)SocketErrorCodes.InterruptedFunctionCall:
                    break;
            }
            return;
        }

        HandleCommand(buffer);
        buffer = null;
    }

    // if i recieved something then it should be handled.
    private void HandleCommand(byte[] data)
    {
        string rxData = Encoding.Default.GetString(data);
        string command = rxData.Trim('\0').Split(' ')[0];

        // check if i got a function for this command
        if (_server.MessageHandlers.Contains(command))
        {

            ((NetworkMessageHandler)_server.MessageHandlers[command])
                .DynamicInvoke(new object[]{this,rxData});
        }
        // if not i'll just continue to recieve and do nothing.
        else if (command != "")
        {
            Recieve();
        }
    }

    private string GetString(byte[] data)
    {
        return Encoding.Default.GetString(data);
    }

    public void Dispose()
    {
        if (!_disposed)
        {
            _disposed = true;

            _clientSocket.Close();
            _clientSocket = null;

            _thread.Abort();
            _thread = null;
        }
        else
        {
            new ObjectDisposedException("connection has alraydy been disposed");
        }
    }
}

You should take a look at the sample for trying out an implementation of your own protocol.

Points of Interest

I would like you to comment on this article. I'm new to posting of articles, I would like to post some more. If you think the article sucks, please tell me why. I'm eager to know what I should change. I am an expert on component/control development. Maybe there are some cool things you'd like to see in an article. Please let me know if you find spelling errors. Please post them or let me know, until then bye.

History

I created this project in January 2005.

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

About the Author

engine252

Belgium Belgium
No Biography provided

Comments and Discussions

 
QuestionA bug? Pinmemberavish99920-Aug-07 1:43 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140709.1 | Last Updated 31 Jan 2005
Article Copyright 2005 by engine252
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid