diff --git a/Projects/DLL/SystemX.Core.dll b/Projects/DLL/SystemX.Core.dll index 135cf62..7515f20 100644 Binary files a/Projects/DLL/SystemX.Core.dll and b/Projects/DLL/SystemX.Core.dll differ diff --git a/Projects/HubX/DBPatch/sqlScripts/dacpac/HubX.DB.dacpac b/Projects/HubX/DBPatch/sqlScripts/dacpac/HubX.DB.dacpac index 380c56b..1307ee6 100644 Binary files a/Projects/HubX/DBPatch/sqlScripts/dacpac/HubX.DB.dacpac and b/Projects/HubX/DBPatch/sqlScripts/dacpac/HubX.DB.dacpac differ diff --git a/Projects/HubX/HubX.Library/HubX.Library.csproj b/Projects/HubX/HubX.Library/HubX.Library.csproj index fa71b7a..766fa05 100644 --- a/Projects/HubX/HubX.Library/HubX.Library.csproj +++ b/Projects/HubX/HubX.Library/HubX.Library.csproj @@ -6,4 +6,14 @@ enable + + + + + + + ..\..\DLL\SystemX.Core.dll + + + diff --git a/Projects/HubX/HubX.Library/Socket/Object/Client.cs b/Projects/HubX/HubX.Library/Socket/Object/Client.cs new file mode 100644 index 0000000..f2f957a --- /dev/null +++ b/Projects/HubX/HubX.Library/Socket/Object/Client.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using HubX.Library.Socket.Session; + +namespace HubX.Library.Socket.Object +{ + public class Client + { + + public EnumObjectType ObjectType = EnumObjectType.Client; + + private int ClientId; + public int Id + { + get { return ClientId; } + set { ClientId = value; } + } + + public ClientSession Session { get; set; } + + public Client() + { + + } + } +} diff --git a/Projects/HubX/HubX.Library/Socket/Object/EnumObjectType.cs b/Projects/HubX/HubX.Library/Socket/Object/EnumObjectType.cs new file mode 100644 index 0000000..84888f3 --- /dev/null +++ b/Projects/HubX/HubX.Library/Socket/Object/EnumObjectType.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HubX.Library.Socket.Object +{ + public enum EnumObjectType + { + NONE = 0, + Client = 1, + } +} diff --git a/Projects/HubX/HubX.Library/Socket/Object/ObjectManager.cs b/Projects/HubX/HubX.Library/Socket/Object/ObjectManager.cs new file mode 100644 index 0000000..8812d84 --- /dev/null +++ b/Projects/HubX/HubX.Library/Socket/Object/ObjectManager.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HubX.Library.Socket.Object +{ + public class ObjectManager + { + public static ObjectManager Instance { get; } = new ObjectManager(); + + object _lock = new object(); + Dictionary _players = new Dictionary(); + + // [UNUSED(1)][TYPE(7)][ID(24)] + int _counter = 0; + + public T Add() where T : Client, new() + { + T gameObject = new T(); + + lock (_lock) + { + gameObject.Id = GenerateId(gameObject.ObjectType); + + if (gameObject.ObjectType == EnumObjectType.Client) + { + _players.Add(gameObject.Id, gameObject as Client); + } + } + + return gameObject; + } + + int GenerateId(EnumObjectType type) + { + lock (_lock) + { + return ((int)type << 24) | (_counter++); + } + } + + public static EnumObjectType GetObjectTypeById(int id) + { + int type = (id >> 24) & 0x7F; + return (EnumObjectType)type; + } + + public bool Remove(int objectId) + { + EnumObjectType objectType = GetObjectTypeById(objectId); + + lock (_lock) + { + if (objectType == EnumObjectType.Client) + return _players.Remove(objectId); + } + + return false; + } + + public Client Find(int objectId) + { + EnumObjectType objectType = GetObjectTypeById(objectId); + + lock (_lock) + { + if (objectType == EnumObjectType.Client) + { + Client player = null; + if (_players.TryGetValue(objectId, out player)) + return player; + } + } + + return null; + } + } +} diff --git a/Projects/HubX/HubX.Library/Socket/Packet/EnumMessageId.cs b/Projects/HubX/HubX.Library/Socket/Packet/EnumMessageId.cs new file mode 100644 index 0000000..20c5e92 --- /dev/null +++ b/Projects/HubX/HubX.Library/Socket/Packet/EnumMessageId.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HubX.Library.Socket.Packet +{ + public enum EnumMessageId + { + //C2S : Client to Server + //S2C : Server to Client + + //unique key CRUD + C2S_INSERT_UniqueKey = 10, + S2C_INSERT_UniqueKey = 11, + C2S_SELECT_UniqueKey = 12, + S2C_SELECT_UniqueKey = 13, + C2S_UPDATE_UniqueKey = 14, + S2C_UPDATE_UniqueKey = 15, + C2S_DELETE_UniqueKey = 16, + S2C_DELETE_UniqueKey = 17, + } + + public enum EnumMessageResult + { + None = 0, + + Success = 10, + Failed = 10, + Error = 11, + } +} diff --git a/Projects/HubX/HubX.Library/Socket/Packet/PacketHandler.cs b/Projects/HubX/HubX.Library/Socket/Packet/PacketHandler.cs new file mode 100644 index 0000000..245a75d --- /dev/null +++ b/Projects/HubX/HubX.Library/Socket/Packet/PacketHandler.cs @@ -0,0 +1,43 @@ +using HubX.Library.Socket.Object; +using HubX.Library.Socket.Session; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Text; +using System.Threading.Tasks; +using SystemX.Core.Communication; + +namespace HubX.Library.Socket.Packet +{ + + public class PacketHandler + { + public static void C2S_INSERT_UniqueKeyHandler(PacketSession session, IMessage packet) + { + C2S_INSERT_UniqueKey movePacket = packet as C2S_INSERT_UniqueKey; + ClientSession clientSession = session as ClientSession; + + Client client = clientSession.Client; + if (client == null) + return; + } + + public static void C_SkillHandler(PacketSession session, IMessage packet) + { + //C_Skill skillPacket = packet as C_Skill; + //ClientSession clientSession = session as ClientSession; + + //Player player = clientSession.MyPlayer; + //if (player == null) + // return; + + //GameRoom room = player.Room; + //if (room == null) + // return; + + //room.Push(room.HandleSkill, player, skillPacket); + } + } + +} diff --git a/Projects/HubX/HubX.Library/Socket/Packet/Protocol.cs b/Projects/HubX/HubX.Library/Socket/Packet/Protocol.cs new file mode 100644 index 0000000..3909a35 --- /dev/null +++ b/Projects/HubX/HubX.Library/Socket/Packet/Protocol.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HubX.Library.Socket.Packet +{ + public interface IMessage + { + } + + public class C2S : IMessage + { + public EnumMessageId MessageId { get; set; } + } + + public class S2C : IMessage + { + public EnumMessageId MessageId { get; set; } + + public EnumMessageResult Result { get; set; } + } + + public sealed class C2S_INSERT_UniqueKey : C2S + { + public string Identity { get; set; } + + public string Data1 { get; set; } + public string Data2 { get; set; } + public string Data3 { get; set; } + public string Data4 { get; set; } + public string Data5 { get; set; } + } + + public sealed class S2C_INSERT_UniqueKey : S2C + { + public string Identity { get; set; } + + public string Data1 { get; set; } + public string Data2 { get; set; } + public string Data3 { get; set; } + public string Data4 { get; set; } + public string Data5 { get; set; } + } +} diff --git a/Projects/HubX/HubX.Library/Socket/Packet/ServerPacketManager.cs b/Projects/HubX/HubX.Library/Socket/Packet/ServerPacketManager.cs new file mode 100644 index 0000000..62e5426 --- /dev/null +++ b/Projects/HubX/HubX.Library/Socket/Packet/ServerPacketManager.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SystemX.Core.Communication; + +namespace HubX.Library.Socket.Packet +{ + public class PacketManager + { + #region Singleton + static PacketManager _instance = new PacketManager(); + public static PacketManager Instance { get { return _instance; } } + #endregion + + PacketManager() + { + Register(); + } + + Dictionary, ushort>> _onRecv = new Dictionary, ushort>>(); + Dictionary> _handler = new Dictionary>(); + + public Action CustomHandler { get; set; } + + public void Register() + { + _onRecv.Add((ushort)EnumMessageId.C2S_INSERT_UniqueKey, MakePacket); + _handler.Add((ushort)EnumMessageId.C2S_INSERT_UniqueKey, PacketHandler.C2S_INSERT_UniqueKeyHandler); + } + + public void OnRecvPacket(PacketSession session, ArraySegment buffer) + { + ushort count = 0; + + ushort size = BitConverter.ToUInt16(buffer.Array, buffer.Offset); + count += 2; + ushort id = BitConverter.ToUInt16(buffer.Array, buffer.Offset + count); + count += 2; + + Action, ushort> action = null; + if (_onRecv.TryGetValue(id, out action)) + action.Invoke(session, buffer, id); + } + + void MakePacket(PacketSession session, ArraySegment buffer, ushort id) where T : IMessage, new() + { + T pkt = new T(); + //pkt.MergeFrom(buffer.Array, buffer.Offset + 4, buffer.Count - 4); + + if (CustomHandler != null) + { + CustomHandler.Invoke(session, pkt, id); + } + else + { + Action action = null; + if (_handler.TryGetValue(id, out action)) + action.Invoke(session, pkt); + } + } + + public Action GetPacketHandler(ushort id) + { + Action action = null; + if (_handler.TryGetValue(id, out action)) + return action; + return null; + } + } +} diff --git a/Projects/HubX/HubX.Library/Socket/Session/ClientSession.cs b/Projects/HubX/HubX.Library/Socket/Session/ClientSession.cs new file mode 100644 index 0000000..e11dad6 --- /dev/null +++ b/Projects/HubX/HubX.Library/Socket/Session/ClientSession.cs @@ -0,0 +1,56 @@ +using HubX.Library.Socket.Object; +using HubX.Library.Socket.Packet; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Numerics; +using System.Text; +using System.Threading.Tasks; +using SystemX.Core.Communication; + +namespace HubX.Library.Socket.Session +{ + public class ClientSession : PacketSession + { + public Client Client { get; set; } + public int SessionId { get; set; } + + public void Send(IMessage packet) + { + //string msgName = packet.Descriptor.Name.Replace("_", string.Empty); + //EnumMessageId msgId = (EnumMessageId)Enum.Parse(typeof(EnumMessageId), msgName); + //ushort size = (ushort)packet.CalculateSize(); + //byte[] sendBuffer = new byte[size + 4]; + //Array.Copy(BitConverter.GetBytes((ushort)(size + 4)), 0, sendBuffer, 0, sizeof(ushort)); + //Array.Copy(BitConverter.GetBytes((ushort)msgId), 0, sendBuffer, 2, sizeof(ushort)); + //Array.Copy(packet.ToByteArray(), 0, sendBuffer, 4, size); + //Send(new ArraySegment(sendBuffer)); + } + + public override void OnConnected(EndPoint endPoint) + { + Log4net.WriteLine($"OnConnected : {endPoint}", LogType.SOCKET); + Client = ObjectManager.Instance.Add(); + { + Client.Session = this; + } + } + + public override void OnRecvPacket(ArraySegment buffer) + { + Log4net.WriteLine($"OnRecvPacket : {Encoding.UTF8.GetString(buffer)}", LogType.SOCKET); + PacketManager.Instance.OnRecvPacket(this, buffer); + } + + public override void OnDisconnected(EndPoint endPoint) + { + Log4net.WriteLine($"OnDisconnected : {endPoint}", LogType.SOCKET); + } + + public override void OnSend(int numOfBytes) + { + //Console.WriteLine($"Transferred bytes: {numOfBytes}"); + } + } +} diff --git a/Projects/HubX/HubX.Library/Socket/Session/SessionManager.cs b/Projects/HubX/HubX.Library/Socket/Session/SessionManager.cs new file mode 100644 index 0000000..f409659 --- /dev/null +++ b/Projects/HubX/HubX.Library/Socket/Session/SessionManager.cs @@ -0,0 +1,54 @@ +using HubX.Library.Socket.Packet; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SystemX.Core.Communication; + +namespace HubX.Library.Socket.Session +{ + public class SessionManager + { + static SessionManager _session = new SessionManager(); + public static SessionManager Instance { get { return _session; } } + + int _sessionId = 0; + Dictionary _sessions = new Dictionary(); + object _lock = new object(); + + public ClientSession Generate() + { + lock (_lock) + { + int sessionId = ++_sessionId; + + ClientSession session = new ClientSession(); + session.SessionId = sessionId; + _sessions.Add(sessionId, session); + + Log4net.WriteLine($"Connected : {sessionId}", LogType.SOCKET); + + return session; + } + } + + public ClientSession Find(int id) + { + lock (_lock) + { + ClientSession session = null; + _sessions.TryGetValue(id, out session); + return session; + } + } + + public void Remove(ClientSession session) + { + lock (_lock) + { + _sessions.Remove(session.SessionId); + } + } + } +} diff --git a/Projects/HubX/HubX.Server/HubX.Server.csproj b/Projects/HubX/HubX.Server/HubX.Server.csproj index d3bb312..2cc9165 100644 --- a/Projects/HubX/HubX.Server/HubX.Server.csproj +++ b/Projects/HubX/HubX.Server/HubX.Server.csproj @@ -6,10 +6,22 @@ enable + + 9999 + + + + 9999 + + + + + + ..\..\DLL\SystemX.Core.dll diff --git a/Projects/HubX/HubX.Server/Program.cs b/Projects/HubX/HubX.Server/Program.cs index c56fe2d..797a699 100644 --- a/Projects/HubX/HubX.Server/Program.cs +++ b/Projects/HubX/HubX.Server/Program.cs @@ -1,4 +1,7 @@ -using SystemX.Core.Log4net; +using HubX.Library.Socket.Session; +using HubX.Server; +using System.Net; +using SystemX.Core.Communication; var builder = WebApplication.CreateBuilder(args); @@ -21,6 +24,30 @@ if (app.Environment.IsDevelopment()) Log4net.WriteLine("Run"); Log4net.WriteLine("Custom LogLevel",LogType.DB); +WeatherForecast weatherForecast = new WeatherForecast(); +weatherForecast.Summary = "so hot"; +var strJson = weatherForecast.ToJson(); + +var deep = weatherForecast.DeepCopy(); +deep.Summary = "so cool"; + +var rr = strJson.ToObject(); + +Task.Run(async() => +{ + await Task.Delay(2000); + Listener _listener = new Listener(); + + // string host = Dns.GetHostName(); + IPHostEntry ipHost = Dns.GetHostEntry("127.0.0.1"); + IPAddress ipAddr = ipHost.AddressList[0]; + IPEndPoint endPoint = new IPEndPoint(ipAddr, 7777); + + _listener.Init(endPoint, () => { return SessionManager.Instance.Generate(); }); + + Console.WriteLine("Listening..."); +}); + app.UseHttpsRedirection(); app.UseAuthorization(); diff --git a/Projects/HubX/HubX.Server/Properties/launchSettings.json b/Projects/HubX/HubX.Server/Properties/launchSettings.json index ceae9d3..aee3210 100644 --- a/Projects/HubX/HubX.Server/Properties/launchSettings.json +++ b/Projects/HubX/HubX.Server/Properties/launchSettings.json @@ -1,4 +1,31 @@ -{ +{ + "profiles": { + "http": { + "commandName": "Project", + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "http://localhost:5103" + }, + "https": { + "commandName": "Project", + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "https://localhost:7163;http://localhost:5103" + }, + "IIS Express": { + "commandName": "IISExpress", + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + }, "$schema": "http://json.schemastore.org/launchsettings.json", "iisSettings": { "windowsAuthentication": false, @@ -7,35 +34,5 @@ "applicationUrl": "http://localhost:33125", "sslPort": 44383 } - }, - "profiles": { - "http": { - "commandName": "Project", - "dotnetRunMessages": true, - "launchBrowser": true, - "launchUrl": "swagger", - "applicationUrl": "http://localhost:5103", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "https": { - "commandName": "Project", - "dotnetRunMessages": true, - "launchBrowser": true, - "launchUrl": "swagger", - "applicationUrl": "https://localhost:7163;http://localhost:5103", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "launchUrl": "swagger", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } } -} +} \ No newline at end of file diff --git a/Projects/SystemX.Core/SystemX.Core/Communication/Http.cs b/Projects/SystemX.Core/SystemX.Core/Communication/Http.cs new file mode 100644 index 0000000..33cbbf2 --- /dev/null +++ b/Projects/SystemX.Core/SystemX.Core/Communication/Http.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http.Json; +using System.Text; +using System.Threading.Tasks; + +namespace SystemX.Core.Communication +{ + public class Http + { + /// + /// PostJsonAsync + /// + /// https://127.0.0.1:443 + /// Range 5~30 secconds + public virtual async Task PostJsonAsync(string url, REQUEST request, short timeOutSeconds = 5) where REQUEST : class where RESPONSE : class + { + RESPONSE? response = default(RESPONSE); + Guid guid = Guid.NewGuid(); + + using (HttpClient httpClient = new HttpClient(GetClientHandler())) + { + int retry = 0; + while (true) + { + await Task.Delay(1); + try + { + var timeOutSec = SetTimeout(timeOutSeconds); + httpClient.Timeout = new TimeSpan(0, 0, timeOutSec); + httpClient.BaseAddress = new Uri($"{url}"); + + Log4net.WriteLine($"[POST] Request({guid})::{url}{Environment.NewLine}{request?.ToJson()}", LogType.HTTP); + + DateTime requestTime = DateTime.Now; + var res = await httpClient.PostAsJsonAsync(url, request); + response = await res.Content.ReadFromJsonAsync(); + + Log4net.WriteLine($"[POST] Rseponse({guid}) ({(DateTime.Now - requestTime).TotalSeconds} sec)::{url}{Environment.NewLine}{response?.ToJson()}", LogType.HTTP); + break; + } + catch (Exception e) + { + Log4net.WriteLine(e); + retry += 1; + } + + if (retry > 1) + break; + } + } + + return response; + } + + /// + /// GetJsonAsnyc + /// + /// https://127.0.0.1:443 + /// Range 5~30 secconds + public virtual async Task GetJsonAsync(string url, short timeOutSeconds = 10) where RESPONSE : class + { + RESPONSE? response = default(RESPONSE); + Guid guid = Guid.NewGuid(); + + using (HttpClient httpClient = new HttpClient(GetClientHandler())) + { + try + { + var timeOutSec = SetTimeout(timeOutSeconds); + httpClient.Timeout = new TimeSpan(0, 0, timeOutSec); + httpClient.BaseAddress = new Uri($"{url}"); + + Log4net.WriteLine($"[GET] Request({guid})::{url}", LogType.HTTP); + + DateTime requestTime = DateTime.Now; + response = await httpClient.GetFromJsonAsync(url); + + Log4net.WriteLine($"[GET] Rseponse({guid}) ({(DateTime.Now - requestTime).TotalSeconds} sec)::{url}", LogType.HTTP); + } + catch (Exception e) + { + Log4net.WriteLine(e); + } + } + + return response; + } + + protected HttpClientHandler GetClientHandler() + { + HttpClientHandler clientHandler = new HttpClientHandler(); + clientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, sslPolicyErrors) => + { + return true; + }; + + return clientHandler; + } + + protected short SetTimeout(short timeOutSeconds) + { + short timeoutMin = 5; + short timeoutMax = 30; + + timeOutSeconds = Math.Clamp(timeOutSeconds, timeoutMin, timeoutMax); + + return timeOutSeconds; + } + } +} diff --git a/Projects/SystemX.Core/SystemX.Core/Communication/Socket/Listener.cs b/Projects/SystemX.Core/SystemX.Core/Communication/Socket/Listener.cs new file mode 100644 index 0000000..ed7d124 --- /dev/null +++ b/Projects/SystemX.Core/SystemX.Core/Communication/Socket/Listener.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Sockets; +using System.Net; +using System.Text; +using System.Threading.Tasks; +using static System.Collections.Specialized.BitVector32; + +namespace SystemX.Core.Communication +{ + public class Listener + { + Socket _listenSocket; + Func _sessionFactory; + + public void Init(IPEndPoint endPoint, Func sessionFactory, int register = 10, int backlog = 100) + { + _listenSocket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + _sessionFactory += sessionFactory; + + _listenSocket.Bind(endPoint); + + // backlog : 최대 대기수 + _listenSocket.Listen(backlog); + + for (int i = 0; i < register; i++) + { + SocketAsyncEventArgs args = new SocketAsyncEventArgs(); + args.Completed += new EventHandler(OnAcceptCompleted); + RegisterAccept(args); + } + } + + void RegisterAccept(SocketAsyncEventArgs args) + { + args.AcceptSocket = null; + + bool pending = _listenSocket.AcceptAsync(args); + if (pending == false) + OnAcceptCompleted(null, args); + } + + void OnAcceptCompleted(object sender, SocketAsyncEventArgs args) + { + if (args.SocketError == SocketError.Success) + { + Session session = _sessionFactory.Invoke(); + session.Start(args.AcceptSocket); + session.OnConnected(args.AcceptSocket.RemoteEndPoint); + } + else + { + Log4net.WriteLine(args.SocketError.ToString(), LogType.Error); + } + + RegisterAccept(args); + } + } +} diff --git a/Projects/SystemX.Core/SystemX.Core/Communication/Socket/RecvBuffer.cs b/Projects/SystemX.Core/SystemX.Core/Communication/Socket/RecvBuffer.cs new file mode 100644 index 0000000..29f67bb --- /dev/null +++ b/Projects/SystemX.Core/SystemX.Core/Communication/Socket/RecvBuffer.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SystemX.Core.Communication +{ + public class RecvBuffer + { + // [r][][w][][][][][][][] + ArraySegment _buffer; + int _readPos; + int _writePos; + + public RecvBuffer(int bufferSize) + { + _buffer = new ArraySegment(new byte[bufferSize], 0, bufferSize); + } + + public int DataSize { get { return _writePos - _readPos; } } + public int FreeSize { get { return _buffer.Count - _writePos; } } + + public ArraySegment ReadSegment + { + get { return new ArraySegment(_buffer.Array, _buffer.Offset + _readPos, DataSize); } + } + + public ArraySegment WriteSegment + { + get { return new ArraySegment(_buffer.Array, _buffer.Offset + _writePos, FreeSize); } + } + + public void Clean() + { + int dataSize = DataSize; + if (dataSize == 0) + { + // 남은 데이터가 없으면 복사하지 않고 커서 위치만 리셋 + _readPos = _writePos = 0; + } + else + { + // 남은 찌끄레기가 있으면 시작 위치로 복사 + Array.Copy(_buffer.Array, _buffer.Offset + _readPos, _buffer.Array, _buffer.Offset, dataSize); + _readPos = 0; + _writePos = dataSize; + } + } + + public bool OnRead(int numOfBytes) + { + if (numOfBytes > DataSize) + return false; + + _readPos += numOfBytes; + return true; + } + + public bool OnWrite(int numOfBytes) + { + if (numOfBytes > FreeSize) + return false; + + _writePos += numOfBytes; + return true; + } + } +} diff --git a/Projects/SystemX.Core/SystemX.Core/Communication/Socket/Session.cs b/Projects/SystemX.Core/SystemX.Core/Communication/Socket/Session.cs new file mode 100644 index 0000000..d4edf48 --- /dev/null +++ b/Projects/SystemX.Core/SystemX.Core/Communication/Socket/Session.cs @@ -0,0 +1,239 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Sockets; +using System.Net; +using System.Text; +using System.Threading.Tasks; + +namespace SystemX.Core.Communication +{ + public abstract class PacketSession : Session + { + public static readonly int HeaderSize = 2; + + // [size(2)][packetId(2)][ ... ][size(2)][packetId(2)][ ... ] + public sealed override int OnRecv(ArraySegment buffer) + { + int processLen = 0; + int packetCount = 0; + + while (true) + { + // 최소한 헤더는 파싱할 수 있는지 확인 + if (buffer.Count < HeaderSize) + break; + + // 패킷이 완전체로 도착했는지 확인 + ushort dataSize = BitConverter.ToUInt16(buffer.Array, buffer.Offset); + if (buffer.Count < dataSize) + break; + + // 여기까지 왔으면 패킷 조립 가능 + OnRecvPacket(new ArraySegment(buffer.Array, buffer.Offset, dataSize)); + packetCount++; + + processLen += dataSize; + buffer = new ArraySegment(buffer.Array, buffer.Offset + dataSize, buffer.Count - dataSize); + } + + return processLen; + } + + public abstract void OnRecvPacket(ArraySegment buffer); + } + + public abstract class Session + { + Socket _socket; + int _disconnected = 0; + + RecvBuffer _recvBuffer = new RecvBuffer(65535); + + object _lock = new object(); + Queue> _sendQueue = new Queue>(); + List> _pendingList = new List>(); + SocketAsyncEventArgs _sendArgs = new SocketAsyncEventArgs(); + SocketAsyncEventArgs _recvArgs = new SocketAsyncEventArgs(); + + public abstract void OnConnected(EndPoint endPoint); + public abstract int OnRecv(ArraySegment buffer); + public abstract void OnSend(int numOfBytes); + public abstract void OnDisconnected(EndPoint endPoint); + + void Clear() + { + lock (_lock) + { + _sendQueue.Clear(); + _pendingList.Clear(); + } + } + + public void Start(Socket socket) + { + Log4net.WriteLine("Session Start"); + + _socket = socket; + + _recvArgs.Completed += new EventHandler(OnRecvCompleted); + _sendArgs.Completed += new EventHandler(OnSendCompleted); + + RegisterRecv(); + } + + public void Send(List> sendBuffList) + { + if (sendBuffList.Count == 0) + return; + + lock (_lock) + { + foreach (ArraySegment sendBuff in sendBuffList) + _sendQueue.Enqueue(sendBuff); + + if (_pendingList.Count == 0) + RegisterSend(); + } + } + + public void Send(ArraySegment sendBuff) + { + lock (_lock) + { + _sendQueue.Enqueue(sendBuff); + if (_pendingList.Count == 0) + RegisterSend(); + } + } + + public void Disconnect() + { + if (Interlocked.Exchange(ref _disconnected, 1) == 1) + return; + + OnDisconnected(_socket.RemoteEndPoint); + _socket.Shutdown(SocketShutdown.Both); + _socket.Close(); + Clear(); + } + + #region 네트워크 통신 + + void RegisterSend() + { + if (_disconnected == 1) + return; + + while (_sendQueue.Count > 0) + { + ArraySegment buff = _sendQueue.Dequeue(); + _pendingList.Add(buff); + } + _sendArgs.BufferList = _pendingList; + + try + { + bool pending = _socket.SendAsync(_sendArgs); + if (pending == false) + OnSendCompleted(null, _sendArgs); + } + catch (Exception e) + { + Log4net.WriteLine($"RegisterSend Failed {e}", LogType.Error); + } + } + + void OnSendCompleted(object sender, SocketAsyncEventArgs args) + { + lock (_lock) + { + if (args.BytesTransferred > 0 && args.SocketError == SocketError.Success) + { + try + { + _sendArgs.BufferList = null; + _pendingList.Clear(); + + OnSend(_sendArgs.BytesTransferred); + + if (_sendQueue.Count > 0) + RegisterSend(); + } + catch (Exception e) + { + Log4net.WriteLine($"OnSendCompleted Failed {e}", LogType.Error); + } + } + else + { + Disconnect(); + } + } + } + + void RegisterRecv() + { + if (_disconnected == 1) + return; + + _recvBuffer.Clean(); + ArraySegment segment = _recvBuffer.WriteSegment; + _recvArgs.SetBuffer(segment.Array, segment.Offset, segment.Count); + + try + { + bool pending = _socket.ReceiveAsync(_recvArgs); + if (pending == false) + OnRecvCompleted(null, _recvArgs); + } + catch (Exception e) + { + Log4net.WriteLine($"RegisterRecv Failed {e}", LogType.Error); + } + } + + void OnRecvCompleted(object sender, SocketAsyncEventArgs args) + { + if (args.BytesTransferred > 0 && args.SocketError == SocketError.Success) + { + try + { + // Write 커서 이동 + if (_recvBuffer.OnWrite(args.BytesTransferred) == false) + { + Disconnect(); + return; + } + + // 컨텐츠 쪽으로 데이터를 넘겨주고 얼마나 처리했는지 받는다 + int processLen = OnRecv(_recvBuffer.ReadSegment); + if (processLen < 0 || _recvBuffer.DataSize < processLen) + { + Disconnect(); + return; + } + + // Read 커서 이동 + if (_recvBuffer.OnRead(processLen) == false) + { + Disconnect(); + return; + } + + RegisterRecv(); + } + catch (Exception e) + { + Log4net.WriteLine($"OnRecvCompleted Failed {e}", LogType.Error); + } + } + else + { + Disconnect(); + } + } + + #endregion + } +} diff --git a/Projects/SystemX.Core/SystemX.Core/DB/DataBase.cs b/Projects/SystemX.Core/SystemX.Core/DB/DataBase.cs new file mode 100644 index 0000000..8080bea --- /dev/null +++ b/Projects/SystemX.Core/SystemX.Core/DB/DataBase.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace SystemX.Core.DB +{ + public class DataBase + { + [JsonPropertyName("IP")] + public string? IP { get; set; } + + [JsonPropertyName("Port")] + public int Port { get; set; } + + [JsonPropertyName("DBName")] + public string? DBName { get; set; } + + [JsonPropertyName("DBID")] + public int DBID { get; set; } + + [JsonPropertyName("DBContext")] + public string? DBContext { get; set; } + + [JsonPropertyName("UserID")] + public string? UserID { get; set; } + + [JsonPropertyName("Password")] + public string? Password { get; set; } + } +} diff --git a/Projects/SystemX.Core/SystemX.Core/DB/EFCore.cs b/Projects/SystemX.Core/SystemX.Core/DB/EFCore.cs new file mode 100644 index 0000000..bb3a997 --- /dev/null +++ b/Projects/SystemX.Core/SystemX.Core/DB/EFCore.cs @@ -0,0 +1,106 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Storage; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SystemX.Core.DB +{ + public static class EFCore + { + /// + /// Get SqlServer Connection String + /// + public static string ConvertToConnectionString(this DataBase dbConfig) + { + return $"server={dbConfig.IP},{dbConfig.Port}; user id={dbConfig.UserID}; password={dbConfig.Password}; database={dbConfig.DBName}; TrustServerCertificate=true;"; + } + + public static IEnumerable? GetEntity(this DbContext dbContext) where TEntity : class + { + IEnumerable? entity = default; + + var type = dbContext.GetType(); + try + { + foreach (var prop in type.GetProperties()) + { + string propertyFullName = $"{prop.PropertyType.FullName}"; + if (propertyFullName.ToLower().Contains(typeof(TEntity).Name.ToLower())) + { + entity = prop.GetValue(dbContext) as IEnumerable; + break; + } + } + } + catch (Exception ex) + { + Log4net.WriteLine(ex); + } + + return entity; + } + + #region Transaction + public static IDbContextTransaction CreateTransaction(this DbContext dbContext) + { + return dbContext.Database.BeginTransaction(); + } + + public static bool CloseTransaction(this DbContext dbContext, IDbContextTransaction transaction) + { + bool result = false; + try + { + dbContext.SaveChanges(); + transaction.Commit(); + result = true; + + Log4net.WriteLine("Transaction Commit", LogType.Debug); + } + catch (Exception ex) + { + transaction.Rollback(); + Log4net.WriteLine("Transaction Rollback", LogType.Error); + Log4net.WriteLine(ex); + } + transaction.Dispose(); + + return result; + } + #endregion + + #region Transaction Async + public static async Task CreateTransactionAsync(this DbContext dbContext) + { + return await dbContext.Database.BeginTransactionAsync(); + } + + public static async Task CloseTransactionAsync(this DbContext dbContext, IDbContextTransaction transaction) + { + bool result = false; + try + { + await dbContext.SaveChangesAsync(); + await transaction.CommitAsync(); + result = true; + + Log4net.WriteLine("Transaction Commit", LogType.Debug); + } + catch (Exception ex) + { + await transaction.RollbackAsync(); + Log4net.WriteLine("Transaction Rollback", LogType.Error); + Log4net.WriteLine(ex); + } + await transaction.DisposeAsync(); + + return result; + } + #endregion + } + + +} diff --git a/Projects/SystemX.Core/SystemX.Core/Log4net/Log4net.cs b/Projects/SystemX.Core/SystemX.Core/Log4net/Log4net.cs index d7ea1a8..8d10913 100644 --- a/Projects/SystemX.Core/SystemX.Core/Log4net/Log4net.cs +++ b/Projects/SystemX.Core/SystemX.Core/Log4net/Log4net.cs @@ -7,240 +7,239 @@ using System.Reflection; using System.Text; using System.Threading.Tasks; -namespace SystemX.Core.Log4net + +#region LogType +public enum LogType { - #region LogType - public enum LogType + //default level + Info = 0, + Warn = 1, + Error = 2, + Debug = 3, + Fatal = 4, + + //custom level + DB = 10, + + HTTP = 20, + SOCKET = 21, +} +#endregion + +#region CustomLevel +public static class Log4netCustomLevel +{ + public static readonly log4net.Core.Level DB = new log4net.Core.Level(10010, LogType.DB.ToString()); + public static readonly log4net.Core.Level HTTP = new log4net.Core.Level(10020, LogType.HTTP.ToString()); + public static readonly log4net.Core.Level SOCKET = new log4net.Core.Level(10021, LogType.SOCKET.ToString()); + + public static void SetCustomLevel(ILoggerRepository repo) { - //default level - Info = 0, - Warn = 1, - Error = 2, - Debug = 3, - Fatal = 4, - - //custom level - DB = 10, - - HTTP = 20, - SOCKET = 21, - } - #endregion - - #region CustomLevel - public static class Log4netCustomLevel - { - public static readonly log4net.Core.Level DB = new log4net.Core.Level(10010, LogType.DB.ToString()); - public static readonly log4net.Core.Level HTTP = new log4net.Core.Level(10020, LogType.HTTP.ToString()); - public static readonly log4net.Core.Level SOCKET = new log4net.Core.Level(10021, LogType.SOCKET.ToString()); - - public static void SetCustomLevel(ILoggerRepository repo) - { - repo.LevelMap.Add(DB); - repo.LevelMap.Add(HTTP); - repo.LevelMap.Add(SOCKET); - } - } - #endregion - - public static class Log4net - { - private static ILog? Manager; - - private static ILoggerRepository repo = LogManager.GetRepository(); - - public static bool IsConfigLoad { get; set; } = false; - - - - static Log4net() - { - Console.WriteLine("log4net constructor"); - if (File.Exists("./log4net.config") == false) - { - File.WriteAllText("log4net.config", Config); - } - - IsConfigLoad = OpenConfig(@"./log4net.config"); - } - - private static bool OpenConfig(string path) - { - bool result = true; - - try - { - Log4netCustomLevel.SetCustomLevel(repo); - - if (File.Exists(path) == false) - result = false; - - log4net.Config.XmlConfigurator.Configure(new FileInfo(path)); - Manager = LogManager.GetLogger(""); - } - catch (Exception e) - { - Console.WriteLine("Log4net Init Error"); - Console.WriteLine(e.Message); - result = false; - } - - return result; - } - - public static void WriteLine(T log, LogType logType = LogType.Info) - { - if (IsConfigLoad == false) - { - Console.ForegroundColor = ConsoleColor.Red; - Console.WriteLine($"[Log4net Initialize Error] {log}"); - return; - } - - switch (logType) - { - case LogType.Info: - { - Manager?.Info(log); - break; - } - case LogType.Warn: - { - Manager?.Warn(log); - break; - } - case LogType.Error: - { - Manager?.Error(log); - break; - } - case LogType.Debug: - { - Manager?.Debug(log); - break; - } - case LogType.Fatal: - { - Manager?.Fatal(log); - break; - } - case LogType.DB: - { - Type? t = MethodBase.GetCurrentMethod()?.DeclaringType; - if (t != null) - { - Manager?.Logger.Log(t, Log4netCustomLevel.DB, log, null); - } - break; - } - case LogType.HTTP: - { - Type? t = MethodBase.GetCurrentMethod()?.DeclaringType; - if (t != null) - { - Manager?.Logger.Log(t, Log4netCustomLevel.HTTP, log, null); - } - break; - } - case LogType.SOCKET: - { - Type? t = MethodBase.GetCurrentMethod()?.DeclaringType; - if (t != null) - { - Manager?.Logger.Log(t, Log4netCustomLevel.SOCKET, log, null); - } - break; - } - } - } - - public static void WriteLine(Exception? ex) - { - WriteLine(ex?.Message, LogType.Error); - } - - - #region Config - static string Config = @$" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -"; - #endregion + repo.LevelMap.Add(DB); + repo.LevelMap.Add(HTTP); + repo.LevelMap.Add(SOCKET); } } +#endregion + +public static class Log4net +{ + private static ILog? Manager; + + private static ILoggerRepository repo = LogManager.GetRepository(); + + public static bool IsConfigLoad { get; set; } = false; + + + + static Log4net() + { + Console.WriteLine("log4net constructor"); + if (File.Exists("./log4net.config") == false) + { + File.WriteAllText("log4net.config", Config); + } + + IsConfigLoad = OpenConfig(@"./log4net.config"); + } + + private static bool OpenConfig(string path) + { + bool result = true; + + try + { + Log4netCustomLevel.SetCustomLevel(repo); + + if (File.Exists(path) == false) + result = false; + + log4net.Config.XmlConfigurator.Configure(new FileInfo(path)); + Manager = LogManager.GetLogger(""); + } + catch (Exception e) + { + Console.WriteLine("Log4net Init Error"); + Console.WriteLine(e.Message); + result = false; + } + + return result; + } + + public static void WriteLine(T log, LogType logType = LogType.Info) + { + if (IsConfigLoad == false) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine($"[Log4net Initialize Error] {log}"); + return; + } + + switch (logType) + { + case LogType.Info: + { + Manager?.Info(log); + break; + } + case LogType.Warn: + { + Manager?.Warn(log); + break; + } + case LogType.Error: + { + Manager?.Error(log); + break; + } + case LogType.Debug: + { + Manager?.Debug(log); + break; + } + case LogType.Fatal: + { + Manager?.Fatal(log); + break; + } + case LogType.DB: + { + Type? t = MethodBase.GetCurrentMethod()?.DeclaringType; + if (t != null) + { + Manager?.Logger.Log(t, Log4netCustomLevel.DB, log, null); + } + break; + } + case LogType.HTTP: + { + Type? t = MethodBase.GetCurrentMethod()?.DeclaringType; + if (t != null) + { + Manager?.Logger.Log(t, Log4netCustomLevel.HTTP, log, null); + } + break; + } + case LogType.SOCKET: + { + Type? t = MethodBase.GetCurrentMethod()?.DeclaringType; + if (t != null) + { + Manager?.Logger.Log(t, Log4netCustomLevel.SOCKET, log, null); + } + break; + } + } + } + + public static void WriteLine(Exception? ex) + { + WriteLine(ex?.Message, LogType.Error); + } + + + #region Config + static string Config = @$" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"; + #endregion +} + /* log4net 지정 가능 색상 diff --git a/Projects/SystemX.Core/SystemX.Core/SystemX.Core.csproj b/Projects/SystemX.Core/SystemX.Core/SystemX.Core.csproj index 1d7f086..c96a53d 100644 --- a/Projects/SystemX.Core/SystemX.Core/SystemX.Core.csproj +++ b/Projects/SystemX.Core/SystemX.Core/SystemX.Core.csproj @@ -17,6 +17,7 @@ + diff --git a/Projects/SystemX.Core/SystemX.Core/Utils/DateTimeUtils.cs b/Projects/SystemX.Core/SystemX.Core/Utils/DateTimeUtils.cs new file mode 100644 index 0000000..212a657 --- /dev/null +++ b/Projects/SystemX.Core/SystemX.Core/Utils/DateTimeUtils.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +public static class DateTimeUtils +{ + public static long ToUnixTime(this DateTime dateTime) + { + DateTimeOffset dto = new DateTimeOffset(dateTime.ToUniversalTime()); + return dto.ToUnixTimeSeconds(); + } +} + diff --git a/Projects/SystemX.Core/SystemX.Core/Json/JsonUtils.cs b/Projects/SystemX.Core/SystemX.Core/Utils/JsonUtils.cs similarity index 97% rename from Projects/SystemX.Core/SystemX.Core/Json/JsonUtils.cs rename to Projects/SystemX.Core/SystemX.Core/Utils/JsonUtils.cs index fab37aa..4cda787 100644 --- a/Projects/SystemX.Core/SystemX.Core/Json/JsonUtils.cs +++ b/Projects/SystemX.Core/SystemX.Core/Utils/JsonUtils.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -using SystemX.Core.Log4net; public static class JsonUtils {