Click here to Skip to main content
15,894,017 members
Articles / Programming Languages / C#

An Asynchronous Socket Server and Client

Rate me:
Please Sign up or sign in to vote.
4.89/5 (265 votes)
29 Apr 2009CPOL16 min read 4M   25.6K   1.1K  
An asynchronous socket server and client with encryption and compression.
/* ====================================================================
 * Copyright (c) 2009 Andre Luis Azevedo (az.andrel@yahoo.com.br)
 * All rights reserved.
 *                       
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *    In addition, the source code must keep original namespace names.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution. In addition, the binary form must keep the original 
 *    namespace names and original file name.
 * 
 * 3. The name "ALAZ" or "ALAZ Library" must not be used to endorse or promote 
 *    products derived from this software without prior written permission.
 *
 * 4. Products derived from this software may not be called "ALAZ" or
 *    "ALAZ Library" nor may "ALAZ" or "ALAZ Library" appear in their 
 *    names without prior written permission of the author.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE. 
 */

using System;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Threading;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Web;

namespace ALAZ.SystemEx.NetEx.SocketsEx
{

    #region Delegates

    public delegate void OnDisconnectEvent();
    public delegate void OnSymmetricAuthenticateEvent(ISocketConnection connection, out RSACryptoServiceProvider serverKey);
    public delegate void OnSSLClientAuthenticateEvent(ISocketConnection connection, out string ServerName, ref X509Certificate2Collection certs, ref bool checkRevocation);
    public delegate void OnSSLServerAuthenticateEvent(ISocketConnection connection, out X509Certificate2 certificate, out bool clientAuthenticate, ref bool checkRevocation);

    #endregion

    #region Exceptions

    /// <summary>
    /// Reconnect attempted exception.
    /// </summary>
    public class ReconnectAttemptException : Exception
    {
        private int FAttempt;
        private bool FMaxReached;
        private IBaseSocketConnectionCreator FCreator;

        public ReconnectAttemptException(string message, IBaseSocketConnectionCreator creator, Exception innerException, int attempt, bool maxReached)
            : base(message, innerException)
        {
            FAttempt = attempt;
            FMaxReached = maxReached;
            FCreator = creator;
        }

        public int Attempt
        {
            get { return FAttempt; }
        }

        public bool MaxReached
        {
            get { return FMaxReached; }
        }

        public IBaseSocketConnectionCreator Creator
        {
            get { return FCreator; }
        }

    }

    /// <summary>
    /// Bad Delimiter.
    /// </summary>
    public class BadDelimiterException : Exception
    {
        public BadDelimiterException(string message) : base(message) { }
    }

    /// <summary>
    /// Message length is greater than the maximum value.
    /// </summary>
    public class MessageLengthException : Exception
    {
        public MessageLengthException(string message): base(message) { }
    }

    /// <summary>
    /// Symmetric authentication failure.
    /// </summary>
    public class SymmetricAuthenticationException: Exception
    {
        public SymmetricAuthenticationException(string message) : base(message) { } 
    }

    /// <summary>
    /// SSL authentication failure.
    /// </summary>
    public class SSLAuthenticationException : Exception
    {
        public SSLAuthenticationException(string message) : base(message) { }
    }

    /// <summary>
    /// Proxy authentication failure.
    /// </summary>
    public class ProxyAuthenticationException :  HttpException
    {

      public ProxyAuthenticationException(int code, string message) : base(code, message) { }

    }


    #endregion 

    #region Structures

    #region AuthMessage

    [Serializable]
    public class AuthMessage
    {
        public byte[] SessionKey;
        public byte[] SessionIV;
        public byte[] ClientKey;
        public byte[] Data;
        public byte[] Sign;
    }

    #endregion

    #endregion

    #region Enums

    #region CallbackThreadType

    public enum CallbackThreadType
    {
        ctWorkerThread,
        ctIOThread
    }

    #endregion

    #region HostType

    /// <summary>
    /// Defines the host type.
    /// </summary>
    public enum HostType
    {
        htServer,
        htClient
    }

    #endregion

    #region EncryptType

