diff Net/TCPServer.cs @ 0:3ab940a0c7a0

Initial commit
author Ivo Smits <Ivo@UCIS.nl>
date Tue, 11 Sep 2012 16:28:53 +0200
parents
children 4ba4fd48e1da
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Net/TCPServer.cs	Tue Sep 11 16:28:53 2012 +0200
@@ -0,0 +1,235 @@
+using System;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading;
+using System.Collections.Generic;
+
+namespace UCIS.Net {
+	public class TCPServer {
+		public class ModuleCollection : Dictionary<byte, IModule> {
+			public void Add(char Key, IModule Value) {
+				base.Add((byte)Key, Value);
+			}
+			public void Remove(char Key) {
+				base.Remove((byte)Key);
+			}
+		}
+
+		public class ClientAcceptedEventArgs : EventArgs {
+			private TCPStream _client;
+			internal ClientAcceptedEventArgs(TCPStream client) {
+				_client = client;
+			}
+			public TCPStream Client { get { return _client; } }
+		}
+
+		public event EventHandler<ClientAcceptedEventArgs> ClientAccepted;
+
+		private Socket _Listener;
+		private UCIS.ThreadPool _ThreadPool;
+		private NetworkConnectionList _Clients = new NetworkConnectionList();
+		private ModuleCollection _Modules = new ModuleCollection();
+		private IModule _CatchAllModule = null;
+		private Int32 _ThrottleCounter;
+		private Int32 _ThrottleBurst = 10;
+		private Int32 _ThrottleRate = 0;
+		private Timer _ThrottleTimer = null;
+
+		public TCPServer() {
+			_ThreadPool = UCIS.ThreadPool.DefaultPool;
+		}
+
+		public NetworkConnectionList Clients {
+			get { return _Clients; }
+		}
+
+		public ModuleCollection Modules {
+			get { return _Modules; }
+		}
+
+		public IModule CatchAllModule {
+			get {
+				return _CatchAllModule;
+			}
+			set {
+				_CatchAllModule = value;
+			}
+		}
+
+		public Int32 ThrottleRate {
+			get { return _ThrottleRate; }
+			set {
+				if (value < 0) throw new ArgumentOutOfRangeException("value");
+				_ThrottleRate = value;
+				if (_Listener == null) return;
+				if (value == 0 && _ThrottleTimer != null) {
+					_ThrottleTimer.Dispose();
+					_ThrottleTimer = null;
+				} else if (value > 0 && _ThrottleTimer == null) {
+					_ThrottleTimer = new Timer(ThrottleCallback, null, 1000, 1000);
+				}
+			}
+		}
+		public Int32 ThrottleBurst {
+			get { return _ThrottleBurst; }
+			set {
+				if (value < 1) throw new ArgumentOutOfRangeException("value");
+				_ThrottleBurst = value;
+			}
+		}
+
+		public EndPoint LocalEndPoint { get { return _Listener.LocalEndPoint; } }
+
+		public void Listen(int Port) {
+			Listen(AddressFamily.InterNetwork, Port);
+		}
+		public void Listen(AddressFamily af, int Port) {
+			Stop();
+
+			_Listener = new Socket(af, SocketType.Stream, ProtocolType.Tcp);
+
+			_Listener.Blocking = false;
+			_Listener.Bind(new IPEndPoint(af == AddressFamily.InterNetworkV6 ? IPAddress.IPv6Any : IPAddress.Any, Port));
+			_Listener.Listen(25);
+			_ThrottleCounter = _ThrottleBurst;
+			_Listener.BeginAccept(AcceptCallback, null);
+
+			if (_ThrottleRate > 0) {
+				_ThrottleTimer = new Timer(ThrottleCallback, null, 1000, 1000);
+			}
+		}
+
+		public void Stop() {
+			if (_ThrottleTimer != null) _ThrottleTimer.Dispose();
+			if (_Listener != null && _Listener.IsBound) _Listener.Close();
+			_Listener = null;
+		}
+
+		public void Close() {
+			Close(true);
+		}
+		public void Close(bool CloseClients) {
+			Stop();
+			if (CloseClients) {
+				_Clients.CloseAll();
+				if (_Clients.Count > 0) Console.WriteLine("TCPServer.Close: Warning: " + _Clients.Count.ToString() + " connections were not properly terminated.");
+			} else {
+				_Clients.Clear();
+			}
+		}
+
+		private void AcceptCallback(System.IAsyncResult ar) {
+			Client Client = null;
+			Socket Socket = null;
+			if (_Listener == null) return;
+			try {
+				Socket = _Listener.EndAccept(ar);
+			} catch (ObjectDisposedException) {
+				Console.WriteLine("TCPServer.AcceptCallback Listener object has been disposed. Aborting.");
+				return;
+			} catch (SocketException ex) {
+				Console.WriteLine("TCPServer.AcceptCallback SocketException: " + ex.Message);
+				Socket = null;
+			}
+			if (Socket != null) {
+				try {
+					Client = new Client(Socket, this);
+					_Clients.Add(Client);
+					if (ClientAccepted != null) ClientAccepted(this, new ClientAcceptedEventArgs(Client));
+					Client.Start(_ThreadPool);
+				} catch (Exception ex) {
+					Console.WriteLine(ex.ToString());
+				}
+			}
+			if (_ThrottleCounter > 0 || _ThrottleRate == 0) {
+				_ThrottleCounter--;
+				_Listener.BeginAccept(AcceptCallback, null);
+			}
+		}
+
+		private void ThrottleCallback(Object state) {
+			if (_Listener == null) return;
+			if (_ThrottleRate == 0) return;
+			if (_ThrottleCounter >= _ThrottleBurst) return;
+			if (_ThrottleCounter <= 0) {
+				_Listener.BeginAccept(AcceptCallback, null);
+			}
+			_ThrottleRate += _ThrottleRate;
+		}
+
+		public interface IModule {
+			// Return value: True = Close connection, False = keep alive
+			bool Accept(TCPStream Stream);
+		}
+
+		private class Client : TCPStream {
+			private TCPServer _Server;
+			private UCIS.ThreadPool.WorkItem _WorkItem;
+			private IModule _Module;
+			private byte _MagicNumber;
+
+			public TCPServer Server {
+				get { return _Server; }
+			}
+			public IModule Module {
+				get { return _Module; }
+			}
+
+			private void _Stream_Closed(object sender, EventArgs e) {
+				_WorkItem = null;
+				_Module = null;
+				_Server = null;
+				base.Closed -= _Stream_Closed;
+			}
+
+			internal Client(Socket Socket, TCPServer Server) : base(Socket) {
+				_Server = Server;
+				Socket.Blocking = true;
+				base.Closed += _Stream_Closed;
+				this.Tag = Server;
+			}
+
+			internal void Start(UCIS.ThreadPool Pool) {
+				_WorkItem = Pool.QueueWorkItem(WorkerProc, null);
+			}
+
+			private void WorkerProc(object state) {
+				bool CloseSocket = true;
+				try {
+					try {
+						base.Blocking = true;
+						//base.NoDelay = true;
+						base.ReadTimeout = 5000;
+						base.WriteBufferSize = 1024 * 10;
+						base.ReadBufferSize = 1024 * 10;
+						//Console.WriteLine("TCPServer: Accepted connection from " + base.Socket.RemoteEndPoint.ToString());
+						_MagicNumber = (byte)base.PeekByte();
+					} catch (TimeoutException ex) {
+						Console.WriteLine("TCPServer: Caught TimeoutException while reading magic number: " + ex.Message);
+						return;
+					}
+					if (_Server._Modules.TryGetValue(_MagicNumber, out _Module)) {
+						this.Tag = _Module;
+						CloseSocket = _Module.Accept(this);
+					} else if (_Server._CatchAllModule != null) {
+						this.Tag = _Server._CatchAllModule;
+						CloseSocket = _Server._CatchAllModule.Accept(this);
+					} else {
+						this.Tag = this;
+						Console.WriteLine("TCPServer: Unknown magic number: " + _MagicNumber.ToString());
+					}
+				} catch (ThreadAbortException) {
+					Console.WriteLine("TCPServer: Caught ThreadAbortException");
+				} catch (SocketException ex) {
+					Console.WriteLine("TCPServer: Caught SocketException: " + ex.Message);
+				} catch (Exception ex) {
+					Console.WriteLine("TCPServer: Caught Exception: " + ex.ToString());
+				} finally {
+					try {
+						if (CloseSocket) base.Close();
+					} catch { }
+				}
+			}
+		}
+	}
+}