comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:3ab940a0c7a0
1 using System;
2 using System.Net;
3 using System.Net.Sockets;
4 using System.Threading;
5 using System.Collections.Generic;
6
7 namespace UCIS.Net {
8 public class TCPServer {
9 public class ModuleCollection : Dictionary<byte, IModule> {
10 public void Add(char Key, IModule Value) {
11 base.Add((byte)Key, Value);
12 }
13 public void Remove(char Key) {
14 base.Remove((byte)Key);
15 }
16 }
17
18 public class ClientAcceptedEventArgs : EventArgs {
19 private TCPStream _client;
20 internal ClientAcceptedEventArgs(TCPStream client) {
21 _client = client;
22 }
23 public TCPStream Client { get { return _client; } }
24 }
25
26 public event EventHandler<ClientAcceptedEventArgs> ClientAccepted;
27
28 private Socket _Listener;
29 private UCIS.ThreadPool _ThreadPool;
30 private NetworkConnectionList _Clients = new NetworkConnectionList();
31 private ModuleCollection _Modules = new ModuleCollection();
32 private IModule _CatchAllModule = null;
33 private Int32 _ThrottleCounter;
34 private Int32 _ThrottleBurst = 10;
35 private Int32 _ThrottleRate = 0;
36 private Timer _ThrottleTimer = null;
37
38 public TCPServer() {
39 _ThreadPool = UCIS.ThreadPool.DefaultPool;
40 }
41
42 public NetworkConnectionList Clients {
43 get { return _Clients; }
44 }
45
46 public ModuleCollection Modules {
47 get { return _Modules; }
48 }
49
50 public IModule CatchAllModule {
51 get {
52 return _CatchAllModule;
53 }
54 set {
55 _CatchAllModule = value;
56 }
57 }
58
59 public Int32 ThrottleRate {
60 get { return _ThrottleRate; }
61 set {
62 if (value < 0) throw new ArgumentOutOfRangeException("value");
63 _ThrottleRate = value;
64 if (_Listener == null) return;
65 if (value == 0 && _ThrottleTimer != null) {
66 _ThrottleTimer.Dispose();
67 _ThrottleTimer = null;
68 } else if (value > 0 && _ThrottleTimer == null) {
69 _ThrottleTimer = new Timer(ThrottleCallback, null, 1000, 1000);
70 }
71 }
72 }
73 public Int32 ThrottleBurst {
74 get { return _ThrottleBurst; }
75 set {
76 if (value < 1) throw new ArgumentOutOfRangeException("value");
77 _ThrottleBurst = value;
78 }
79 }
80
81 public EndPoint LocalEndPoint { get { return _Listener.LocalEndPoint; } }
82
83 public void Listen(int Port) {
84 Listen(AddressFamily.InterNetwork, Port);
85 }
86 public void Listen(AddressFamily af, int Port) {
87 Stop();
88
89 _Listener = new Socket(af, SocketType.Stream, ProtocolType.Tcp);
90
91 _Listener.Blocking = false;
92 _Listener.Bind(new IPEndPoint(af == AddressFamily.InterNetworkV6 ? IPAddress.IPv6Any : IPAddress.Any, Port));
93 _Listener.Listen(25);
94 _ThrottleCounter = _ThrottleBurst;
95 _Listener.BeginAccept(AcceptCallback, null);
96
97 if (_ThrottleRate > 0) {
98 _ThrottleTimer = new Timer(ThrottleCallback, null, 1000, 1000);
99 }
100 }
101
102 public void Stop() {
103 if (_ThrottleTimer != null) _ThrottleTimer.Dispose();
104 if (_Listener != null && _Listener.IsBound) _Listener.Close();
105 _Listener = null;
106 }
107
108 public void Close() {
109 Close(true);
110 }
111 public void Close(bool CloseClients) {
112 Stop();
113 if (CloseClients) {
114 _Clients.CloseAll();
115 if (_Clients.Count > 0) Console.WriteLine("TCPServer.Close: Warning: " + _Clients.Count.ToString() + " connections were not properly terminated.");
116 } else {
117 _Clients.Clear();
118 }
119 }
120
121 private void AcceptCallback(System.IAsyncResult ar) {
122 Client Client = null;
123 Socket Socket = null;
124 if (_Listener == null) return;
125 try {
126 Socket = _Listener.EndAccept(ar);
127 } catch (ObjectDisposedException) {
128 Console.WriteLine("TCPServer.AcceptCallback Listener object has been disposed. Aborting.");
129 return;
130 } catch (SocketException ex) {
131 Console.WriteLine("TCPServer.AcceptCallback SocketException: " + ex.Message);
132 Socket = null;
133 }
134 if (Socket != null) {
135 try {
136 Client = new Client(Socket, this);
137 _Clients.Add(Client);
138 if (ClientAccepted != null) ClientAccepted(this, new ClientAcceptedEventArgs(Client));
139 Client.Start(_ThreadPool);
140 } catch (Exception ex) {
141 Console.WriteLine(ex.ToString());
142 }
143 }
144 if (_ThrottleCounter > 0 || _ThrottleRate == 0) {
145 _ThrottleCounter--;
146 _Listener.BeginAccept(AcceptCallback, null);
147 }
148 }
149
150 private void ThrottleCallback(Object state) {
151 if (_Listener == null) return;
152 if (_ThrottleRate == 0) return;
153 if (_ThrottleCounter >= _ThrottleBurst) return;
154 if (_ThrottleCounter <= 0) {
155 _Listener.BeginAccept(AcceptCallback, null);
156 }
157 _ThrottleRate += _ThrottleRate;
158 }
159
160 public interface IModule {
161 // Return value: True = Close connection, False = keep alive
162 bool Accept(TCPStream Stream);
163 }
164
165 private class Client : TCPStream {
166 private TCPServer _Server;
167 private UCIS.ThreadPool.WorkItem _WorkItem;
168 private IModule _Module;
169 private byte _MagicNumber;
170
171 public TCPServer Server {
172 get { return _Server; }
173 }
174 public IModule Module {
175 get { return _Module; }
176 }
177
178 private void _Stream_Closed(object sender, EventArgs e) {
179 _WorkItem = null;
180 _Module = null;
181 _Server = null;
182 base.Closed -= _Stream_Closed;
183 }
184
185 internal Client(Socket Socket, TCPServer Server) : base(Socket) {
186 _Server = Server;
187 Socket.Blocking = true;
188 base.Closed += _Stream_Closed;
189 this.Tag = Server;
190 }
191
192 internal void Start(UCIS.ThreadPool Pool) {
193 _WorkItem = Pool.QueueWorkItem(WorkerProc, null);
194 }
195
196 private void WorkerProc(object state) {
197 bool CloseSocket = true;
198 try {
199 try {
200 base.Blocking = true;
201 //base.NoDelay = true;
202 base.ReadTimeout = 5000;
203 base.WriteBufferSize = 1024 * 10;
204 base.ReadBufferSize = 1024 * 10;
205 //Console.WriteLine("TCPServer: Accepted connection from " + base.Socket.RemoteEndPoint.ToString());
206 _MagicNumber = (byte)base.PeekByte();
207 } catch (TimeoutException ex) {
208 Console.WriteLine("TCPServer: Caught TimeoutException while reading magic number: " + ex.Message);
209 return;
210 }
211 if (_Server._Modules.TryGetValue(_MagicNumber, out _Module)) {
212 this.Tag = _Module;
213 CloseSocket = _Module.Accept(this);
214 } else if (_Server._CatchAllModule != null) {
215 this.Tag = _Server._CatchAllModule;
216 CloseSocket = _Server._CatchAllModule.Accept(this);
217 } else {
218 this.Tag = this;
219 Console.WriteLine("TCPServer: Unknown magic number: " + _MagicNumber.ToString());
220 }
221 } catch (ThreadAbortException) {
222 Console.WriteLine("TCPServer: Caught ThreadAbortException");
223 } catch (SocketException ex) {
224 Console.WriteLine("TCPServer: Caught SocketException: " + ex.Message);
225 } catch (Exception ex) {
226 Console.WriteLine("TCPServer: Caught Exception: " + ex.ToString());
227 } finally {
228 try {
229 if (CloseSocket) base.Close();
230 } catch { }
231 }
232 }
233 }
234 }
235 }