[성현모] Http, Socket 통신 추가

This commit is contained in:
SHM
2025-04-21 08:30:59 +09:00
parent 1bfc56f437
commit a12a3949bf
25 changed files with 1370 additions and 264 deletions

Binary file not shown.

View File

@ -6,4 +6,14 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\HubX.Library.DB\HubX.Library.DB.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="SystemX.Core">
<HintPath>..\..\DLL\SystemX.Core.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

View File

@ -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()
{
}
}
}

View File

@ -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,
}
}

View File

@ -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<int, Client> _players = new Dictionary<int, Client>();
// [UNUSED(1)][TYPE(7)][ID(24)]
int _counter = 0;
public T Add<T>() 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;
}
}
}

View File

@ -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,
}
}

View File

@ -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);
}
}
}

View File

@ -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; }
}
}

View File

@ -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, Action<PacketSession, ArraySegment<byte>, ushort>> _onRecv = new Dictionary<ushort, Action<PacketSession, ArraySegment<byte>, ushort>>();
Dictionary<ushort, Action<PacketSession, IMessage>> _handler = new Dictionary<ushort, Action<PacketSession, IMessage>>();
public Action<PacketSession, IMessage, ushort> CustomHandler { get; set; }
public void Register()
{
_onRecv.Add((ushort)EnumMessageId.C2S_INSERT_UniqueKey, MakePacket<C2S_INSERT_UniqueKey>);
_handler.Add((ushort)EnumMessageId.C2S_INSERT_UniqueKey, PacketHandler.C2S_INSERT_UniqueKeyHandler);
}
public void OnRecvPacket(PacketSession session, ArraySegment<byte> 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<PacketSession, ArraySegment<byte>, ushort> action = null;
if (_onRecv.TryGetValue(id, out action))
action.Invoke(session, buffer, id);
}
void MakePacket<T>(PacketSession session, ArraySegment<byte> 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<PacketSession, IMessage> action = null;
if (_handler.TryGetValue(id, out action))
action.Invoke(session, pkt);
}
}
public Action<PacketSession, IMessage> GetPacketHandler(ushort id)
{
Action<PacketSession, IMessage> action = null;
if (_handler.TryGetValue(id, out action))
return action;
return null;
}
}
}

View File

@ -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<byte>(sendBuffer));
}
public override void OnConnected(EndPoint endPoint)
{
Log4net.WriteLine($"OnConnected : {endPoint}", LogType.SOCKET);
Client = ObjectManager.Instance.Add<Client>();
{
Client.Session = this;
}
}
public override void OnRecvPacket(ArraySegment<byte> 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}");
}
}
}

View File

@ -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<int, ClientSession> _sessions = new Dictionary<int, ClientSession>();
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);
}
}
}
}

View File

@ -6,10 +6,22 @@
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<WarningLevel>9999</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<WarningLevel>9999</WarningLevel>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\HubX.Library\HubX.Library.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="SystemX.Core">
<HintPath>..\..\DLL\SystemX.Core.dll</HintPath>

View File

@ -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<WeatherForecast>();
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();

View File

@ -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"
}
}
}
}

View File

@ -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
{
/// <summary>
/// PostJsonAsync
/// </summary>
/// <param name="url">https://127.0.0.1:443</param>
/// <param name="timeOutSeconds">Range 5~30 secconds</param>
public virtual async Task<RESPONSE?> PostJsonAsync<REQUEST, RESPONSE>(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<RESPONSE>();
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;
}
/// <summary>
/// GetJsonAsnyc
/// </summary>
/// <param name="url">https://127.0.0.1:443</param>
/// <param name="timeOutSeconds">Range 5~30 secconds</param>
public virtual async Task<RESPONSE?> GetJsonAsync<RESPONSE>(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<RESPONSE>(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;
}
}
}

View File

@ -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<Session> _sessionFactory;
public void Init(IPEndPoint endPoint, Func<Session> 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<SocketAsyncEventArgs>(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);
}
}
}

View File