    /// <summary>
    /// Defines the encrypt method used.
    /// </summary>
    public enum EncryptType
    {
        etNone,
        etRijndael,
        etSSL
    }

    #endregion

    #region CompressionType

    /// <summary>
    /// Defines the compression method used.
    /// </summary>
    public enum CompressionType
    {
        ctNone,
        ctGZIP
    }

    #endregion

    #region DelimiterType

    /// <summary>
    /// Defines message delimiter type.
    /// </summary>
    public enum DelimiterType
    {
        dtNone,
        dtMessageTailExcludeOnReceive,
        dtMessageTailIncludeOnReceive
    }

    #endregion

    #region EventProcessing

    internal enum EventProcessing
    {
        epNone,
        epProxy,
        epEncrypt,
        epUser
    }

    #endregion

    #region ProxyType

    /// <summary>
    /// Defines the proxy host type.
    /// </summary>
    public enum ProxyType
    {
      ptSOCKS4,
      ptSOCKS4a,
      ptSOCKS5,
      ptHTTP
    }

    #endregion

    #region SOCKS5AuthMode
		 
    /// <summary>
    /// Defines the SOCK5 authentication mode.
    /// </summary>
    internal enum SOCKS5AuthMode
    {
      saNoAuth = 0,
      ssUserPass = 2
    }

  	#endregion

    #region SOCKS5Phase
		 
	  /// <summary>
    /// Defines the SOCKS5 authentication phase
    /// </summary>
    internal enum SOCKS5Phase
    {

      spIdle,
      spGreeting,
      spAuthenticating,
      spConnecting

    }

    #endregion
  
    #endregion

    #region Interfaces

    #region IBaseSocketConnectionCreator

    public interface IBaseSocketConnectionCreator
    {

        string Name
        {
            get;
        }

        CompressionType CompressionType
        {
            get;
        }

        EncryptType EncryptType
        {
            get;
        }

    }

    #endregion

    #region IBaseSocketConnectionHost

    public interface IBaseSocketConnectionHost
    {

        int SocketBufferSize
        {
            get;
        }

        int MessageBufferSize
        {
            get;
        }

        byte[] Delimiter
        {
            get;
        }

        DelimiterType DelimiterType
        {
            get;
        }

        int IdleCheckInterval
        {
            get;
        }

        int IdleTimeOutValue
        {
            get;
        }

        HostType HostType
        {
            get;
        }

    }

    #endregion

    #region ISocketConnection

    #region ISocketConnectionInfo

    public interface ISocketConnectionInfo
    {

        /// <summary>
        /// Connection user data.
        /// </summary>
        object UserData
        {
            get;
            set;
        }

        /// <summary>
        /// Connection Session Id.
        /// </summary>
        long ConnectionId
        {
            get;
        }

        /// <summary>
        /// Connection Creator object.
        /// </summary>
        IBaseSocketConnectionCreator Creator
        {
            get;
        }

        /// <summary>
        /// Connection Host object.
        /// </summary>
        IBaseSocketConnectionHost Host
        {
            get;
        }

        /// <summary>
        /// Handle of the OS Socket.
        /// </summary>
        IntPtr SocketHandle
        {
            get;
        }

        /// <summary>
        /// Local socket endpoint.
        /// </summary>
        IPEndPoint LocalEndPoint
        {
            get;
        }

        /// <summary>
        /// Remote socket endpoint.
        /// </summary>
        IPEndPoint RemoteEndPoint
        {
            get;
        }

        DateTime LastAction
        {
            get;
        }

        long ReadBytes
        {
            get;
        }

        long WriteBytes
        {
            get;
        }
        
    }
    
    #endregion

    #region ISocketConnection

    /// <summary>
    /// Common connection properties and methods.
    /// </summary>
    public interface ISocketConnection : ISocketConnectionInfo
    {

