ConnectionServer.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. using Island.StandardLib.Exceptions;
  2. using Island.StandardLib.Storage;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Collections.Concurrent;
  6. using System.Linq;
  7. using System.Net;
  8. using System.Net.Sockets;
  9. using System.Text;
  10. using System.Threading;
  11. using Island.StandardLib.Utils;
  12. namespace Island.StandardLib
  13. {
  14. /// <summary>
  15. /// 可继承此类,封装服务器操作
  16. /// </summary>
  17. /// <typeparam name="TPlayer">为每个连接者提供一个唯一的TPlayer,此类需继承自<see cref="ConnectionPlayerBase"/>并提供无参数构造函数</typeparam>
  18. public abstract class ConnectionServer<TPlayer, LoginOrRegisterRequestType>
  19. where TPlayer : ConnectionPlayerBase, new()
  20. where LoginOrRegisterRequestType : ILoginOrRegisterRequest, new()
  21. {
  22. public int ServerPort { get; private set; }
  23. public string ServerAddress { get; private set; }
  24. public int MaxBitSize { get; private set; }
  25. public uint ServerVersion { get; private set; }
  26. public ConcurrentDictionary<string, TPlayer> OnlinePlayers;
  27. public LockerList<string> PlayersLocker;
  28. public TPlayer FindOnlinePlayer(string nick)
  29. {
  30. if (OnlinePlayers.TryGetValue(nick, out TPlayer player))
  31. return player;
  32. else return null;
  33. }
  34. public ConnectionServer(string addr, int port, uint version = 1, int maxbitsz = 524288)
  35. {
  36. Logger.InitLoggerOnce();
  37. PlayersLocker = new LockerList<string>();
  38. ConsoleColor bkup = Console.ForegroundColor;
  39. Console.Write("Island.StandardLib.");
  40. Console.ForegroundColor = ConsoleColor.Cyan;
  41. Console.Write("ServerConnection");
  42. Console.ForegroundColor = bkup;
  43. Console.WriteLine("()");
  44. Console.WriteLine(" __ ______ _ _ ____ \n \\ \\/ / ___| | \\ | | __ ) \n \\ / | | \\| | _ \\ \n / \\ |___ | |\\ | |_) |\n /_/\\_\\____| |_| \\_|____/\n");
  45. Console.Write("-------------------- ");
  46. Console.ForegroundColor = ConsoleColor.Cyan;
  47. Console.Write("StandardLib ");
  48. Console.ForegroundColor = bkup;
  49. Console.WriteLine("Version 21Y0125 --------------------\n");
  50. Console.ForegroundColor = ConsoleColor.Green;
  51. Console.WriteLine("github.com/XCBOSA/Island.StandardLib");
  52. Console.ForegroundColor = bkup;
  53. OnlinePlayers = new ConcurrentDictionary<string, TPlayer>();
  54. ServerAddress = addr;
  55. MaxBitSize = maxbitsz;
  56. ServerPort = port;
  57. ServerVersion = version;
  58. IPAddress serverip = IPAddress.Parse(addr);
  59. Socket serverSocket = new Socket(serverip.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
  60. serverSocket.Bind(new IPEndPoint(serverip, ServerPort));
  61. serverSocket.Listen(1024);
  62. Thread acceptThread = new Thread(AcceptLoop);
  63. acceptThread.IsBackground = true;
  64. acceptThread.Start(serverSocket);
  65. Logger.WriteLine(LogLevel.Default, $"Server start at {addr}:{ServerPort}");
  66. }
  67. void AcceptLoop(object obj)
  68. {
  69. Socket serverSocket = (Socket)obj;
  70. OnConnectionBegin();
  71. Exception exitWith;
  72. while (true)
  73. {
  74. try
  75. {
  76. Socket client = serverSocket.Accept();
  77. //Logger.WriteLine(LogLevel.Info, "{0}:{1} Connected.", ((IPEndPoint)client.RemoteEndPoint).Address.ToString(),
  78. // ((IPEndPoint)client.RemoteEndPoint).Port.ToString());
  79. new Thread(sock => OnReceiveLoginOrRegisterRequest((Socket)sock)) { IsBackground = true }.Start(client);
  80. }
  81. catch (Exception e)
  82. {
  83. exitWith = e;
  84. Logger.LogError(e);
  85. break;
  86. }
  87. }
  88. OnConnectionBreaked(exitWith);
  89. }
  90. protected abstract LoginResult PassLogin(LoginOrRegisterRequestType request, IPAddress visitIp);
  91. protected abstract RegisterResult PassReg(LoginOrRegisterRequestType request, IPAddress visitIp);
  92. protected virtual void OnConnectionBegin() { }
  93. protected virtual void OnConnectionBreaked(Exception reason) { }
  94. protected void KeepConnect(Socket client, string name)
  95. {
  96. if (OnlinePlayers.ContainsKey(name))
  97. throw new PlayerSocketFatalException(name, PlayerSocketFatalExceptionType.InternalDuplicatePlayerObject);
  98. PlayersLocker.Lock(name);
  99. TPlayer isp = new TPlayer();
  100. isp.Init(client, 0, name, MaxBitSize, InternalDestroy);
  101. OnlinePlayers.TryAdd(isp.Nick, isp);
  102. Logger.WriteLine(LogLevel.Info, "{0} Joined.", isp.Nick);
  103. PlayersLocker.Unlock(name);
  104. }
  105. protected virtual void OnReceiveLoginOrRegisterRequest(Socket client)
  106. {
  107. try
  108. {
  109. LoginOrRegisterRequestType request = client.ReceiveOnce<LoginOrRegisterRequestType>(2048);
  110. bool cont = false;
  111. byte[] callbackd = null;
  112. if (request.IsLogin)
  113. {
  114. LoginCallback callback = new LoginCallback();
  115. if (request.ClientVersion != ServerVersion)
  116. callback.Code = request.ClientVersion < ServerVersion ? LoginResult.VersionLower : LoginResult.VersionHigher;
  117. else callback.Code = PassLogin(request, ((IPEndPoint)client.RemoteEndPoint).Address);
  118. if (callback.Code == LoginResult.Success)
  119. cont = true;
  120. callbackd = callback.GetBytes();
  121. }
  122. else
  123. {
  124. RegisterCallback callback = new RegisterCallback();
  125. if (request.ClientVersion != ServerVersion)
  126. callback.Username = (int)(request.ClientVersion < ServerVersion ? RegisterResult.VersionLower : RegisterResult.VersionHigher);
  127. else callback.Username = (int)PassReg(request, ((IPEndPoint)client.RemoteEndPoint).Address);
  128. callbackd = callback.GetBytes();
  129. }
  130. client.SendOnce(callbackd);
  131. if (cont)
  132. {
  133. KeepConnect(client, request.UserName);
  134. }
  135. else
  136. {
  137. //Logger.WriteLine(LogLevel.Info, "{0}:{1} Disconnected.", ((IPEndPoint)client.RemoteEndPoint).Address.ToString(),
  138. // ((IPEndPoint)client.RemoteEndPoint).Port.ToString());
  139. client.Close();
  140. client.Dispose();
  141. }
  142. }
  143. catch (Exception e)
  144. {
  145. Logger.Log(LogLevel.Error, ((IPEndPoint)client.RemoteEndPoint).Address.ToString() + ":");
  146. Logger.LogError(e);
  147. client.Close();
  148. client.Dispose();
  149. }
  150. }
  151. void InternalDestroy(ConnectionPlayerBase player)
  152. {
  153. if (!OnlinePlayers.TryRemove(player.Nick, out TPlayer _))
  154. throw new Exception($"InternalDestroy: OnlinePlayers.TryRemove return false, player = {player.Nick}");
  155. }
  156. }
  157. public abstract class ConnectionPlayerBase
  158. {
  159. int max_bitsize;
  160. public int Id { get; protected set; }
  161. public string Nick { get; private set; }
  162. public bool IsOnline { get; private set; }
  163. public ConnectObjectFromServer CommandSendPool;
  164. public Thread SocketLoopThread;
  165. public ConnectionPlayerBase() { }
  166. Action<ConnectionPlayerBase> selfDestroy;
  167. internal void Init(Socket vaildPlayerSocket, int uid, string nick, int mbsz, Action<ConnectionPlayerBase> selfDest)
  168. {
  169. CommandSendPool = new ConnectObjectFromServer();
  170. Id = uid;
  171. Nick = nick;
  172. SocketLoopThread = new Thread(SocketLoop);
  173. SocketLoopThread.IsBackground = true;
  174. SocketLoopThread.Start(vaildPlayerSocket);
  175. max_bitsize = mbsz;
  176. selfDestroy = selfDest;
  177. }
  178. Socket socket;
  179. public void SocketLoop(object objVaildPlayerSocket)
  180. {
  181. IsOnline = true;
  182. socket = (Socket)objVaildPlayerSocket;
  183. bool isFirstSend = true;
  184. OnConnectionBegin();
  185. Exception breakWith;
  186. socket.ReceiveTimeout = 5000;
  187. socket.SendTimeout = 5000;
  188. while (true)
  189. {
  190. try
  191. {
  192. ConnectObjectFromClient clientData = null;
  193. if (isFirstSend)
  194. {
  195. clientData = new ConnectObjectFromClient();
  196. isFirstSend = false;
  197. }
  198. else clientData = socket.ReceiveOnce<ConnectObjectFromClient>(max_bitsize, Nick);
  199. lock (CommandSendPool)
  200. {
  201. InnerCommandPass(clientData);
  202. socket.SendOnce(CommandSendPool);
  203. CommandSendPool.ClearCommands();
  204. }
  205. }
  206. catch (PlayerSocketFatalException e) { breakWith = e; Logger.LogError(e); break; }
  207. catch (SocketException e) { breakWith = e; Logger.Log(LogLevel.Info, "Player {0}: Stopped Connection", Nick); break; }
  208. catch (Exception e) { breakWith = e; Logger.Log(LogLevel.Warning, "Player {0}: Exception: {1}", Nick, e); break; }
  209. }
  210. try
  211. {
  212. socket.Close();
  213. socket.Dispose();
  214. }
  215. catch { }
  216. OnConnectionBreaked(breakWith);
  217. selfDestroy(this);
  218. }
  219. void InnerCommandPass(ConnectObjectFromClient clientData)
  220. {
  221. for (int i = 0; i < clientData.Commands.Length; i++)
  222. {
  223. ConnectCommand command = clientData.Commands[i];
  224. PassCommand(command);
  225. }
  226. }
  227. public void ForceEndConnect()
  228. {
  229. socket.Close();
  230. socket.Dispose();
  231. }
  232. protected abstract void PassCommand(ConnectCommand command);
  233. protected virtual void OnConnectionBegin() { }
  234. protected virtual void OnConnectionBreaked(Exception reason) { }
  235. }
  236. }