@ -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<byte> _buffer;
int _readPos;
int _writePos;
public RecvBuffer(int bufferSize)
{
_buffer = new ArraySegment<byte>(new byte[bufferSize], 0, bufferSize);
}
public int DataSize { get { return _writePos - _readPos; } }
public int FreeSize { get { return _buffer.Count - _writePos; } }
public ArraySegment<byte> ReadSegment
{
get { return new ArraySegment<byte>(_buffer.Array, _buffer.Offset + _readPos, DataSize); }
}
public ArraySegment<byte> WriteSegment
{
get { return new ArraySegment<byte>(_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;
}
}
}

View File

@ -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<byte> 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<byte>(buffer.Array, buffer.Offset, dataSize));
packetCount++;
processLen += dataSize;
buffer = new ArraySegment<byte>(buffer.Array, buffer.Offset + dataSize, buffer.Count - dataSize);
}
return processLen;
}
public abstract void OnRecvPacket(ArraySegment<byte> buffer);
}
public abstract class Session
{
Socket _socket;
int _disconnected = 0;
RecvBuffer _recvBuffer = new RecvBuffer(65535);
object _lock = new object();
Queue<ArraySegment<byte>> _sendQueue = new Queue<ArraySegment<byte>>();
List<ArraySegment<byte>> _pendingList = new List<ArraySegment<byte>>();
SocketAsyncEventArgs _sendArgs = new SocketAsyncEventArgs();
SocketAsyncEventArgs _recvArgs = new SocketAsyncEventArgs();
public abstract void OnConnected(EndPoint endPoint);
public abstract int OnRecv(ArraySegment<byte> 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<SocketAsyncEventArgs>(OnRecvCompleted);
_sendArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnSendCompleted);
RegisterRecv();
}
public void Send(List<ArraySegment<byte>> sendBuffList)
{
if (sendBuffList.Count == 0)
return;
lock (_lock)
{
foreach (ArraySegment<byte> sendBuff in sendBuffList)
_sendQueue.Enqueue(sendBuff);
if (_pendingList.Count == 0)
RegisterSend();
}
}
public void Send(ArraySegment<byte> 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<byte> 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<byte> 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
}
}

View File

@ -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; }
}
}

View File

@ -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
{
/// <summary>
/// Get SqlServer Connection String
/// </summary>
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<TEntity>? GetEntity<TEntity>(this DbContext dbContext) where TEntity : class
{
IEnumerable<TEntity>? 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<TEntity>;
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<IDbContextTransaction> CreateTransactionAsync(this DbContext dbContext)
{
return await dbContext.Database.BeginTransactionAsync();
}
public static async Task<bool> 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
}
}

View File

