diff --git a/SocketStudy/Client/PClientSocket.cs b/SocketStudy/Client/PClientSocket.cs index 26e234f..8d5d9c6 100644 --- a/SocketStudy/Client/PClientSocket.cs +++ b/SocketStudy/Client/PClientSocket.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; +using System.Threading; using System.Threading.Tasks; namespace Client @@ -19,6 +20,13 @@ namespace Client // create socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); endPoint = new IPEndPoint(IPAddress.Parse(ip), port); + sock.Connect(endPoint); + } + + public void Send(string msg) + { + byte[] buff = Encoding.UTF8.GetBytes(msg); + int sendSize = sock.Send(buff, SocketFlags.None); } public void Connect() @@ -47,5 +55,18 @@ namespace Client sock.Close(); } + + public void ContinuousSend() + { + byte[] buff = Encoding.UTF8.GetBytes("Hello TCP"); + + while (true) + { + int sendSize = sock.Send(buff, SocketFlags.None); + //Console.WriteLine($"send size: {sendSize}"); + + Thread.Sleep(500); + } + } } } diff --git a/SocketStudy/Client/Program.cs b/SocketStudy/Client/Program.cs index b2d3bd2..5a6c0f8 100644 --- a/SocketStudy/Client/Program.cs +++ b/SocketStudy/Client/Program.cs @@ -2,16 +2,34 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; namespace Client { class Program { + private static readonly int CLIENT_COUNT = 100; + static void Main(string[] args) { - PClientSocket clientSocket = new PClientSocket("127.0.0.1", 7777); - clientSocket.Connect(); + List clientList = new List(); + + for (int i = 0; i < CLIENT_COUNT; i++) + { + PClientSocket clientSocket = new PClientSocket("127.0.0.1", 7777); + clientList.Add(clientSocket); + } + + while (true) + { + for (int i = 0; i < CLIENT_COUNT; i++) + { + clientList[i].Send($"Hello from client{i}"); + } + + Thread.Sleep(100); + } Console.ReadKey(); } diff --git a/SocketStudy/Server_Listener/App.config b/SocketStudy/Server_Listener/App.config new file mode 100644 index 0000000..aee9adf --- /dev/null +++ b/SocketStudy/Server_Listener/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/SocketStudy/Server_Listener/Communication/PClient.cs b/SocketStudy/Server_Listener/Communication/PClient.cs new file mode 100644 index 0000000..0e1a140 --- /dev/null +++ b/SocketStudy/Server_Listener/Communication/PClient.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; + +namespace Server_Listener.Communication +{ + class PClient + { + private readonly int BUFF_SIZE = 8192; + + public delegate void ClientReceivedHandler(PClient sender, byte[] data); + public event ClientReceivedHandler Received; + + public delegate void ClientDisconnectedHandler(PClient sender); + public event ClientDisconnectedHandler Disconnected; + + public string ID { get; private set; } + + public IPEndPoint EndPoint { get; private set; } + + public bool Connected { get { return socket.Connected; } } + + private Socket socket; + + public PClient(Socket accepted) + { + socket = accepted; + this.ID = Guid.NewGuid().ToString(); + this.EndPoint = (IPEndPoint)socket.RemoteEndPoint; + socket.BeginReceive(new byte[] { 0 }, 0, 0, 0, AcceptCallback, null); + } + + private void AcceptCallback(IAsyncResult ar) + { + try + { + socket.EndReceive(ar); + + byte[] buff = new byte[BUFF_SIZE]; + + int receiveSize = socket.Receive(buff, buff.Length, SocketFlags.None); + if (receiveSize <= 0) + { + Close(); + + if (Disconnected != null) + Disconnected(this); + } + else if (receiveSize < buff.Length) + { + Array.Resize(ref buff, receiveSize); + } + + if (Received != null) + Received(this, buff); + + socket.BeginReceive(new byte[] { 0 }, 0, 0, 0, AcceptCallback, null); + } + catch (Exception ex) + { + this.Close(); + + if (Disconnected != null) + Disconnected(this); + + Debug.WriteLine($"[ERROR] CleintAcceptCallback: {ex.Message}"); + } + } + + public void Close() + { + socket.Close(); + socket.Dispose(); + } + } +} diff --git a/SocketStudy/Server_Listener/Communication/PClientInfo.cs b/SocketStudy/Server_Listener/Communication/PClientInfo.cs new file mode 100644 index 0000000..ff8ea7f --- /dev/null +++ b/SocketStudy/Server_Listener/Communication/PClientInfo.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Server_Listener.Communication +{ + class PClientInfo + { + public PClient Client { get; set; } + public string LastMessage { get; set; } + public DateTime CreatedAt { get; set; } + public DateTime LastReceivedAt { get; set; } + } +} diff --git a/SocketStudy/Server_Listener/Communication/PListener.cs b/SocketStudy/Server_Listener/Communication/PListener.cs new file mode 100644 index 0000000..6247408 --- /dev/null +++ b/SocketStudy/Server_Listener/Communication/PListener.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; +using System.Timers; + +namespace Server_Listener.Communication +{ + delegate void SocketAcceptedHandler(object sender, SocketAcceptedEventArgs e); + + class PListener + { + public Socket BaseSocket { get; private set; } + + public bool IsRunning { get; private set; } = false; + + public int Port { get; private set; } = -1; + + public event SocketAcceptedHandler SocketAccepted; + + public PListener(int port) + { + this.Port = port; + } + + public void Start() + { + if (this.IsRunning) + return; + + this.IsRunning = true; + this.BaseSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + this.BaseSocket.Bind(new IPEndPoint(IPAddress.Any, this.Port)); + this.BaseSocket.Listen(100); + this.BaseSocket.BeginAccept(AcceptCallback, null); + } + + public void Stop() + { + if (!IsRunning) + return; + + this.IsRunning = false; + this.BaseSocket.Close(); + } + + private void AcceptCallback(IAsyncResult ar) + { + try + { + Socket socket = this.BaseSocket.EndAccept(ar); + if (SocketAccepted != null) + SocketAccepted(this, new SocketAcceptedEventArgs(socket)); + + if (IsRunning) + this.BaseSocket.BeginAccept(AcceptCallback, null); + } + catch (Exception ex) + { + Debug.WriteLine($"[ERROR] ServerAcceptCallback: {ex.Message}"); + } + } + } +} diff --git a/SocketStudy/Server_Listener/Communication/PServer.cs b/SocketStudy/Server_Listener/Communication/PServer.cs new file mode 100644 index 0000000..279a668 --- /dev/null +++ b/SocketStudy/Server_Listener/Communication/PServer.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Timers; + +namespace Server_Listener.Communication +{ + class PServer + { + public delegate void ClientDataReceivedHandler(PClientInfo sender, byte[] data); + public event ClientDataReceivedHandler OnDataReceived; + + private PListener listener; + private Dictionary clientInfoDict; + + private Timer garbageTimer; + + private object garbageLock = new object(); + + public PServer(int port) + { + listener = new PListener(port); + clientInfoDict = new Dictionary(); + + listener.SocketAccepted += Listener_SocketAccepted; + + garbageTimer = new Timer(); + garbageTimer.Interval = 10 * 1000; + garbageTimer.Elapsed += GarbageTimer_Elapsed; + } + + public void Start() + { + listener.Start(); + garbageTimer.Start(); + } + + private void Listener_SocketAccepted(object sender, SocketAcceptedEventArgs e) + { + PClient client = new PClient(e.Accepted); + client.Received += Client_Received; + client.Disconnected += Client_Disconnected; + + Debug.WriteLine($"Client accepted: {client.ID} @ { DateTime.Now}"); + } + + private void Client_Disconnected(PClient sender) + { + if (clientInfoDict.ContainsKey(sender.ID)) + { + lock (garbageLock) + { + clientInfoDict.Remove(sender.ID); + } + } + } + + private void Client_Received(PClient sender, byte[] data) + { + lock (garbageLock) + { + if (!clientInfoDict.ContainsKey(sender.ID)) + clientInfoDict.Add(sender.ID, new PClientInfo()); + + DateTime now = DateTime.Now; + + clientInfoDict[sender.ID].Client = sender; + clientInfoDict[sender.ID].CreatedAt = now; + clientInfoDict[sender.ID].LastReceivedAt = now; + } + + string msg = Encoding.UTF8.GetString(data); + clientInfoDict[sender.ID].LastMessage = msg; + + if (OnDataReceived != null && clientInfoDict.ContainsKey(sender.ID)) + OnDataReceived(clientInfoDict[sender.ID], data); + + Debug.WriteLine(($"Client message({sender.ID}): {msg} @ { DateTime.Now}")); + } + + private void GarbageTimer_Elapsed(object sender, ElapsedEventArgs e) + { + List garbageKeys = new List(); + foreach (KeyValuePair item in clientInfoDict) + { + if (!item.Value.Client.Connected) + garbageKeys.Add(item.Key); + } + + foreach (string key in garbageKeys) + { + clientInfoDict.Remove(key); + } + } + } +} diff --git a/SocketStudy/Server_Listener/Communication/SocketAcceptedEventArgs.cs b/SocketStudy/Server_Listener/Communication/SocketAcceptedEventArgs.cs new file mode 100644 index 0000000..f7d7b3e --- /dev/null +++ b/SocketStudy/Server_Listener/Communication/SocketAcceptedEventArgs.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; + +namespace Server_Listener.Communication +{ + class SocketAcceptedEventArgs : EventArgs + { + public Socket Accepted { get; private set; } + + public IPAddress Address { get; private set; } + + public IPEndPoint EndPoint { get; private set; } + + public SocketAcceptedEventArgs(Socket socket) + { + this.Accepted = socket; + this.EndPoint = (IPEndPoint)socket.RemoteEndPoint; + this.Address = this.EndPoint.Address; + + } + } +} diff --git a/SocketStudy/Server_Listener/Program.cs b/SocketStudy/Server_Listener/Program.cs new file mode 100644 index 0000000..7c7fb7f --- /dev/null +++ b/SocketStudy/Server_Listener/Program.cs @@ -0,0 +1,31 @@ +using Server_Listener.Communication; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Sockets; +using System.Text; +using System.Threading.Tasks; + +namespace Server_Listener +{ + + class Program + { + private static PServer server; + + static void Main(string[] args) + { + server = new PServer(7777); + server.OnDataReceived += Server_OnDataReceived; + + server.Start(); + + Console.ReadKey(); + } + + private static void Server_OnDataReceived(PClientInfo sender, byte[] data) + { + Console.WriteLine($"{sender.Client.EndPoint.Port}@{sender.LastReceivedAt}: {sender.LastMessage}"); + } + } +} diff --git a/SocketStudy/Server_Listener/Properties/AssemblyInfo.cs b/SocketStudy/Server_Listener/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..2d4bfd4 --- /dev/null +++ b/SocketStudy/Server_Listener/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// 어셈블리에 대한 일반 정보는 다음 특성 집합을 통해 +// 제어됩니다. 어셈블리와 관련된 정보를 수정하려면 +// 이러한 특성 값을 변경하세요. +[assembly: AssemblyTitle("Server_Listener")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Server_Listener")] +[assembly: AssemblyCopyright("Copyright © 2022")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// ComVisible을 false로 설정하면 이 어셈블리의 형식이 COM 구성 요소에 +// 표시되지 않습니다. COM에서 이 어셈블리의 형식에 액세스하려면 +// 해당 형식에 대해 ComVisible 특성을 true로 설정하세요. +[assembly: ComVisible(false)] + +// 이 프로젝트가 COM에 노출되는 경우 다음 GUID는 typelib의 ID를 나타냅니다. +[assembly: Guid("420f59ce-1ed7-4194-9dca-fcb6428afca3")] + +// 어셈블리의 버전 정보는 다음 네 가지 값으로 구성됩니다. +// +// 주 버전 +// 부 버전 +// 빌드 번호 +// 수정 버전 +// +// 모든 값을 지정하거나 아래와 같이 '*'를 사용하여 빌드 번호 및 수정 번호를 +// 기본값으로 할 수 있습니다. +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/SocketStudy/Server_Listener/Server_Client_Listener.csproj b/SocketStudy/Server_Listener/Server_Client_Listener.csproj new file mode 100644 index 0000000..98f3e72 --- /dev/null +++ b/SocketStudy/Server_Listener/Server_Client_Listener.csproj @@ -0,0 +1,58 @@ + + + + + Debug + AnyCPU + {420F59CE-1ED7-4194-9DCA-FCB6428AFCA3} + Exe + Server_Listener + Server_Listener + v4.8.1 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SocketStudy/SocketStudy.sln b/SocketStudy/SocketStudy.sln index 4021734..224e3d4 100644 --- a/SocketStudy/SocketStudy.sln +++ b/SocketStudy/SocketStudy.sln @@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server_Single", "Server\Ser EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server_Async", "Server_Async\Server_Async.csproj", "{94E9926E-CD35-49F8-867E-2E8B76E7FB1B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server_Client_Listener", "Server_Listener\Server_Client_Listener.csproj", "{420F59CE-1ED7-4194-9DCA-FCB6428AFCA3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,6 +29,10 @@ Global {94E9926E-CD35-49F8-867E-2E8B76E7FB1B}.Debug|Any CPU.Build.0 = Debug|Any CPU {94E9926E-CD35-49F8-867E-2E8B76E7FB1B}.Release|Any CPU.ActiveCfg = Release|Any CPU {94E9926E-CD35-49F8-867E-2E8B76E7FB1B}.Release|Any CPU.Build.0 = Release|Any CPU + {420F59CE-1ED7-4194-9DCA-FCB6428AFCA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {420F59CE-1ED7-4194-9DCA-FCB6428AFCA3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {420F59CE-1ED7-4194-9DCA-FCB6428AFCA3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {420F59CE-1ED7-4194-9DCA-FCB6428AFCA3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE