AppServer.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Net;
  7. using System.Reflection;
  8. using System.Security.Authentication;
  9. using System.Security.Cryptography.X509Certificates;
  10. using System.Text;
  11. using System.Threading;
  12. using System.Threading.Tasks;
  13. using Microsoft.Extensions.Logging;
  14. using SuperSocket.Common;
  15. using SuperSocket.SocketBase.Command;
  16. using SuperSocket.SocketBase.Config;
  17. using SuperSocket.SocketBase.Protocol;
  18. using SuperSocket.SocketBase.Security;
  19. namespace SuperSocket.SocketBase
  20. {
  21. /// <summary>
  22. /// AppServer class
  23. /// </summary>
  24. public class AppServer : AppServer<AppSession>
  25. {
  26. /// <summary>
  27. /// Initializes a new instance of the <see cref="AppServer"/> class.
  28. /// </summary>
  29. public AppServer()
  30. : base()
  31. {
  32. }
  33. /// <summary>
  34. /// Initializes a new instance of the <see cref="AppServer"/> class.
  35. /// </summary>
  36. /// <param name="receiveFilterFactory">The Receive filter factory.</param>
  37. public AppServer(IReceiveFilterFactory<StringRequestInfo> receiveFilterFactory)
  38. : base(receiveFilterFactory)
  39. {
  40. }
  41. }
  42. /// <summary>
  43. /// AppServer class
  44. /// </summary>
  45. /// <typeparam name="TAppSession">The type of the app session.</typeparam>
  46. public class AppServer<TAppSession> : AppServer<TAppSession, StringRequestInfo>
  47. where TAppSession : AppSession<TAppSession, StringRequestInfo>, IAppSession, new()
  48. {
  49. /// <summary>
  50. /// Initializes a new instance of the <see cref="AppServer&lt;TAppSession&gt;"/> class.
  51. /// </summary>
  52. public AppServer()
  53. : base()
  54. {
  55. }
  56. /// <summary>
  57. /// Initializes a new instance of the <see cref="AppServer&lt;TAppSession&gt;"/> class.
  58. /// </summary>
  59. /// <param name="receiveFilterFactory">The Receive filter factory.</param>
  60. public AppServer(IReceiveFilterFactory<StringRequestInfo> receiveFilterFactory)
  61. : base(receiveFilterFactory)
  62. {
  63. }
  64. internal override IReceiveFilterFactory<StringRequestInfo> CreateDefaultReceiveFilterFactory()
  65. {
  66. return new CommandLineReceiveFilterFactory(TextEncoding);
  67. }
  68. }
  69. /// <summary>
  70. /// AppServer basic class
  71. /// </summary>
  72. /// <typeparam name="TAppSession">The type of the app session.</typeparam>
  73. /// <typeparam name="TRequestInfo">The type of the request info.</typeparam>
  74. public abstract class AppServer<TAppSession, TRequestInfo> : AppServerBase<TAppSession, TRequestInfo>
  75. where TRequestInfo : class, IRequestInfo
  76. where TAppSession : AppSession<TAppSession, TRequestInfo>, IAppSession, new()
  77. {
  78. /// <summary>
  79. /// Initializes a new instance of the <see cref="AppServer&lt;TAppSession, TRequestInfo&gt;"/> class.
  80. /// </summary>
  81. public AppServer()
  82. : base()
  83. {
  84. }
  85. /// <summary>
  86. /// Initializes a new instance of the <see cref="AppServer&lt;TAppSession, TRequestInfo&gt;"/> class.
  87. /// </summary>
  88. /// <param name="protocol">The protocol.</param>
  89. protected AppServer(IReceiveFilterFactory<TRequestInfo> protocol)
  90. : base(protocol)
  91. {
  92. }
  93. internal override IReceiveFilterFactory<TRequestInfo> CreateDefaultReceiveFilterFactory()
  94. {
  95. return null;
  96. }
  97. /// <summary>
  98. /// Starts this AppServer instance.
  99. /// </summary>
  100. /// <returns></returns>
  101. public override bool Start()
  102. {
  103. if (!base.Start())
  104. return false;
  105. if (!Config.DisableSessionSnapshot)
  106. StartSessionSnapshotTimer();
  107. if (Config.ClearIdleSession)
  108. StartClearSessionTimer();
  109. return true;
  110. }
  111. private ConcurrentDictionary<string, TAppSession> m_SessionDict = new ConcurrentDictionary<string, TAppSession>(StringComparer.OrdinalIgnoreCase);
  112. /// <summary>
  113. /// Registers the session into the session container.
  114. /// </summary>
  115. /// <param name="sessionID">The session ID.</param>
  116. /// <param name="appSession">The app session.</param>
  117. /// <returns></returns>
  118. protected override bool RegisterSession(string sessionID, TAppSession appSession)
  119. {
  120. if (m_SessionDict.TryAdd(sessionID, appSession))
  121. return true;
  122. Logger.LogError(appSession, "The session is refused because the it's ID already exists!");
  123. return false;
  124. }
  125. /// <summary>
  126. /// Gets the app session by ID.
  127. /// </summary>
  128. /// <param name="sessionID">The session ID.</param>
  129. /// <returns></returns>
  130. [Obsolete("Use the method GetSessionByID instead")]
  131. public TAppSession GetAppSessionByID(string sessionID)
  132. {
  133. return GetSessionByID(sessionID);
  134. }
  135. /// <summary>
  136. /// Gets the app session by ID.
  137. /// </summary>
  138. /// <param name="sessionID">The session ID.</param>
  139. /// <returns></returns>
  140. public override TAppSession GetSessionByID(string sessionID)
  141. {
  142. if (string.IsNullOrEmpty(sessionID))
  143. return NullAppSession;
  144. TAppSession targetSession;
  145. m_SessionDict.TryGetValue(sessionID, out targetSession);
  146. return targetSession;
  147. }
  148. /// <summary>
  149. /// Called when [socket session closed].
  150. /// </summary>
  151. /// <param name="session">The session.</param>
  152. /// <param name="reason">The reason.</param>
  153. protected override void OnSessionClosed(TAppSession session, CloseReason reason)
  154. {
  155. string sessionID = session.SessionID;
  156. if (!string.IsNullOrEmpty(sessionID))
  157. {
  158. TAppSession removedSession;
  159. if (!m_SessionDict.TryRemove(sessionID, out removedSession))
  160. {
  161. Logger.LogError(session, "Failed to remove this session, Because it has't been in session container!");
  162. }
  163. }
  164. base.OnSessionClosed(session, reason);
  165. }
  166. /// <summary>
  167. /// Gets the total session count.
  168. /// </summary>
  169. public override int SessionCount
  170. {
  171. get
  172. {
  173. return m_SessionDict.Count;
  174. }
  175. }
  176. #region Clear idle sessions
  177. private System.Threading.Timer m_ClearIdleSessionTimer = null;
  178. private void StartClearSessionTimer()
  179. {
  180. int interval = Config.ClearIdleSessionInterval * 1000;//in milliseconds
  181. m_ClearIdleSessionTimer = new System.Threading.Timer(ClearIdleSession, new object(), interval, interval);
  182. }
  183. /// <summary>
  184. /// Clears the idle session.
  185. /// </summary>
  186. /// <param name="state">The state.</param>
  187. private void ClearIdleSession(object state)
  188. {
  189. if (Monitor.TryEnter(state))
  190. {
  191. try
  192. {
  193. var sessionSource = SessionSource;
  194. if (sessionSource == null)
  195. return;
  196. DateTime now = DateTime.UtcNow;
  197. DateTime timeOut = now.AddSeconds(0 - Config.IdleSessionTimeOut);
  198. var timeOutSessions = sessionSource.Where(s => s.Value.LastActiveTime <= timeOut).Select(s => s.Value);
  199. System.Threading.Tasks.Parallel.ForEach(timeOutSessions, s =>
  200. {
  201. Logger.LogInfo(s, string.Format("The session will be closed for {0} timeout, the session start time: {1}, last active time: {2}!", now.Subtract(s.LastActiveTime).TotalSeconds, s.StartTime, s.LastActiveTime));
  202. s.Close(CloseReason.TimeOut);
  203. });
  204. }
  205. catch (Exception e)
  206. {
  207. Logger.LogError(e,e.Message);
  208. }
  209. finally
  210. {
  211. Monitor.Exit(state);
  212. }
  213. }
  214. }
  215. private KeyValuePair<string, TAppSession>[] SessionSource
  216. {
  217. get
  218. {
  219. if (Config.DisableSessionSnapshot)
  220. return m_SessionDict.ToArray();
  221. else
  222. return m_SessionsSnapshot;
  223. }
  224. }
  225. #endregion
  226. #region Take session snapshot
  227. private System.Threading.Timer m_SessionSnapshotTimer = null;
  228. private KeyValuePair<string, TAppSession>[] m_SessionsSnapshot = new KeyValuePair<string, TAppSession>[0];
  229. private void StartSessionSnapshotTimer()
  230. {
  231. int interval = Math.Max(Config.SessionSnapshotInterval, 1) * 1000;//in milliseconds
  232. m_SessionSnapshotTimer = new System.Threading.Timer(TakeSessionSnapshot, new object(), interval, interval);
  233. }
  234. private void TakeSessionSnapshot(object state)
  235. {
  236. if (Monitor.TryEnter(state))
  237. {
  238. Interlocked.Exchange(ref m_SessionsSnapshot, m_SessionDict.ToArray());
  239. Monitor.Exit(state);
  240. }
  241. }
  242. #endregion
  243. #region Search session utils
  244. /// <summary>
  245. /// Gets the matched sessions from sessions snapshot.
  246. /// </summary>
  247. /// <param name="critera">The prediction critera.</param>
  248. /// <returns></returns>
  249. public override IEnumerable<TAppSession> GetSessions(Func<TAppSession, bool> critera)
  250. {
  251. var sessionSource = SessionSource;
  252. if (sessionSource == null)
  253. return null;
  254. return sessionSource.Select(p => p.Value).Where(critera);
  255. }
  256. /// <summary>
  257. /// Gets all sessions in sessions snapshot.
  258. /// </summary>
  259. /// <returns></returns>
  260. public override IEnumerable<TAppSession> GetAllSessions()
  261. {
  262. var sessionSource = SessionSource;
  263. if (sessionSource == null)
  264. return null;
  265. return sessionSource.Select(p => p.Value);
  266. }
  267. /// <summary>
  268. /// Stops this instance.
  269. /// </summary>
  270. public override void Stop()
  271. {
  272. base.Stop();
  273. if (m_SessionSnapshotTimer != null)
  274. {
  275. m_SessionSnapshotTimer.Change(Timeout.Infinite, Timeout.Infinite);
  276. m_SessionSnapshotTimer.Dispose();
  277. m_SessionSnapshotTimer = null;
  278. }
  279. if (m_ClearIdleSessionTimer != null)
  280. {
  281. m_ClearIdleSessionTimer.Change(Timeout.Infinite, Timeout.Infinite);
  282. m_ClearIdleSessionTimer.Dispose();
  283. m_ClearIdleSessionTimer = null;
  284. }
  285. m_SessionsSnapshot = null;
  286. var sessions = m_SessionDict.ToArray();
  287. if (sessions.Length > 0)
  288. {
  289. var tasks = new Task[sessions.Length];
  290. for (var i = 0; i < tasks.Length; i++)
  291. {
  292. tasks[i] = Task.Factory.StartNew((s) =>
  293. {
  294. var session = s as TAppSession;
  295. if (session != null)
  296. {
  297. session.Close(CloseReason.ServerShutdown);
  298. }
  299. }, sessions[i].Value);
  300. }
  301. Task.WaitAll(tasks);
  302. }
  303. }
  304. #endregion
  305. }
  306. }