Click here to Skip to main content
Click here to Skip to main content
Articles » Languages » C# » General » Downloads
 
Add your own
alternative version

SIP Stack with SIP Proxy - (VOIP)

, 11 Jun 2007 CPOL
C# implementation of SIP
SIP_Proxy_demo.zip
SIP_Proxy_demo
SIP_Proxy_demo.suo
SIP_Proxy_demo
bin
Debug
LumiSoft.Net.dll
SIP_Proxy_demo.exe
SIP_Proxy_demo.vshost.exe
dep
LumiSoft.Net.dll
LumiSoft.Net.pdb
Properties
Settings.settings
Resources
add.ico
app.ico
delete.ico
edit.ico
error.ico
info.ico
refresh.ico
rule.ico
server_running.ico
server_stopped.ico
viewmessages.ico
Stack.zip
Net
docs
dns
dns_records.jpg
dns_records.vsd
Net
_junk
_Obsolete
AUTH
bin
Release
LumiSoft.Net.dll
Data
Dns
Client
FTP
Client
Server
HTTP
Server
ICMP
IMAP
Client
Server
IO
Log
LumiSoft.Net
Mime
vCard
Net.csproj.user
Net.suo
NNTP
Client
POP3
Client
Server
RTP
SDP
ServersCore
SIP
Client
Message
Proxy
Stack
SMTP
Client
Server
STUN
Client
Message
URI
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace LumiSoft.Net
{
	/// <summary>
	/// This is base class for Socket and Session based servers.
	/// </summary>
	public abstract class SocketServer : System.ComponentModel.Component 
	{
        private List<SocketServerSession> m_pSessions           = null;
        private Queue<QueuedConnection>   m_pQueuedConnections  = null;
		private bool                      m_Running             = false;
		private System.Timers.Timer       m_pTimer              = null;
        private BindInfo[]                m_pBindInfo           = null;
		private string                    m_HostName            = "";
		private int                       m_SessionIdleTimeOut  = 30000;
		private int                       m_MaxConnections      = 1000;
		private int                       m_MaxBadCommands      = 8;       
		private bool                      m_LogCmds             = false;

		#region Events declarations

		/// <summary>
		/// Occurs when server or session has system error(unhandled error).
		/// </summary>
		public event ErrorEventHandler SysError = null;

		#endregion

        #region Internal Structs and Classes

        #region struct QueuedConnection

        /// <summary>
        /// This struct holds queued connection info.
        /// </summary>
        private struct QueuedConnection
        {
            private Socket   m_pSocket;
            private BindInfo m_pBindInfo;

            /// <summary>
            /// Default constructor.
            /// </summary>
            /// <param name="socket">Socket.</param>
            /// <param name="bindInfo">Bind info.</param>
            public QueuedConnection(Socket socket,BindInfo bindInfo)
            {
                m_pSocket   = socket;
                m_pBindInfo = bindInfo;
            }

            #region Properties Implementation

            /// <summary>
            /// Gets socket.
            /// </summary>
            public Socket Socket
            {
                get{ return m_pSocket; }
            }

            /// <summary>
            /// Gets bind info.
            /// </summary>
            public BindInfo BindInfo
            {
                get{ return m_pBindInfo; }
            }

            #endregion
        }

        #endregion

        #endregion

        /// <summary>
		/// Default constructor.
		/// </summary>
		public SocketServer()
		{
			m_pSessions          = new List<SocketServerSession>();
			m_pQueuedConnections = new Queue<QueuedConnection>();
			m_pTimer             = new System.Timers.Timer(15000);
			m_pBindInfo          = new BindInfo[]{new BindInfo(IPAddress.Any,10000,false,null)};
			m_HostName           = System.Net.Dns.GetHostName();

			m_pTimer.AutoReset = true;
			m_pTimer.Elapsed += new System.Timers.ElapsedEventHandler(this.m_pTimer_Elapsed);
		}

		#region method Dispose

		/// <summary>
		/// Clean up any resources being used and stops server.
		/// </summary>
		public new void Dispose()
		{
			base.Dispose();

			StopServer();				
		}

		#endregion


		#region method StartServer

		/// <summary>
		/// Starts server.
		/// </summary>
		public void StartServer()
		{
			if(!m_Running){
				m_Running = true;

				// Start accepting ang queueing connections
				Thread tr = new Thread(new ThreadStart(this.StartProcCons));
				tr.Start();

				// Start proccessing queued connections
				Thread trSessionCreator = new Thread(new ThreadStart(this.StartProcQueuedCons));
				trSessionCreator.Start();

				m_pTimer.Enabled = true;
			}
		}

		#endregion

		#region method StopServer

		/// <summary>
		/// Stops server. NOTE: Active sessions aren't cancled.
		/// </summary>
		public void StopServer()
		{
			if(m_Running){
                m_Running = false;

                // Stop accepting new connections
                foreach(BindInfo bindInfo in m_pBindInfo){
                    if(bindInfo.Tag != null){
                        ((Socket)bindInfo.Tag).Close();
                        bindInfo.Tag = null;
                    }
                }

                // Wait method StartProcCons to exit
                Thread.Sleep(100);
			}
		}

		#endregion


		#region method StartProcCons

		/// <summary>
		/// Starts proccessiong incoming connections (Accepts and queues connections).
		/// </summary>
		private void StartProcCons()
		{	
			try{
                CircleCollection<BindInfo> binds = new CircleCollection<BindInfo>();
                foreach(BindInfo bindInfo in m_pBindInfo){
                    Socket s = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
				    s.Bind(new IPEndPoint(bindInfo.IP,bindInfo.Port));
				    s.Listen(500);
                
                    bindInfo.Tag = s;
                    binds.Add(bindInfo);
                }

                // Accept connections and queue them			
				while(m_Running){
					// We have reached maximum connection limit
					if(m_pSessions.Count > m_MaxConnections){
						// Wait while some active connectins are closed
						while(m_pSessions.Count > m_MaxConnections){
							Thread.Sleep(100);
						}
					}

                    // Get incomong connection
                    BindInfo bindInfo = binds.Next();

                    // There is waiting connection
                    if(m_Running && ((Socket)bindInfo.Tag).Poll(0,SelectMode.SelectRead)){
                        // Accept incoming connection
					    Socket s = ((Socket)bindInfo.Tag).Accept();
                                                
        				// Add session to queue
		        		lock(m_pQueuedConnections){
				        	m_pQueuedConnections.Enqueue(new QueuedConnection(s,bindInfo));
					    }
                    }
					
                    Thread.Sleep(2);
				}
			}
			catch(SocketException x){
				// Socket listening stopped, happens when StopServer is called.
				// We need just skip this error.
				if(x.ErrorCode == 10004){			
				}
				else{
					OnSysError("WE MUST NEVER REACH HERE !!! StartProcCons:",x);
				}
			}
			catch(Exception x){
				OnSysError("WE MUST NEVER REACH HERE !!! StartProcCons:",x);
			}
		}

		#endregion
		
		#region method StartProcQueuedCons

		/// <summary>
		/// Starts queueed connections proccessing (Creates and starts session foreach queued connection).
		/// </summary>
		private void StartProcQueuedCons()
		{
			try{
				while(m_Running){
                    // There are queued connections, start sessions.
                    if(m_pQueuedConnections.Count > 0){
                        QueuedConnection connection;
                        lock(m_pQueuedConnections){
                            connection = m_pQueuedConnections.Dequeue();
                        }

                        try{
                            InitNewSession(connection.Socket,connection.BindInfo);
                        }
				        catch(Exception x){
					        OnSysError("StartProcQueuedCons InitNewSession():",x);
					    }
                    }
					// There are no connections to proccess, delay proccessing. We need to it 
					// because if there are no connections to proccess, while loop takes too much CPU.
					else{
						Thread.Sleep(10);
					}
				}
			}
			catch(Exception x){
				OnSysError("WE MUST NEVER REACH HERE !!! StartProcQueuedCons:",x);
			}
		}

		#endregion


		#region method AddSession

		/// <summary>
		/// Adds specified session to sessions collection.
		/// </summary>
		/// <param name="session">Session to add.</param>
		internal protected void AddSession(SocketServerSession session)
		{
			lock(m_pSessions){
				m_pSessions.Add(session);
			}			
		}

		#endregion

		#region method RemoveSession

		/// <summary>
		/// Removes specified session from sessions collection.
		/// </summary>
		/// <param name="session">Session to remove.</param>
		internal protected void RemoveSession(SocketServerSession session)
		{
			lock(m_pSessions){
				m_pSessions.Remove(session);
			}			
		}

		#endregion


		#region method OnSysError

		/// <summary>
		/// 
		/// </summary>
		/// <param name="text"></param>
		/// <param name="x"></param>
		internal protected void OnSysError(string text,Exception x)
		{
			if(this.SysError != null){
				this.SysError(this,new Error_EventArgs(x,new StackTrace()));
			}
		}

		#endregion

		#region method OnSessionTimeoutTimer

		/// <summary>
		/// This method must get timedout sessions and end them.
		/// </summary>
		private void OnSessionTimeoutTimer()
		{
			try{
				// Close/Remove timed out sessions
				lock(m_pSessions){
					SocketServerSession[] sessions = this.Sessions;

					// Loop sessions and and call OnSessionTimeout() for timed out sessions.
					for(int i=0;i<sessions.Length;i++){	                
						// If session throws exception, handle it here or next sessions timouts are not handled.
						try{
							// Session timed out
							if(DateTime.Now > sessions[i].SessionLastDataTime.AddMilliseconds(this.SessionIdleTimeOut)){                                
								sessions[i].OnSessionTimeout();
							}
						}
						catch(Exception x){
							OnSysError("OnTimer:",x);
						}
					}
				}
			}
			catch(Exception x){
				OnSysError("WE MUST NEVER REACH HERE !!! OnTimer:",x);
			}
		}

		#endregion

		#region method m_pTimer_Elapsed

		private void m_pTimer_Elapsed(object sender,System.Timers.ElapsedEventArgs e)
		{	
			OnSessionTimeoutTimer();
		}

		#endregion


		#region virtual method InitNewSession

		/// <summary>
		/// Initialize and start new session here. Session isn't added to session list automatically, 
		/// session must add itself to server session list by calling AddSession().
		/// </summary>
		/// <param name="socket">Connected client socket.</param>
        /// <param name="bindInfo">BindInfo what accepted socket.</param>
		protected virtual void InitNewSession(Socket socket,BindInfo bindInfo)
		{
		}

		#endregion


		#region Properties Implementation

        /// <summary>
        /// Gets or set socket binding info. Use this property to specify on which IP,port server 
        /// listnes and also if is SSL or STARTTLS support.
        /// </summary>
        public BindInfo[] BindInfo
        {
            get{ return m_pBindInfo; }

            set{
                if(value == null){
                    throw new NullReferenceException("BindInfo can't be null !");
                }

                //--- See if bindinfo has changed -----------
                bool changed = false;
                if(m_pBindInfo.Length != value.Length){
                    changed = true;
                }
                else{
                    for(int i=0;i<m_pBindInfo.Length;i++){
                        if(!m_pBindInfo[i].Equals(value[i])){
                            changed = true;
                            break;
                        }
                    }
                }
                //-------------------------------------------

                if(changed){
                    // If server is currently running, stop it before applying bind info.
                    bool running = m_Running;
                    if(running){
                        StopServer();
                    }
     
                    m_pBindInfo = value; 

                    // We need to restart server to take effect IP or Port change
				    if(running){				    
					    StartServer();
				    }
                }
            }
        }

		/// <summary>
		/// Gets or sets maximum allowed connections.
		/// </summary>
		public int MaxConnections 
		{
			get{ return m_MaxConnections; }

			set{ m_MaxConnections = value; }
		}
        
		/// <summary>
		/// Runs and stops server.
		/// </summary>
		public bool Enabled 
		{
			get{ return m_Running; }

			set{				
				if(value != m_Running & !this.DesignMode){
					if(value){
						StartServer();
					}
					else{
						StopServer();
					}
				}
			}
		}
	
		/// <summary>
		/// Gets or sets if to log commands.
		/// </summary>
		public bool LogCommands
		{
			get{ return m_LogCmds; }

			set{ m_LogCmds = value; }
		}

		/// <summary>
		/// Session idle timeout in milliseconds.
		/// </summary>
		public int SessionIdleTimeOut 
		{
			get{ return m_SessionIdleTimeOut; }

			set{ m_SessionIdleTimeOut = value; }
		}
				
		/// <summary>
		/// Gets or sets maximum bad commands allowed to session.
		/// </summary>
		public int MaxBadCommands
		{
			get{ return m_MaxBadCommands; }

			set{ m_MaxBadCommands = value; }
		}

		/// <summary>
		/// Gets or set host name that is reported to clients.
		/// </summary>
		public string HostName
		{
			get{ return m_HostName; }

			set{
				if(value.Length > 0){
					m_HostName = value;
				}
			}
		}

		/// <summary>
		/// Gets active sessions.
		/// </summary>
		public SocketServerSession[] Sessions
		{
			get{ return m_pSessions.ToArray(); }
		}

		#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)

Share

About the Author

Ivar Lumi

Estonia Estonia
No Biography provided

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141223.1 | Last Updated 11 Jun 2007
Article Copyright 2007 by Ivar Lumi
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid