using Island.StandardLib.Exceptions; using Island.StandardLib.Storage; using System; using System.Collections.Generic; using System.Collections.Concurrent; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using Island.StandardLib.Utils; namespace Island.StandardLib { /// /// 可继承此类,封装服务器操作 /// /// 为每个连接者提供一个唯一的TPlayer,此类需继承自并提供无参数构造函数 public abstract class ConnectionServer where TPlayer : ConnectionPlayerBase, new() where LoginOrRegisterRequestType : ILoginOrRegisterRequest, new() { public int ServerPort { get; private set; } public string ServerAddress { get; private set; } public int MaxBitSize { get; private set; } public uint ServerVersion { get; private set; } public ConcurrentDictionary OnlinePlayers; public LockerList PlayersLocker; public TPlayer FindOnlinePlayer(string nick) { if (OnlinePlayers.TryGetValue(nick, out TPlayer player)) return player; else return null; } public ConnectionServer(string addr, int port, uint version = 1, int maxbitsz = 524288) { Logger.InitLoggerOnce(); PlayersLocker = new LockerList(); ConsoleColor bkup = Console.ForegroundColor; Console.Write("Island.StandardLib."); Console.ForegroundColor = ConsoleColor.Cyan; Console.Write("ServerConnection"); Console.ForegroundColor = bkup; Console.WriteLine("()"); Console.WriteLine(" __ ______ _ _ ____ \n \\ \\/ / ___| | \\ | | __ ) \n \\ / | | \\| | _ \\ \n / \\ |___ | |\\ | |_) |\n /_/\\_\\____| |_| \\_|____/\n"); Console.Write("-------------------- "); Console.ForegroundColor = ConsoleColor.Cyan; Console.Write("StandardLib "); Console.ForegroundColor = bkup; Console.WriteLine("Version 21Y0125 --------------------\n"); Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("github.com/XCBOSA/Island.StandardLib"); Console.ForegroundColor = bkup; OnlinePlayers = new ConcurrentDictionary(); ServerAddress = addr; MaxBitSize = maxbitsz; ServerPort = port; ServerVersion = version; IPAddress serverip = IPAddress.Parse(addr); Socket serverSocket = new Socket(serverip.AddressFamily, SocketType.Stream, ProtocolType.Tcp); serverSocket.Bind(new IPEndPoint(serverip, ServerPort)); serverSocket.Listen(1024); Thread acceptThread = new Thread(AcceptLoop); acceptThread.IsBackground = true; acceptThread.Start(serverSocket); Logger.WriteLine(LogLevel.Default, $"Server start at {addr}:{ServerPort}"); } void AcceptLoop(object obj) { Socket serverSocket = (Socket)obj; OnConnectionBegin(); Exception exitWith; while (true) { try { Socket client = serverSocket.Accept(); //Logger.WriteLine(LogLevel.Info, "{0}:{1} Connected.", ((IPEndPoint)client.RemoteEndPoint).Address.ToString(), // ((IPEndPoint)client.RemoteEndPoint).Port.ToString()); new Thread(sock => OnReceiveLoginOrRegisterRequest((Socket)sock)) { IsBackground = true }.Start(client); } catch (Exception e) { exitWith = e; Logger.LogError(e); break; } } OnConnectionBreaked(exitWith); } protected abstract LoginResult PassLogin(LoginOrRegisterRequestType request, IPAddress visitIp); protected abstract RegisterResult PassReg(LoginOrRegisterRequestType request, IPAddress visitIp); protected virtual void OnConnectionBegin() { } protected virtual void OnConnectionBreaked(Exception reason) { } protected void KeepConnect(Socket client, string name) { if (OnlinePlayers.ContainsKey(name)) throw new PlayerSocketFatalException(name, PlayerSocketFatalExceptionType.InternalDuplicatePlayerObject); PlayersLocker.Lock(name); TPlayer isp = new TPlayer(); isp.Init(client, 0, name, MaxBitSize, InternalDestroy); OnlinePlayers.TryAdd(isp.Nick, isp); Logger.WriteLine(LogLevel.Info, "{0} Joined.", isp.Nick); PlayersLocker.Unlock(name); } protected virtual void OnReceiveLoginOrRegisterRequest(Socket client) { try { LoginOrRegisterRequestType request = client.ReceiveOnce(2048); bool cont = false; byte[] callbackd = null; if (request.IsLogin) { LoginCallback callback = new LoginCallback(); if (request.ClientVersion != ServerVersion) callback.Code = request.ClientVersion < ServerVersion ? LoginResult.VersionLower : LoginResult.VersionHigher; else callback.Code = PassLogin(request, ((IPEndPoint)client.RemoteEndPoint).Address); if (callback.Code == LoginResult.Success) cont = true; callbackd = callback.GetBytes(); } else { RegisterCallback callback = new RegisterCallback(); if (request.ClientVersion != ServerVersion) callback.Username = (int)(request.ClientVersion < ServerVersion ? RegisterResult.VersionLower : RegisterResult.VersionHigher); else callback.Username = (int)PassReg(request, ((IPEndPoint)client.RemoteEndPoint).Address); callbackd = callback.GetBytes(); } client.SendOnce(callbackd); if (cont) { KeepConnect(client, request.UserName); } else { //Logger.WriteLine(LogLevel.Info, "{0}:{1} Disconnected.", ((IPEndPoint)client.RemoteEndPoint).Address.ToString(), // ((IPEndPoint)client.RemoteEndPoint).Port.ToString()); client.Close(); client.Dispose(); } } catch (Exception e) { Logger.Log(LogLevel.Error, ((IPEndPoint)client.RemoteEndPoint).Address.ToString() + ":"); Logger.LogError(e); client.Close(); client.Dispose(); } } void InternalDestroy(ConnectionPlayerBase player) { if (!OnlinePlayers.TryRemove(player.Nick, out TPlayer _)) throw new Exception($"InternalDestroy: OnlinePlayers.TryRemove return false, player = {player.Nick}"); } } public abstract class ConnectionPlayerBase { int max_bitsize; public int Id { get; protected set; } public string Nick { get; private set; } public bool IsOnline { get; private set; } public ConnectObjectFromServer CommandSendPool; public Thread SocketLoopThread; public ConnectionPlayerBase() { } Action selfDestroy; internal void Init(Socket vaildPlayerSocket, int uid, string nick, int mbsz, Action selfDest) { CommandSendPool = new ConnectObjectFromServer(); Id = uid; Nick = nick; SocketLoopThread = new Thread(SocketLoop); SocketLoopThread.IsBackground = true; SocketLoopThread.Start(vaildPlayerSocket); max_bitsize = mbsz; selfDestroy = selfDest; } Socket socket; public void SocketLoop(object objVaildPlayerSocket) { IsOnline = true; socket = (Socket)objVaildPlayerSocket; bool isFirstSend = true; OnConnectionBegin(); Exception breakWith; socket.ReceiveTimeout = 5000; socket.SendTimeout = 5000; while (true) { try { ConnectObjectFromClient clientData = null; if (isFirstSend) { clientData = new ConnectObjectFromClient(); isFirstSend = false; } else clientData = socket.ReceiveOnce(max_bitsize, Nick); lock (CommandSendPool) { InnerCommandPass(clientData); socket.SendOnce(CommandSendPool); CommandSendPool.ClearCommands(); } } catch (PlayerSocketFatalException e) { breakWith = e; Logger.LogError(e); break; } catch (SocketException e) { breakWith = e; Logger.Log(LogLevel.Info, "Player {0}: Stopped Connection", Nick); break; } catch (Exception e) { breakWith = e; Logger.Log(LogLevel.Warning, "Player {0}: Exception: {1}", Nick, e); break; } } try { socket.Close(); socket.Dispose(); } catch { } OnConnectionBreaked(breakWith); selfDestroy(this); } void InnerCommandPass(ConnectObjectFromClient clientData) { for (int i = 0; i < clientData.Commands.Length; i++) { ConnectCommand command = clientData.Commands[i]; PassCommand(command); } } public void ForceEndConnect() { socket.Close(); socket.Dispose(); } protected abstract void PassCommand(ConnectCommand command); protected virtual void OnConnectionBegin() { } protected virtual void OnConnectionBreaked(Exception reason) { } } }