Click here to Skip to main content
15,897,273 members
Articles / Programming Languages / C#

Custom socket communication

Rate me:
Please Sign up or sign in to vote.
4.50/5 (2 votes)
13 Dec 2012CPOL4 min read 29.7K   1.7K   30  
Custom socket communication between two .NET projects
using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.IO;
using System.Xml.Serialization;

[assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.xml", Watch = true)]

namespace Utils
{
    public class SocketSender<D, R> : IDisposable
    {
        public delegate void ClientConnect(String remoteAddress);
        public delegate void ClientRemove(String remoteAddress);
        public delegate void ResponseRecieved(String remoteAddress, R data);
        public delegate Boolean CanSend(String remoteAddress, D data);

        private log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);

        // max incoming connection on socket.Listen
        private const int MAX_CONN = 10;
        // timeout increment
        private const int TIMEOUT_INC = 2000;

        private int port;
        private Socket sock;
        private Thread acceptThread;

        private Timer lifeTickTimer;

        private ClientConnect onClientConnect;
        public event ClientConnect OnClientConnect
        {
            add
            {
                onClientConnect = (ClientConnect)System.Delegate.Combine(onClientConnect, value);
            }
            remove
            {
                onClientConnect = (ClientConnect)System.Delegate.Remove(onClientConnect, value);
            }
        }

        private ClientRemove onClientRemove;
        public event ClientRemove OnClientRemove
        {
            add
            {
                onClientRemove = (ClientRemove)System.Delegate.Combine(onClientRemove, value);
            }
            remove
            {
                onClientRemove = (ClientRemove)System.Delegate.Remove(onClientRemove, value);
            }
        }

        private ResponseRecieved onResponseRecieved;
        public event ResponseRecieved OnResponseRecieved
        {
            add
            {
                onResponseRecieved = (ResponseRecieved)System.Delegate.Combine(onResponseRecieved, value);
            }
            remove
            {
                onResponseRecieved = (ResponseRecieved)System.Delegate.Remove(onResponseRecieved, value);
            }
        }

        private CanSend onCanSend;
        public event CanSend OnCanSend
        {
            add
            {
                onCanSend = (CanSend)System.Delegate.Combine(onCanSend, value);
            }
            remove
            {
                onCanSend = (CanSend)System.Delegate.Remove(onCanSend, value);
            }
        }
        private XmlSerializer responseSerializer = new XmlSerializer(typeof(R));

        private List<ComClass<D>> clients = new List<ComClass<D>>();

        private int lifeTickTimeout;

        /// <summary>
        /// SockerSender server
        /// </summary>
        /// <param name="port">tcpip listen port</param>
        /// <param name="lifeTickTimeout">timeout between lifetick packet in ms</param>
        /// <param name="sendLifeTicks">send or not send lifetick events</param>
        public SocketSender(int port, int lifeTickTimeout, Boolean sendLifeTicks)
        {
            this.port = port;
            this.lifeTickTimeout = lifeTickTimeout;
            if (sendLifeTicks)
                this.lifeTickTimer = new Timer(lifeTick_callback, null, 0, lifeTickTimeout);
        }

        /// <summary>
        /// Start listening
        /// </summary>
        public void Start()
        {
            acceptThread = new Thread(run);
            // start listening in new thread
            acceptThread.Start();
        }

        private void run()
        {
            try
            {
                // prepare the socket
                sock = new Socket(
                        AddressFamily.InterNetwork,
                        SocketType.Stream,
                        ProtocolType.Tcp);
                IPEndPoint ipe = new IPEndPoint(
                        IPAddress.Parse("0.0.0.0"), port);
                sock.Bind(ipe);

                sock.Listen(MAX_CONN);
                log.Debug("Start listening");
                while (true)
                {
                    Socket newConnection = sock.Accept();
                    // new socket is stored in a list of clients
                    ComClass<D> newClient = new ComClass<D>(newConnection, clientResponse);
                    newClient.SendTimeOut(lifeTickTimeout + TIMEOUT_INC);
                    lock (clients)
                    {
                        clients.Add(newClient);
                        log.Debug("Clients: " + clients.Count.ToString());
                    }
                    newClient.StartRead();
                    if (onClientConnect != null)
                        onClientConnect.Invoke(newClient.RemoteAddress);
                }
            }
            catch (Exception ex)
            {
                log.Debug("Exception: " + ex.Message);
            }
        }

        /// <summary>
        /// Send data to all connected clients
        /// </summary>
        /// <param name="data"></param>
        public void SendData(D data)
        {
            lock (clients)
            {
                // first we remove dead clients
                int i = 0;
                while (i < clients.Count)
                {
                    if (clients[i].IsDead)
                    {
                        if (onClientRemove != null)
                            this.onClientRemove.Invoke(clients[i].RemoteAddress);
                        clients.RemoveAt(i);
                    }
                    else
                        i++;
                }
                log.Debug("Clients: " + clients.Count.ToString());
                // after that we send data to all clients
                foreach (ComClass<D> com in clients)
                {
                    Boolean sendData = true;
                    // if we have a handler we check with the handler before sending
                    if (this.onCanSend != null)
                        sendData = this.onCanSend.Invoke(com.RemoteAddress, data);
                    // send
                    if (sendData)
                        com.SendData(data);
                }
            }
        }

        private void lifeTick_callback(Object state)
        {
            log.Debug("Send LifeTick");
            SendData(default(D));
        }

        private void clientResponse(String remoteAddress, String xmlData)
        {
            StringReader sReader = new StringReader(xmlData);
            R sd = (R)responseSerializer.Deserialize(sReader);
            if ((sd != null) && (onResponseRecieved != null))
            {
                // response recieved
                onResponseRecieved.Invoke(remoteAddress, sd);
            }
        }

        public void Dispose()
        {
            if (sock != null)
            {
                // when closing no erros
                try
                {
                    // we close all client sockets
                    foreach (ComClass<D> com in clients)
                    {
                        com.Dispose();
                    }
                    // than we close the listening socket
                    sock.Close();
                }
                catch (Exception) { }
            }
            if (acceptThread != null)
            {
                acceptThread.Abort();
                acceptThread.Join();
            }
        }
    }


    public class ComClass<D> : IDisposable
    {
        private log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);

        public delegate void ClientResponse(String remoteAddress, String xmlMsg);

        private Socket socket;
        private StreamReader m_reader;
        private StreamWriter m_writer;

        private Boolean isDead = false;
        public Boolean IsDead
        {
            get
            {
                return this.isDead;
            }
        }

        private Thread readThread;
        private volatile Boolean reading;

        private XmlSerializer dataSerializer = new XmlSerializer(typeof(D));

        private ClientResponse onClientResponse;

        private String remoteAddress;
        public String RemoteAddress
        {
            get { return this.remoteAddress; }
        }

        public ComClass(Socket socket)
        {
            // we store the remote addres of client
            this.remoteAddress = ((IPEndPoint)socket.RemoteEndPoint).ToString();
            log.Debug("Connected to: " + remoteAddress);
            this.socket = socket;
            this.m_reader = new StreamReader(new NetworkStream(socket), Encoding.Unicode);
            this.m_writer = new StreamWriter(new NetworkStream(socket), Encoding.Unicode);
        }

        public ComClass(Socket socket, ClientResponse onClientResponse)
        {
            // we store the remote addres of client
            this.remoteAddress = ((IPEndPoint)socket.RemoteEndPoint).ToString();
            log.Debug("Connected to: " + remoteAddress);
            this.socket = socket;
            this.m_reader = new StreamReader(new NetworkStream(socket), Encoding.Unicode);
            this.m_writer = new StreamWriter(new NetworkStream(socket), Encoding.Unicode);
            this.onClientResponse = onClientResponse;
        }

        public void SendData(D data)
        {
            // sending of data in custom thread
            Thread senderThread = new Thread(sendInThread);
            senderThread.Start(data);
        }

        public void SendTimeOut(int timeout)
        {
            // sending of data in custom thread
            Thread senderThread = new Thread(sendInThread);
            senderThread.Start(timeout);
        }

        public void StartRead()
        {
            this.readThread = new Thread(readSocket);
            this.readThread.Start();
        }

        private void readSocket()
        {
            this.reading = true;
            while (reading)
            {
                try
                {
                    // reading socket for responses
                    readData();
                }
                catch (System.Threading.ThreadAbortException)
                {
                    log.Debug("ThreadAbort exception");
                    // končamo zanko
                    break;
                }
                catch (Exception)
                {
                }
            }
        }

        private void sendInThread(Object data)
        {
            lock (m_writer)
            {
                try
                {
                    // we are sending timeout
                    if (data is Int32)
                    {
                        // send TIMEOUT=0000000
                        m_writer.WriteLine(Commons.CreateTimeoutString((Int32)data));
                        m_writer.Flush();
                    }
                    else
                        if (data == null)
                        {
                            // sending lifetick just size line
                            m_writer.WriteLine(Commons.CreateSizeString(0));
                            m_writer.Flush();
                        }
                        else
                        {
                            StringBuilder sBuilder = new StringBuilder();
                            StringWriter sWriter = new StringWriter(sBuilder);
                            dataSerializer.Serialize(sWriter, (D)data);
                            // size tag on begining
                            m_writer.WriteLine(Commons.CreateSizeString(sBuilder.Length));
                            m_writer.WriteLine(sBuilder.ToString());
                            m_writer.Flush();
                        }
                }
                catch (Exception ex)
                {
                    log.Debug(ex.Message);
                    this.isDead = true;
                    this.Dispose();
                }
            }
        }

        private void readData()
        {
            // read the data - sync with size message
            int size = -1;
            do
            {
                String message = m_reader.ReadLine();
                size = Commons.ParseSizeMessage(message);
            }
            while (size < 0);

            char[] buf = new char[size];
            StringBuilder sb = new StringBuilder();
            while (sb.Length < size)
            {
                int len = m_reader.Read(buf, 0, size - sb.Length);
                sb.Append(new string(buf, 0, len));
            }

            if (onClientResponse != null)
                onClientResponse.Invoke(this.remoteAddress, sb.ToString());
        }

        #region IDisposable Members

        public void Dispose()
        {
            // stop the reading thread
            this.reading = false;
            if (readThread != null)
                readThread.Abort();
            if (socket != null)
            {
                log.Debug("Drop connection.");
                try
                {
                    m_writer.Close();
                    socket.Close();
                }
                catch (Exception) { }
            }
        }

        #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
Software Developer
Slovenia Slovenia
Senior C# .NET developer in gaming industry

Specialties
C#, XML, WebServices, WCF

Comments and Discussions