Quantcast
Channel: Implementation of an asynchronous TCP/UDP server - Code Review Stack Exchange
Viewing all articles
Browse latest Browse all 3

Implementation of an asynchronous TCP/UDP server

0
0

I am trying to implement a TCP/UDP server so all I have to do is something like this:

var server = new Server(Type.UDP, "127.0.0.1", 8888);server.OnDataRecieved += Datahandler;server.Start();

I have tried to make it perform as fast as possible by using Asynchronous calls where possible.

I basically would like to know if there is anything missing/any changes that people would recommend (and why). It is not finished yet as I need to handle exceptions better etc...

TODO: I need to complete the signature of the events to make them more meaningful, etc.

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Net;using System.Net.Sockets;using System.Threading;namespace SBlackler.Networking{public sealed class HighPerformanceServer{    private Int32 _currentConnections = 0;    Socket listener;    EndPoint ipeSender;    #region "Properties"    public Int32 Port { get; set; }    public Int32 CurrentConnections { get { return _currentConnections; } }    public Int32 MaxQueuedConnections { get; set; }    public IPEndPoint Endpoint { get; set; }    public ServerType Type { get; set; }    #endregion    #region "Constructors"    private HighPerformanceServer()    {        // do nothing    }    public HighPerformanceServer(ServerType type, String IpAddress)    {        Init(type, IpAddress, 28930);    }    public HighPerformanceServer(ServerType type, String IpAddress, Int32 Port)    {        Init(type, IpAddress, Port);    }    private void Init(ServerType server, String IpAddress, Int32 Port)    {        IPAddress ip;        // Check the IpAddress to make sure that it is valid        if (!String.IsNullOrEmpty(IpAddress) && IPAddress.TryParse(IpAddress, out ip))        {            this.Endpoint = new IPEndPoint(ip, Port);            // Make sure that the port is greater than 100 as not to conflict with any other programs            if (Port < 100)            {                throw new ArgumentException("The argument 'Port' is not valid. Please select a value greater than 100.");            }            else            {                this.Port = Port;            }        }        else        {            throw new ArgumentException("The argument 'IpAddress' is not valid");        }        // We never want a ServerType of None, but we include it as it is recommended by FXCop.        if (server != ServerType.None)        {            this.Type = server;        }        else        {            throw new ArgumentException("The argument 'ServerType' is not valid");        }    }    #endregion    #region "Events"    public event EventHandler<EventArgs> OnServerStart;    public event EventHandler<EventArgs> OnServerStarted;    public event EventHandler<EventArgs> OnServerStopping;    public event EventHandler<EventArgs> OnServerStoped;    public event EventHandler<EventArgs> OnClientConnected;    public event EventHandler<EventArgs> OnClientDisconnecting;    public event EventHandler<EventArgs> OnClientDisconnected;    public event EventHandler<EventArgs> OnDataReceived;    #endregion    public void Start()    {        // Tell anything that is listening that we have starting to work        if (OnServerStart != null)        {            OnServerStart(this, null);        }        // Get either a TCP or UDP socket depending on what we specified when we created the class        listener = GetCorrectSocket();        if (listener != null)        {            // Bind the socket to the endpoint            listener.Bind(this.Endpoint);            // TODO :: Add throttleling (using SEMAPHORE's)            if (this.Type == ServerType.TCP)            {                // Start listening to the socket, accepting any backlog                listener.Listen(this.MaxQueuedConnections);                // Use the BeginAccept to accept new clients                listener.BeginAccept(new AsyncCallback(ClientConnected), listener);            }            else if (this.Type == ServerType.UDP)            {                // So we can buffer and store information, create a new information class                SocketConnectionInfo connection = new SocketConnectionInfo();                connection.Buffer = new byte[SocketConnectionInfo.BufferSize];                connection.Socket = listener;                // Setup the IPEndpoint                ipeSender = new IPEndPoint(IPAddress.Any, this.Port);                // Start recieving from the client                listener.BeginReceiveFrom(connection.Buffer, 0, connection.Buffer.Length, SocketFlags.None, ref ipeSender, new AsyncCallback(DataReceived), connection);            }            // Tell anything that is listening that we have started to work            if (OnServerStarted != null)            {                OnServerStarted(this, null);            }        }        else        {            // There was an error creating the correct socket            throw new InvalidOperationException("Could not create the correct sever socket type.");        }    }    internal Socket GetCorrectSocket()    {        if (this.Type == ServerType.TCP)        {            return new Socket(this.Endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);        }        else if (this.Type == ServerType.UDP)        {            return new Socket(this.Endpoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);        }        else        {            return null;        }    }    public void Stop()    {        if (OnServerStopping != null)        {            OnServerStopping(this, null);        }        if (OnServerStoped != null)        {            OnServerStoped(this, null);        }    }    internal void ClientConnected(IAsyncResult asyncResult)    {        // Increment our ConcurrentConnections counter        Interlocked.Increment(ref _currentConnections);        // So we can buffer and store information, create a new information class        SocketConnectionInfo connection = new SocketConnectionInfo();        connection.Buffer = new byte[SocketConnectionInfo.BufferSize];        // We want to end the async event as soon as possible        Socket asyncListener = (Socket)asyncResult.AsyncState;        Socket asyncClient = asyncListener.EndAccept(asyncResult);        // Set the SocketConnectionInformations socket to the current client        connection.Socket = asyncClient;        // Tell anyone that's listening that we have a new client connected        if (OnClientConnected != null)        {            OnClientConnected(this, null);        }        // TODO :: Add throttleling (using SEMAPHORE's)        // Begin recieving the data from the client        if (this.Type == ServerType.TCP)        {            asyncClient.BeginReceive(connection.Buffer, 0, connection.Buffer.Length, SocketFlags.None, new AsyncCallback(DataReceived), connection);        }        else if (this.Type == ServerType.UDP)        {            asyncClient.BeginReceiveFrom(connection.Buffer, 0, connection.Buffer.Length, SocketFlags.None, ref ipeSender, new AsyncCallback(DataReceived), connection);        }        // Now we have begun recieving data from this client,        // we can now accept a new client        listener.BeginAccept(new AsyncCallback(ClientConnected), listener);    }    internal void DataReceived(IAsyncResult asyncResult)    {        try        {            SocketConnectionInfo connection = (SocketConnectionInfo)asyncResult.AsyncState;            Int32 bytesRead;            // End the correct async process            if (this.Type == ServerType.UDP)            {                bytesRead = connection.Socket.EndReceiveFrom(asyncResult, ref ipeSender);            }            else if (this.Type == ServerType.TCP)            {                bytesRead = connection.Socket.EndReceive(asyncResult);            }            else            {                bytesRead = 0;            }            // Increment the counter of BytesRead            connection.BytesRead += bytesRead;            // Check to see whether the socket is connected or not...            if (IsSocketConnected(connection.Socket))            {                // If we have read no more bytes, raise the data received event                if (bytesRead == 0 || (bytesRead > 0 && bytesRead < SocketConnectionInfo.BufferSize))                {                    byte[] buffer = connection.Buffer;                    Int32 totalBytesRead = connection.BytesRead;                    // Setup the connection info again ready for another packet                    connection = new SocketConnectionInfo();                    connection.Buffer = new byte[SocketConnectionInfo.BufferSize];                    connection.Socket = ((SocketConnectionInfo)asyncResult.AsyncState).Socket;                    // Fire off the receive event as quickly as possible, then we can process the data...                    if (this.Type == ServerType.UDP)                    {                        connection.Socket.BeginReceiveFrom(connection.Buffer, 0, connection.Buffer.Length, SocketFlags.None, ref ipeSender, new AsyncCallback(DataReceived), connection);                    }                    else if (this.Type == ServerType.TCP)                    {                        connection.Socket.BeginReceive(connection.Buffer, 0, connection.Buffer.Length, SocketFlags.None, new AsyncCallback(DataReceived), connection);                    }                    // Remove any extra data                    if (totalBytesRead < buffer.Length)                    {                        Array.Resize<Byte>(ref buffer, totalBytesRead);                    }                    // Now raise the event, sender will contain the buffer for now                    if (OnDataReceived != null)                    {                        OnDataReceived(buffer, null);                    }                    buffer = null;                }                else                {                    // Resize the array ready for the next chunk of data                    Array.Resize<Byte>(ref connection.Buffer, connection.Buffer.Length + SocketConnectionInfo.BufferSize);                    // Fire off the receive event again, with the bigger buffer                    if (this.Type == ServerType.UDP)                    {                        connection.Socket.BeginReceiveFrom(connection.Buffer, 0, connection.Buffer.Length, SocketFlags.None, ref ipeSender, new AsyncCallback(DataReceived), connection);                    }                    else if (this.Type == ServerType.TCP)                    {                        connection.Socket.BeginReceive(connection.Buffer, 0, connection.Buffer.Length, SocketFlags.None, new AsyncCallback(DataReceived), connection);                    }                }            }            else if(connection.BytesRead > 0)            {                // We still have data                Array.Resize<Byte>(ref connection.Buffer, connection.BytesRead);                // call the event                if (OnDataReceived != null)                {                    OnDataReceived(connection.Buffer, null);                }            }        }        catch (Exception ex)        {            Console.WriteLine(ex.Message);        }    }    internal bool IsSocketConnected(Socket socket)    {        return !(socket.Poll(1, SelectMode.SelectRead) && socket.Available == 0);    }    internal void DisconnectClient(SocketConnectionInfo connection)    {        if (OnClientDisconnecting != null)        {            OnClientDisconnecting(this, null);        }        connection.Socket.BeginDisconnect(true, new AsyncCallback(ClientDisconnected), connection);    }    internal void ClientDisconnected(IAsyncResult asyncResult)    {        SocketConnectionInfo sci = (SocketConnectionInfo)asyncResult;        sci.Socket.EndDisconnect(asyncResult);        if (OnClientDisconnected != null)        {            OnClientDisconnected(this, null);        }    }}public class SocketConnectionInfo{    public const Int32 BufferSize = 1048576;    public Socket Socket;    public byte[] Buffer;    public Int32 BytesRead { get; set; }}public enum ServerType{    None = 0,    TCP = 1,    UDP = 2}}

Viewing all articles
Browse latest Browse all 3

Latest Images

Trending Articles





Latest Images