using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading.Tasks; 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(); public ConcurrentDictionary Clients { get; } = new ConcurrentDictionary(); public PServer(string ip, int port, int backlog) { IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(ip), port); serverSocket.Bind(endPoint); serverSocket.Listen(backlog); } public async Task StartAsync() { while (true) { try { Socket clientSocket = await serverSocket.AcceptAsync(); Console.WriteLine($"[{DateTime.Now}] Accept client: {clientSocket.RemoteEndPoint}"); ThreadPool.QueueUserWorkItem(RunAsync, clientSocket); } catch (Exception ex) { Console.WriteLine(ex); } } } private async void RunAsync(object? sender) { if (sender == null) return; Socket clientSocket = (Socket)sender; byte[] headerBuffer = new byte[2]; string id = ""; string nickname = ""; string roomName = ""; try { while (true) { Task task1 = Task.Delay(HEARTBEAT_INTERVAL * HEARTBEAT_THREASHOLD); // header buffer 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}"); await Remove(id, nickname, roomName, clientSocket); return; } else if (n1 == 1) { await clientSocket.ReceiveAsync(new ArraySegment(headerBuffer, 1, 1), SocketFlags.None); } // data buffer short dataSize = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(headerBuffer)); byte[] dataBuffer = new byte[dataSize]; int totalRecv = 0; while (totalRecv < dataSize) { int n2 = await clientSocket.ReceiveAsync(new ArraySegment(dataBuffer, totalRecv, dataSize - totalRecv), SocketFlags.None); totalRecv += n2; } PacketType packetType = (PacketType)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(dataBuffer)); if (packetType == PacketType.LoginRequest) { LoginRequestPacket requestPacket = new LoginRequestPacket(dataBuffer); Clients.AddOrUpdate(requestPacket.Id, clientSocket, (oldKey, oldValue) => { DuplicatePacket packet = new DuplicatePacket(); oldValue.Send(packet.Serialize()); return clientSocket; }); Console.WriteLine($"[{DateTime.Now}] LoginRequest - Id: {requestPacket.Id}, Nickname: {requestPacket.NickName}"); id = requestPacket.Id; nickname = requestPacket.NickName; LoginResponsePacket responsePacket = new LoginResponsePacket(StatusCode.Success); await clientSocket.SendAsync(responsePacket.Serialize(), SocketFlags.None); } else if (packetType == PacketType.CreateRoomRequest) { CreateRoomRequestPacket requestPacket = new CreateRoomRequestPacket(dataBuffer); Room room = new Room(); if (Rooms.TryAdd(requestPacket.RoomName, room)) { roomName = requestPacket.RoomName; room.Users.TryAdd(id, nickname); Console.WriteLine($"[{DateTime.Now}] CreateRoomRequest - RoomName: {roomName}, id: {id}, nickname: {nickname}"); CreateRoomResponsePacket responsePacket = new CreateRoomResponsePacket(StatusCode.Success); await clientSocket.SendAsync(responsePacket.Serialize(), SocketFlags.None); } else { Console.WriteLine($"[{DateTime.Now}] CreateRoomRequest failed - RoomName: {requestPacket.RoomName}"); CreateRoomResponsePacket responsePacket = new CreateRoomResponsePacket(StatusCode.Failed); await clientSocket.SendAsync(responsePacket.Serialize(), SocketFlags.None); } } else if (packetType == PacketType.RoomListRequest) { RoomListResponsePacket packet = new RoomListResponsePacket(Rooms.Keys); await clientSocket.SendAsync(packet.Serialize(), SocketFlags.None); } else if (packetType == PacketType.EnterRoomRequest) { EnterRoomRequestPacket requestPacket = new EnterRoomRequestPacket(dataBuffer); if (Rooms.TryGetValue(requestPacket.RoomName, out var room)) { roomName = requestPacket.RoomName; room.Users.TryAdd(id, nickname); Console.WriteLine($"[{DateTime.Now}] EnterRoomRequest - RoomName: {roomName}, id: {id}, nickname: {nickname}"); EnterRoomResponsePacket responsePacket = new EnterRoomResponsePacket(StatusCode.Success); await clientSocket.SendAsync(responsePacket.Serialize(), SocketFlags.None); await Task.Delay(100); foreach (var user in room.Users) { if (user.Value == nickname) continue; // add me to other user if (Clients.TryGetValue(user.Key, out var otherClient)) { UserEnterPacket enterPacket = new UserEnterPacket(nickname); await otherClient.SendAsync(enterPacket.Serialize(), SocketFlags.None); } // add other user to me UserEnterPacket packet = new UserEnterPacket(user.Value); await clientSocket.SendAsync(packet.Serialize(), SocketFlags.None); } } else { Console.WriteLine($"[{DateTime.Now}] EnterRoomRequest failed - RoomName: {requestPacket.RoomName}"); EnterRoomResponsePacket responsePacket = new EnterRoomResponsePacket(StatusCode.Failed); await clientSocket.SendAsync(responsePacket.Serialize(), SocketFlags.None); } } else if (packetType == PacketType.UserLeave) { UserLeavePacket packet = new UserLeavePacket(dataBuffer); if (Rooms.TryGetValue(roomName, out var room)) { room.Users.TryRemove(id, out _); if (room.Users.IsEmpty) Rooms.TryRemove(roomName, out _); roomName = ""; foreach (var user in room.Users) { if (Clients.TryGetValue(user.Key, out var otherClient)) await otherClient.SendAsync(packet.Serialize(), SocketFlags.None); } } } else if (packetType == PacketType.Chat) { ChatPacket packet = new ChatPacket(dataBuffer); if (Rooms.TryGetValue(roomName, out var room)) { foreach (var user in room.Users) { if (Clients.TryGetValue(user.Key, out var otherClient)) await otherClient.SendAsync(packet.Serialize(), SocketFlags.None); } } } else if (packetType == PacketType.Heartbeat) { Console.WriteLine($"[{DateTime.Now}] Heartbeat from client - {clientSocket.RemoteEndPoint} (id: {id}, nickname: {nickname})"); } } } catch (Exception ex) { Console.WriteLine(ex); await Remove(id, nickname, roomName, clientSocket); } } private async Task Remove(string id, string nickname, string roomName, Socket clientSocket) { if (Clients.TryGetValue(id, out var client) && client == clientSocket) Clients.TryRemove(id, out _); if (Rooms.TryGetValue(roomName, out var room)) { UserLeavePacket packet = new UserLeavePacket(nickname); room.Users.TryRemove(id, out _); if (room.Users.IsEmpty) Rooms.TryRemove(roomName, out _); roomName = ""; foreach (var user in room.Users) { if (Clients.TryGetValue(user.Key, out var otherClient)) await otherClient.SendAsync(packet.Serialize(), SocketFlags.None); } } clientSocket.Dispose(); } } }