Click here to Skip to main content
15,895,011 members
Articles / Programming Languages / XML

A Full Library for a Socket Client/Server System

Rate me:
Please Sign up or sign in to vote.
4.81/5 (42 votes)
14 Jan 2015CPOL5 min read 162K   11.3K   149  
My article shows a library that everyone can use to create their socket communication. Also, it explains how the library is developed.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SocketServerLib.SocketHandler;
using System.Diagnostics;

namespace SocketServerLib.Message
{
    /// <summary>
    /// This abstact class represent A message. You have to extend and implement this class to define your message.
    /// The message header is already included.
    /// </summary>
    public abstract class AbstractMessage
    {
        /// <summary>
        /// The message header.
        /// </summary>
        protected AbstractMessageHeader header = null;
        /// <summary>
        /// The body as byte[]
        /// </summary>
        protected byte[] body = null;
        /// <summary>
        /// Index in the buffer
        /// </summary>
        private int bufferIndex = -1;
        /// <summary>
        /// Buffer for pending message in receiving
        /// </summary>
        private byte[] previousBuffer = null;

        /// <summary>
        /// Constructor.
        /// </summary>
        public AbstractMessage()
        {
        }

        /// <summary>
        /// Return an empty instance of a Message Header. Implement this method to define your message class.
        /// </summary>
        /// <returns>The empty Message Header instance</returns>
        protected abstract AbstractMessageHeader GetMessageHeaderInstance();

        #region Properties
        
        /// <summary>
        /// The Client UID.
        /// </summary>
        public string ClientUID
        {
            get
            {
                return header.ClientUID;
            }
        }

        /// <summary>
        /// The message UID.
        /// </summary>
        public string MessageUID
        {
            get
            {
                return header.MessageUID;
            }
        }

        /// <summary>
        /// The message length included the header.
        /// </summary>
        public int MessageLength
        {
            get
            {
                return header.MessageLength;
            }
        }

        /// <summary>
        /// The Message Header.
        /// </summary>
        internal AbstractMessageHeader Header
        {
            get
            {
                return header;
            }
        }

        #endregion

        /// <summary>
        /// The message body as byte[]
        /// </summary>
        /// <returns>The buffer contains the message body</returns>
        public virtual byte[] GetBuffer()
        {
            return body;
        }

        /// <summary>
        /// Check if the message is complete.
        /// </summary>
        /// <returns>Return true if the message is complete, otherwise false.</returns>
        public bool IsComplete()
        {
            return bufferIndex == -1;
        }

        #region Read methods on the buffer

        /// <summary>
        /// Read the first message contained in the buffer.
        /// </summary>
        /// <param name="buffer">The buffer used for the receiving</param>
        /// <param name="len">The real size of the buffer</param>
        /// <returns>0 if the message in the buffer is not complete. Otherwise the len of the message read</returns>
        private int ReadFirstMessage(byte[] buffer, int len)
        {
            return ReadFirstMessage(buffer, 0, len);
        }

        /// <summary>
        /// Read the first message contained in the buffer.
        /// </summary>
        /// <param name="buffer">The buffer used for the receiving</param>
        /// <param name="offset">The offset for start read the buffer</param>
        /// <param name="len">The real size of the buffer</param>
        /// <returns>0 if the message in the buffer is not complete. Otherwise the len of the message read</returns>
        private int ReadFirstMessage(byte[] buffer, int offset, int len)
        {
            // Get a Message Header instance
            header = GetMessageHeaderInstance();
            // Read the Message Header from the buffer
            int idx = header.Read(buffer, offset);
            // Check if the message is complete
            if (!header.IsComplete())
            {
                // If it's not, save the buffer in the previous buffer (pending message) and return 0.
                previousBuffer = new byte[len - offset];
                Buffer.BlockCopy(buffer, offset, previousBuffer, 0, previousBuffer.Length);
                bufferIndex = 0;
                return 0;
            }
            // Get the message body
            body = new byte[header.MessageLength - header.HeaderLength];
            int max = (header.MessageLength >= len) ? (len - header.HeaderLength) : (header.MessageLength - header.HeaderLength);
            Array.Copy(buffer, idx, body, 0, max);
            // Check if the message is complete.
            if (body.Length > len - header.HeaderLength)
            {
                // If it's not, return 0.
                bufferIndex = (len - header.HeaderLength);
                return 0;
            }
            bufferIndex = -1;
            return (header.MessageLength >= len) ? 0 : (header.MessageLength);
        }