@ -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>(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 = @$"<?xml version=""1.0"" encoding=""utf-8"" ?>
<configuration>
<log4net>
<root>
<level value=""ALL""/>
<appender-ref ref=""Console""/>
<appender-ref ref=""file""/>
<appender-ref ref=""fatal_file""/>
</root>
<appender name=""Console"" type=""log4net.Appender.ManagedColoredConsoleAppender"">
<layout type=""log4net.Layout.PatternLayout"">
<conversionPattern value=""[%date] [%thread] %-6level: %message%newline"" />
</layout>
<mapping>
<level value=""FATAL"" />
<foreColor value=""White"" />
<backColor value=""Red"" />
</mapping>
<mapping>
<level value=""ERROR"" />
<foreColor value=""Red"" />
</mapping>
<mapping>
<level value=""WARN"" />
<foreColor value=""Yellow"" />
</mapping>
<mapping>
<level value=""INFO"" />
<foreColor value=""Green"" />
</mapping>
<mapping>
<level value=""DEBUG"" />
<foreColor value=""Blue"" />
</mapping>
<mapping>
<level value=""DB"" />
<foreColor value=""DarkMagenta"" />
</mapping>
<mapping>
<level value=""HTTP"" />
<foreColor value=""DarkYellow"" />
</mapping>
<mapping>
<level value=""SOCKET"" />
<foreColor value=""DarkCyan"" />
</mapping>
</appender>
<appender name=""file"" type=""log4net.Appender.RollingFileAppender"">
<file value=""log/"" />
<datepattern value=""yyyy////MM////yyyy-MM-dd'.log'""/>
<appendToFile value=""true"" />
<rollingStyle value=""Date"" />
<staticLogFileName value=""false"" />
<layout type=""log4net.Layout.PatternLayout"">
<conversionPattern value=""[%date] [%thread] %level %logger - %message%newline"" />
</layout>
</appender>
<appender name=""fatal_file"" type=""log4net.Appender.RollingFileAppender"">
<file value=""log/"" />
<datepattern value=""yyyy////MM////yyyy-MM-dd'_fatal.log'""/>
<appendToFile value=""true"" />
<rollingStyle value=""Date"" />
<staticLogFileName value=""false"" />
<filter type=""log4net.Filter.LevelRangeFilter"">
<param name=""LevelMin"" value=""FATAL"" />
<param name=""LevelMax"" value=""FATAL"" />
</filter>
<layout type=""log4net.Layout.PatternLayout"">
<conversionPattern value=""[%date] [%thread] %level %logger - %message%newline"" />
</layout>
</appender>
</log4net>
</configuration>";
#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>(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 = @$"<?xml version=""1.0"" encoding=""utf-8"" ?>
<configuration>
<log4net>
<root>
<level value=""ALL""/>
<appender-ref ref=""Console""/>
<appender-ref ref=""file""/>
<appender-ref ref=""fatal_file""/>
</root>
<appender name=""Console"" type=""log4net.Appender.ManagedColoredConsoleAppender"">
<layout type=""log4net.Layout.PatternLayout"">
<conversionPattern value=""[%date] [%thread] %-6level: %message%newline"" />
</layout>
<mapping>
<level value=""FATAL"" />
<foreColor value=""White"" />
<backColor value=""Red"" />
</mapping>
<mapping>
<level value=""ERROR"" />
<foreColor value=""Red"" />
</mapping>
<mapping>
<level value=""WARN"" />
<foreColor value=""Yellow"" />
</mapping>
<mapping>
<level value=""INFO"" />
<foreColor value=""Green"" />
</mapping>
<mapping>
<level value=""DEBUG"" />
<foreColor value=""Blue"" />
</mapping>
<mapping>
<level value=""DB"" />
<foreColor value=""DarkMagenta"" />
</mapping>
<mapping>
<level value=""HTTP"" />
<foreColor value=""DarkYellow"" />
</mapping>
<mapping>
<level value=""SOCKET"" />
<foreColor value=""DarkCyan"" />
</mapping>
</appender>
<appender name=""file"" type=""log4net.Appender.RollingFileAppender"">
<file value=""log/"" />
<datepattern value=""yyyy////MM////yyyy-MM-dd'.log'""/>
<appendToFile value=""true"" />
<rollingStyle value=""Date"" />
<staticLogFileName value=""false"" />
<layout type=""log4net.Layout.PatternLayout"">
<conversionPattern value=""[%date] [%thread] %level %logger - %message%newline"" />
</layout>
</appender>
<appender name=""fatal_file"" type=""log4net.Appender.RollingFileAppender"">
<file value=""log/"" />
<datepattern value=""yyyy////MM////yyyy-MM-dd'_fatal.log'""/>
<appendToFile value=""true"" />
<rollingStyle value=""Date"" />
<staticLogFileName value=""false"" />
<filter type=""log4net.Filter.LevelRangeFilter"">
<param name=""LevelMin"" value=""FATAL"" />
<param name=""LevelMax"" value=""FATAL"" />
</filter>
<layout type=""log4net.Layout.PatternLayout"">
<conversionPattern value=""[%date] [%thread] %level %logger - %message%newline"" />
</layout>
</appender>
</log4net>
</configuration>";
#endregion
}
/*
log4net 지정 가능 색상

View File

@ -17,6 +17,7 @@
<ItemGroup>
<PackageReference Include="log4net" Version="3.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.15" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>

View File

@ -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();
}
}

View File

@ -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
{