123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259 |
- 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
- {
- /// <summary>
- /// 可继承此类,封装服务器操作
- /// </summary>
- /// <typeparam name="TPlayer">为每个连接者提供一个唯一的TPlayer,此类需继承自<see cref="ConnectionPlayerBase"/>并提供无参数构造函数</typeparam>
- public abstract class ConnectionServer<TPlayer, LoginOrRegisterRequestType>
- 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<string, TPlayer> OnlinePlayers;
- public LockerList<string> 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<string>();
- 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<string, TPlayer>();
- 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<LoginOrRegisterRequestType>(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<ConnectionPlayerBase> selfDestroy;
- internal void Init(Socket vaildPlayerSocket, int uid, string nick, int mbsz, Action<ConnectionPlayerBase> 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<ConnectObjectFromClient>(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) { }
- }
- }
|