PageRenderTime 63ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/source/D3Sharp/Net/Server.cs

https://github.com/Pyr3x/d3sharp
C# | 307 lines | 215 code | 61 blank | 31 comment | 37 complexity | fce6749a1e1ac58624c039bf4ca024d1 MD5 | raw file
  1. /*
  2. * Copyright (C) 2011 D3Sharp Project
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software
  16. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  17. */
  18. using System;
  19. using System.Collections.Generic;
  20. using System.Linq;
  21. using System.Net;
  22. using System.Net.Sockets;
  23. using D3Sharp.Utils;
  24. using D3Sharp.Utils.Extensions;
  25. namespace D3Sharp.Net
  26. {
  27. public class Server : IDisposable
  28. {
  29. public bool IsListening { get; private set; }
  30. public int Port { get; private set; }
  31. protected Socket Listener;
  32. protected Dictionary<Socket, IConnection> Connections = new Dictionary<Socket, IConnection>();
  33. protected object ConnectionLock = new object();
  34. public delegate void ConnectionEventHandler(object sender, ConnectionEventArgs e);
  35. public delegate void ConnectionDataEventHandler(object sender, ConnectionDataEventArgs e);
  36. public event ConnectionEventHandler OnConnect;
  37. public event ConnectionEventHandler OnDisconnect;
  38. public event ConnectionDataEventHandler DataReceived;
  39. public event ConnectionDataEventHandler DataSent;
  40. protected static readonly Logger Logger = LogManager.CreateLogger();
  41. private bool _disposed;
  42. public virtual void Run() { }
  43. #region listener
  44. public virtual bool Listen(string bindIP, int port)
  45. {
  46. // Check if the server has been disposed.
  47. if (_disposed) throw new ObjectDisposedException(this.GetType().Name, "Server has been disposed.");
  48. // Check if the server is already listening.
  49. if (IsListening) throw new InvalidOperationException("Server is already listening.");
  50. // Create new TCP socket and set socket options.
  51. Listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  52. try
  53. {
  54. // This is failing on Linux; dunno why.
  55. Listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, true);
  56. }
  57. catch (SocketException e)
  58. {
  59. Logger.DebugException(e, "Listen");
  60. }
  61. Listener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true);
  62. try
  63. {
  64. // Bind.
  65. Listener.Bind(new IPEndPoint(IPAddress.Parse(bindIP), port));
  66. this.Port = port;
  67. }
  68. catch(SocketException e)
  69. {
  70. Logger.Fatal(string.Format("{0} can't bind on {1}, server shutting down..", this.GetType().Name, bindIP));
  71. this.Shutdown();
  72. return false;
  73. }
  74. // Start listening for incoming connections.
  75. Listener.Listen(10);
  76. IsListening = true;
  77. // Begin accepting any incoming connections asynchronously.
  78. Listener.BeginAccept(AcceptCallback, null);
  79. return true;
  80. }
  81. private void AcceptCallback(IAsyncResult result)
  82. {
  83. try
  84. {
  85. var socket = Listener.EndAccept(result); // Finish accepting the incoming connection.
  86. var connection = new Connection(this, socket); // Add the new connection to the dictionary.
  87. lock (ConnectionLock) Connections[socket] = connection; // add the connection to list.
  88. OnClientConnection(new ConnectionEventArgs(connection)); // Raise the OnConnect event.
  89. connection.BeginReceive(ReceiveCallback, connection); // Begin receiving on the new connection connection.
  90. Listener.BeginAccept(AcceptCallback, null); // Continue receiving other incoming connection asynchronously.
  91. }
  92. catch (Exception e)
  93. {
  94. Logger.DebugException(e, "AcceptCallback");
  95. }
  96. }
  97. private void ReceiveCallback(IAsyncResult result)
  98. {
  99. var connection = result.AsyncState as Connection; // Get the connection connection passed to the callback.
  100. if (connection == null) return;
  101. try
  102. {
  103. var bytesRecv = connection.EndReceive(result); // Finish receiving data from the socket.
  104. if (bytesRecv > 0)
  105. {
  106. OnDataReceived(new ConnectionDataEventArgs(connection, connection.RecvBuffer.Enumerate(0, bytesRecv))); // Raise the DataReceived event.
  107. if (connection.IsConnected) connection.BeginReceive(ReceiveCallback, connection); // Begin receiving again on the socket, if it is connected.
  108. else RemoveConnection(connection, true); // else remove it from connection list.
  109. }
  110. else RemoveConnection(connection, true); // Connection was lost.
  111. }
  112. catch (SocketException)
  113. {
  114. RemoveConnection(connection, true); // An error occured while receiving, connection has disconnected.
  115. }
  116. catch (Exception e)
  117. {
  118. Logger.DebugException(e, "ReceiveCallback");
  119. }
  120. }
  121. public virtual int Send(Connection connection, IEnumerable<byte> data, SocketFlags flags)
  122. {
  123. if (connection == null) throw new ArgumentNullException("connection");
  124. if (data == null) throw new ArgumentNullException("data");
  125. var buffer = data.ToArray();
  126. return Send(connection, buffer, 0, buffer.Length, SocketFlags.None);
  127. }
  128. public virtual int Send(Connection connection, byte[] buffer, int start, int count, SocketFlags flags)
  129. {
  130. if (connection == null) throw new ArgumentNullException("connection");
  131. if (buffer == null) throw new ArgumentNullException("buffer");
  132. var totalBytesSent = 0;
  133. var bytesRemaining = buffer.Length;
  134. try
  135. {
  136. while (bytesRemaining > 0) // Ensure we send every byte.
  137. {
  138. var bytesSent = connection.Socket.Send(buffer, totalBytesSent, bytesRemaining, flags); // Send the remaining data.
  139. if (bytesSent > 0)
  140. OnDataSent(new ConnectionDataEventArgs(connection, buffer.Enumerate(totalBytesSent, bytesSent))); // Raise the Data Sent event.
  141. // Decrement bytes remaining and increment bytes sent.
  142. bytesRemaining -= bytesSent;
  143. totalBytesSent += bytesSent;
  144. }
  145. }
  146. catch (SocketException)
  147. {
  148. RemoveConnection(connection, true); // An error occured while sending, connection has disconnected.
  149. }
  150. catch (Exception e)
  151. {
  152. Logger.DebugException(e, "Send");
  153. }
  154. return totalBytesSent;
  155. }
  156. #endregion
  157. #region service methods
  158. public IEnumerable<IConnection> GetConnections()
  159. {
  160. lock (ConnectionLock)
  161. foreach (IConnection connection in Connections.Values)
  162. yield return connection;
  163. }
  164. #endregion
  165. #region events
  166. protected virtual void OnClientConnection(ConnectionEventArgs e)
  167. {
  168. var handler = OnConnect;
  169. if (handler != null) handler(this, e);
  170. }
  171. protected virtual void OnClientDisconnect(ConnectionEventArgs e)
  172. {
  173. var handler = OnDisconnect;
  174. if (handler != null) handler(this, e);
  175. }
  176. protected virtual void OnDataReceived(ConnectionDataEventArgs e)
  177. {
  178. var handler = DataReceived;
  179. if (handler != null) handler(this, e);
  180. }
  181. protected virtual void OnDataSent(ConnectionDataEventArgs e)
  182. {
  183. var handler = DataSent;
  184. if (handler != null) handler(this, e);
  185. }
  186. #endregion
  187. #region disconnect & shutdown handlers
  188. public virtual void DisconnectAll()
  189. {
  190. lock (ConnectionLock)
  191. {
  192. foreach (var connection in Connections.Values.Cast<Connection>().Where(conn => conn.IsConnected)) // Check if the connection is connected.
  193. {
  194. // Disconnect and raise the OnDisconnect event.
  195. connection.Socket.Disconnect(false);
  196. OnClientDisconnect(new ConnectionEventArgs(connection));
  197. }
  198. Connections.Clear();
  199. }
  200. }
  201. public virtual void Disconnect(Connection connection)
  202. {
  203. if (connection == null) throw new ArgumentNullException("connection");
  204. if (!connection.IsConnected) return;
  205. connection.Socket.Disconnect(false);
  206. RemoveConnection(connection, true);
  207. }
  208. private void RemoveConnection(Connection connection, bool raiseEvent)
  209. {
  210. // Remove the connection from the dictionary and raise the OnDisconnection event.
  211. lock (ConnectionLock)
  212. if (Connections.Remove(connection.Socket) && raiseEvent)
  213. OnClientDisconnect(new ConnectionEventArgs(connection));
  214. }
  215. public virtual void Shutdown()
  216. {
  217. // Check if the server has been disposed.
  218. if (_disposed) throw new ObjectDisposedException(this.GetType().Name, "Server has been disposed.");
  219. // Check if the server is actually listening.
  220. if (!IsListening) return;
  221. // Close the listener socket.
  222. if (Listener != null) Listener.Close();
  223. Listener = null;
  224. IsListening = false;
  225. }
  226. #endregion
  227. #region de-ctor
  228. public void Dispose()
  229. {
  230. Dispose(true);
  231. GC.SuppressFinalize(this);
  232. }
  233. protected virtual void Dispose(bool disposing)
  234. {
  235. if (_disposed) return;
  236. if (disposing)
  237. {
  238. Shutdown(); // Close the listener socket.
  239. DisconnectAll(); // Disconnect all users.
  240. }
  241. // Dispose of unmanaged resources here.
  242. _disposed = true;
  243. }
  244. #endregion
  245. }
  246. }