diff --git a/Chat/Client/Singleton.cs b/Chat/Client/Singleton.cs index 0dbb40f..c10d926 100644 --- a/Chat/Client/Singleton.cs +++ b/Chat/Client/Singleton.cs @@ -13,6 +13,8 @@ namespace Client { public static Singleton Instance { get; set; } = new Singleton(); + public readonly int HEARTBEAT_INTERVAL = 5000; + public string Id { get; set; } = ""; public string Nickname { get; set; } = ""; public Socket Socket { get; } = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); @@ -45,6 +47,13 @@ namespace Client Socket socket = (Socket)sender; byte[] headerBuffer = new byte[2]; + System.Timers.Timer timer = new System.Timers.Timer(HEARTBEAT_INTERVAL); + timer.Elapsed += async (sender, e) => + { + HeartbeatPacket packet = new HeartbeatPacket(); + await socket.SendAsync(packet.Serialize(), SocketFlags.None); + }; + try { while (true) @@ -54,6 +63,8 @@ namespace Client if (n1 < 1) { Console.WriteLine($"[{DateTime.Now}] Disconnect server: {socket.RemoteEndPoint}"); + + timer.Stop(); socket.Dispose(); return; } @@ -78,6 +89,8 @@ namespace Client { LoginResponsePacket packet = new LoginResponsePacket(dataBuffer); LoginResponsed?.Invoke(packet, EventArgs.Empty); + + timer.Start(); } else if (packetType == PacketType.CreateRoomResponse) { diff --git a/Chat/Core/HeartbeatPacket.cs b/Chat/Core/HeartbeatPacket.cs new file mode 100644 index 0000000..d53baed --- /dev/null +++ b/Chat/Core/HeartbeatPacket.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; + +namespace Core +{ + public class HeartbeatPacket : IPacket + { + public byte[] Serialize() + { + byte[] packetType = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)PacketType.Heartbeat)); + + short dataSize = (short)(packetType.Length); + byte[] header = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(dataSize)); + + byte[] buffer = new byte[2 + dataSize]; + + int cursor = 0; + Array.Copy(header, 0, buffer, cursor, header.Length); + cursor += header.Length; + + Array.Copy(packetType, 0, buffer, cursor, packetType.Length); + + return buffer; + } + } +} diff --git a/Chat/Core/PServer.cs b/Chat/Core/PServer.cs index 43e3c5a..c23ea3a 100644 --- a/Chat/Core/PServer.cs +++ b/Chat/Core/PServer.cs @@ -11,6 +11,9 @@ namespace Core { public class PServer { + private readonly int HEARTBEAT_INTERVAL = 5000; + private readonly int HEARTBEAT_THREASHOLD = 5; + private Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); public ConcurrentDictionary Rooms { get; } = new ConcurrentDictionary(); @@ -56,8 +59,19 @@ namespace Core { while (true) { + Task task1 = Task.Delay(HEARTBEAT_INTERVAL * HEARTBEAT_THREASHOLD); // header buffer - int n1 = await clientSocket.ReceiveAsync(headerBuffer, SocketFlags.None); + Task task2 = clientSocket.ReceiveAsync(headerBuffer, SocketFlags.None); + + Task result = await Task.WhenAny(task1, task2); + if (result == task1) + { + Console.WriteLine($"[{DateTime.Now}] Disconnect client - {clientSocket.RemoteEndPoint}"); + await Remove(id, nickname, roomName, clientSocket); + return; + } + + int n1 = await task2; if (n1 < 1) { Console.WriteLine($"[{DateTime.Now}] Disconnect client - {clientSocket.RemoteEndPoint}"); @@ -195,6 +209,10 @@ namespace Core } } } + else if (packetType == PacketType.Heartbeat) + { + Console.WriteLine($"[{DateTime.Now}] Heartbeat from client - {clientSocket.RemoteEndPoint} (id: {id}, nickname: {nickname})"); + } } } catch (Exception ex) diff --git a/Chat/Core/PacketType.cs b/Chat/Core/PacketType.cs index a686746..1218db1 100644 --- a/Chat/Core/PacketType.cs +++ b/Chat/Core/PacketType.cs @@ -19,6 +19,7 @@ namespace Core UserEnter, UserLeave, Chat, - Duplicate + Duplicate, + Heartbeat, } }