        /// <summary>
        /// Set Socket Time To Live option
        /// </summary>
        /// <param name="value">
        /// Value for TTL in seconds
        /// </param>
        void SetTTL(short value);
        /// <summary>
        /// Set Socket Linger option.
        /// </summary>
        /// <param name="lo">
        /// LingerOption value to be set
        /// </param>
        void SetLinger(LingerOption lo);
        /// <summary>
        /// Set Socket Nagle algoritm.
        /// </summary>
        /// <param name="value">
        /// Enable/Disable value
        /// </param>
        void SetNagle(bool value);
      
        /// <summary>
        /// Represents the connection as a IClientSocketConnection.
        /// </summary>
        /// <returns>
        /// 
        /// </returns>
        IClientSocketConnection AsClientConnection();

        /// <summary>
        /// Represents the connection as a IServerSocketConnection.
        /// </summary>
        /// <returns></returns>
        IServerSocketConnection AsServerConnection();

        /// <summary>
        /// Get the connection from the connectionId.
        /// </summary>
        /// <param name="connectionId">
        /// The connectionId.
        /// </param>
        /// <returns>
        /// ISocketConnection to use.
        /// </returns>
        ISocketConnection GetConnectionById(long connectionId);

        /// <summary>
        /// Get all the connections.
        /// </summary>
        ISocketConnection[] GetConnections();

        /// <summary>
        /// Begin send data.
        /// </summary>
        /// <param name="buffer">
        /// Data to be sent.
        /// </param>
        void BeginSend(byte[] buffer);

        /// <summary>
        /// Begin receive the data.
        /// </summary>
        void BeginReceive();

        /// <summary>
        /// Begin disconnect the connection.
        /// </summary>
        void BeginDisconnect();

    }

    #endregion

    #region IClientSocketConnection

    /// <summary>
    /// Client connection methods.
    /// </summary>
    public interface IClientSocketConnection: ISocketConnection
    {

        /// <summary>
        /// Proxy information.
        /// </summary>
        ProxyInfo ProxyInfo
        {
          get;
        }

        /// <summary>
        /// Begin reconnect the connection.
        /// </summary>
        void BeginReconnect();
    }

    #endregion

    #region IServerSocketConnection

    /// <summary>
    /// Server connection methods.
    /// </summary>
    public interface IServerSocketConnection: ISocketConnection
    {

        /// <summary>
        /// Begin send data to all server connections.
        /// </summary>
        /// <param name="buffer">
        /// Data to be sent.
        /// </param>
        /// <param name="includeMe">
        /// Includes the current connection in send�s loop
        /// </param>
        void BeginSendToAll(byte[] buffer, bool includeMe);

        /// <summary>
        /// Begin send data to the connection.
        /// </summary>
        /// <param name="connection">
        /// The connection that the data will be sent.
        /// </param>
        /// <param name="buffer">
        /// Data to be sent.
        /// </param>
        void BeginSendTo(ISocketConnection connection, byte[] buffer);

    }

    #endregion

    #endregion

    #region ISocketService

    /// <summary>
    /// Socket service methods.
    /// </summary>
    public interface ISocketService
    {
        /// <summary>
        /// Fired when connected.
        /// </summary>
        /// <param name="e">
        /// Information about the connection.
        /// </param>
        void OnConnected(ConnectionEventArgs e);

        /// <summary>
        /// Fired when data arrives.
        /// </summary>
        /// <param name="e">
        /// Information about the Message.
        /// </param>
        void OnReceived(MessageEventArgs e);

        /// <summary>
        /// Fired when data is sent.
        /// </summary>
        /// <param name="e">
        /// Information about the Message.
        /// </param>
        void OnSent(MessageEventArgs e);

        /// <summary>
        /// Fired when disconnected.
        /// </summary>
        /// <param name="e">
        /// Information about the connection.
        /// </param>
        void OnDisconnected(ConnectionEventArgs e);

        /// <summary>
        /// Fired when exception occurs.
        /// </summary>
        /// <param name="e">
        /// Information about the exception and connection.
        /// </param>
        void OnException(ExceptionEventArgs e);

    }

    #endregion

    #region ICryptoService

    /// <summary>
    /// Crypto service methods.
    /// </summary>
    public interface ICryptoService
    {
        