        /// <summary>
        /// Add a buffer to the pending message and try to read the first message.
        /// </summary>
        /// <param name="buffer">The buffer to add</param>
        /// <param name="len">The real size of the buffer</param>
        /// <returns>0 if the message in the buffer is still not complete. Otherwise the len of the message read</returns>
        private int AppendBuffer(byte[] buffer, int len)
        {
            if (!header.IsComplete())
            {
                int prevLen = previousBuffer.Length;
                byte[] fullBuffer = new byte[buffer.Length + prevLen];
                Buffer.BlockCopy(previousBuffer, 0, fullBuffer, 0, previousBuffer.Length);
                Buffer.BlockCopy(buffer, 0, fullBuffer, previousBuffer.Length, buffer.Length);
                previousBuffer = null;
                int moremsg = ReadFirstMessage(fullBuffer, fullBuffer.Length);
                if (moremsg != 0)
                {
                    moremsg -= prevLen;
                }
                return moremsg;
            }
            int max = ((bufferIndex + len) > body.Length) ? (body.Length - bufferIndex) : len;
            Array.Copy(buffer, 0, body, bufferIndex, max);
            bufferIndex += max;
            if (bufferIndex == (header.MessageLength - header.HeaderLength))
            {
                bufferIndex = -1;
                return (max == len) ? 0 : max;
            }
            return 0;
        }

        /// <summary>
        /// Try to read a message from the buffer.
        /// </summary>
        /// <param name="message">The destination message</param>
        /// <param name="state">The state object</param>
        /// <param name="byteRead">The umber of bytes in the input buffer</param>
        /// <returns>The message read, otherwise false.</returns>
        internal static AbstractMessage TryReadMessage(AbstractMessage message, SocketStateObject state, int byteRead)
        {
            AbstractMessage messageRead = null;
            int moreMessage = 0;
            byte[] buffer = state.buffer;  // Get buffer

            if (state.pendingBuffer != null) //Check for pending data and merge it
            {
                buffer = new byte[byteRead + state.pendingBuffer.Length];
                Array.Copy(state.pendingBuffer, 0, buffer, 0, state.pendingBuffer.Length);
                Array.Copy(state.buffer, 0, buffer, state.pendingBuffer.Length, byteRead);
                byteRead = buffer.Length;
            }
            state.pendingBuffer = null;
            if (state.message == null)
            {
                state.message = message;
                moreMessage = state.message.ReadFirstMessage(buffer, byteRead);
                Trace.WriteLine(string.Format("Receive 1st package MessageUID {0} ClientUID {1}", state.message.MessageUID, state.message.ClientUID));
            }
            else
            {
                moreMessage = state.message.AppendBuffer(buffer, byteRead);
                Trace.WriteLine(string.Format("Receive more package MessageUID {0} ClientUID {1}", state.message.MessageUID, state.message.ClientUID));
            }
            if (state.message.IsComplete())
            {
                Trace.WriteLine(string.Format("Receive complete message {0} len {1}", state.message.MessageUID, state.message.MessageLength));
                messageRead = state.message;
                Trace.WriteLine(string.Format("Prepare to receive a new message. moreMessage = {0}", moreMessage));
                state.message = null;
                if (moreMessage > 0)
                {
                    state.pendingBuffer = new byte[byteRead - moreMessage];
                    Array.Copy(buffer, moreMessage, state.pendingBuffer, 0, state.pendingBuffer.Length);
                    Trace.WriteLine(string.Format("Copy {0} bytes to pending buffer", state.pendingBuffer.Length));
                }
            }
            return messageRead;
        }

        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

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


Written By
Team Leader Mediatech Solutions
Italy Italy
I’m an IT Project Manager for an Italian Betting Company and over the last 2 years I acquired experience in Betting area.
I have developed code in different object oriented languages (C#, C++, Java) for more than 10 years using a set of technology such as .Net, J2EE, multithreading, etc…

Comments and Discussions