        /// <summary>
        /// Fired when symmetric encryption is used.
        /// </summary>
        /// <param name="serverKey">
        /// The RSA provider used to encrypt symmetric IV and Key.
        /// </param>
        void OnSymmetricAuthenticate(ISocketConnection connection, out RSACryptoServiceProvider serverKey);

        /// <summary>
        /// Fired when SSL encryption is used in client host.
        /// </summary>
        /// <param name="ServerName">
        /// The host name in certificate.
        /// </param>
        /// <param name="certs">
        /// The certification collection to be used (null if not using client certification).
        /// </param>
        /// <param name="checkRevocation">
        /// Indicates if the certificated must be checked for revocation.
        /// </param>
        void OnSSLClientAuthenticate(ISocketConnection connection, out string ServerName, ref X509Certificate2Collection certs, ref bool checkRevocation);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="connection"></param>
        /// <param name="serverCertificate"></param>
        /// <param name="chain"></param>
        /// <param name="sslPolicyErrors"></param>
        /// <param name="acceptCertificate"></param>
        void OnSSLClientValidateServerCertificate(X509Certificate serverCertificate, X509Chain chain, SslPolicyErrors sslPolicyErrors, out bool acceptCertificate);

        /// <summary>
        /// Fired when SSL encryption is used in server host.
        /// </summary>
        /// <param name="certificate">
        /// The certificate to be used.
        /// </param>
        /// <param name="clientAuthenticate">
        /// Indicates if client connection will be authenticated (uses certificate).
        /// </param>
        /// <param name="checkRevocation">
        /// Indicates if the certificated must be checked for revocation.
        /// </param>
        void OnSSLServerAuthenticate(ISocketConnection connection, out X509Certificate2 certificate, out bool clientAuthenticate, ref bool checkRevocation);

    }

    #endregion

    #endregion

    #region Classes

    #region SocketConnection

    public class SocketConnection
    { 

        public int ConnectionId;
        public IPAddress LocalAddress;
        public IPAddress RemoteAddress;
    
    }

    #endregion

    #region BaseSocketService

    /// <summary>
    /// Base class for ISocketServive. Use it overriding the virtual methods.
    /// </summary>
    public abstract class BaseSocketService : ISocketService
    {

        #region ISocketService Members

        public virtual void OnConnected(ConnectionEventArgs e) { }
        public virtual void OnSent(MessageEventArgs e) { }
        public virtual void OnReceived(MessageEventArgs e) { }
        public virtual void OnDisconnected(ConnectionEventArgs e) { }
        public virtual void OnException(ExceptionEventArgs e) { }

        #endregion

    }

    #endregion

    #region BaseCryptoService

    /// <summary>
    /// Base class for ICryptoServive. Use it overriding the virtual methods.
    /// </summary>
    public abstract class BaseCryptoService : ICryptoService
    {

        #region ICryptoService Members

        public virtual void OnSymmetricAuthenticate(ISocketConnection connection, out RSACryptoServiceProvider serverKey)
        {

            serverKey = new RSACryptoServiceProvider();
            serverKey.Clear();
        }

        public virtual void OnSSLClientAuthenticate(ISocketConnection connection, out string serverName, ref X509Certificate2Collection certs, ref bool checkRevocation)
        {
            serverName = String.Empty;
        }

        public virtual void OnSSLServerAuthenticate(ISocketConnection connection, out X509Certificate2 certificate, out bool clientAuthenticate, ref bool checkRevocation)
        {
            certificate = new X509Certificate2();
            clientAuthenticate = true;
        }

        public virtual void OnSSLClientValidateServerCertificate(X509Certificate serverCertificate, X509Chain chain, SslPolicyErrors sslPolicyErrors, out bool acceptCertificate)
        {
            acceptCertificate = false;    
        }

        #endregion

    }

    #endregion

    #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 (Senior)
Brazil Brazil
- Living in São Paulo, Brazil
- Developing since 1994 with
* Clipper (Summer '87 and 5.02)
* FoxPro (DOS, 2.6), Visual Foxpro (6, 7, 8, 9)
* Delphi (1, 2, 5, 7, 2007)
* C# (2.0, 4.0)

Comments and Discussions