using SObject; using STcpHelper.Packet; using System.Net; using System.Net.Sockets; using System.Text; namespace STcpHelper { public class STcpServer { private CancellationTokenSource _cts; private List _clients; private readonly int HEADER_SIZE = STcpPacketHeader.HEADER_LENGTH; private readonly int BUFFER_SIZE; private int _port; private TcpListener _listener; public event EventHandler MessageCallback; public event EventHandler ErrorCallback; public STcpServer(int port, int bufferSize = 1024) { _port = port; BUFFER_SIZE = bufferSize; _clients = new List(); } public async void Start() { _listener = new TcpListener(IPAddress.Parse("127.0.0.1"), _port); _listener.Start(); WriteMessage("Server start..."); _cts = new CancellationTokenSource(); while (true) { if (_cts.IsCancellationRequested) break; TcpClient client = await _listener.AcceptTcpClientAsync(); _clients.Add(client); WriteMessage($"Client accepted({client.Client.RemoteEndPoint.ToString()})"); _cts = new CancellationTokenSource(); _ = Task.Run(async () => { using (NetworkStream stream = client.GetStream()) { byte[] headerBuffer = new byte[HEADER_SIZE]; byte[] dataBuffer = new byte[BUFFER_SIZE]; try { while (true) { int headerBytesRead = await stream.ReadAsync(headerBuffer, 0, HEADER_SIZE); if (headerBytesRead != HEADER_SIZE) { WriteError(new Exception("Cannot read header")); return; } STcpPacketHeader header = new STcpPacketHeader(headerBuffer); int dataSize = header.DataLength; int totalRecv = 0; while (totalRecv < dataSize) { int dataBytesRead = await stream.ReadAsync(dataBuffer, totalRecv, dataSize - totalRecv); totalRecv += dataBytesRead; } WriteMessage($"[{client.Client.RemoteEndPoint.ToString()}] Type: {header.Type.ToString()}"); if (header.Type == PacketType.TEXT) { STcpTextPacket packet = new STcpTextPacket(dataBuffer); // Broadcast BroadcastMessage(packet.Text, client); } else if (header.Type == PacketType.REQ_CLIENT_LIST) { STcpClientListResPacket packet = new STcpClientListResPacket(_clients); // Send SendPacket(packet, client); } else if (header.Type == PacketType.REQ_FILE) { STcpFileReqPacket recvPacket = new STcpFileReqPacket(dataBuffer); STcpFileResPacket packet = new STcpFileResPacket(recvPacket.Path); // Send async SendAsyncPacket(packet, client); } else if (header.Type == PacketType.REQ_ZOO) { //Zoo zoo = new Zoo(); //Random rand = new Random(); //zoo.Dogs = new List() //{ // new Wolf($"Wooolf-{rand.Next(1, 10)}", rand.Next(1, 10)), // new Coyote($"Coyote-{rand.Next(1, 10)}", rand.Next(1, 10)), // new Wolf($"Wooolf-{rand.Next(1, 10)}", rand.Next(1, 10)), // new Coyote($"Coyote-{rand.Next(1, 10)}", rand.Next(1, 10)), // new Coyote($"Coyote-{rand.Next(1, 10)}", rand.Next(1, 10)), // new Wolf($"Wooolf-{rand.Next(1, 10)}", rand.Next(1, 10)), // new Wolf($"Wooolf-{rand.Next(1, 10)}", rand.Next(1, 10)), //}; STcpZooResPacket packet = new STcpZooResPacket(new Zoo()); SendPacket(packet, client); } if (!IsClientConnected(client)) break; } } catch (Exception ex) { WriteError(ex); } WriteMessage($"Client disconnected({client.Client.RemoteEndPoint.ToString()})"); client.Close(); _clients.Remove(client); } }, _cts.Token); } } public void Stop() { _cts.Cancel(); } private byte[] MakeTextPacket(string text) { STcpTextPacket packet = new STcpTextPacket(text); return packet.Serialize(); } private void SendPacket(ISTcpPacket packet, TcpClient requester) { byte[] responseByte = packet.Serialize(); if (!IsClientConnected(requester)) return; if (responseByte.Length < BUFFER_SIZE) requester.GetStream().WriteAsync(responseByte, 0, responseByte.Length); else { int totalSend = 0; while (totalSend < responseByte.Length) { int sendCount = responseByte.Length - totalSend < BUFFER_SIZE ? responseByte.Length - totalSend : BUFFER_SIZE; requester.GetStream().WriteAsync(responseByte, totalSend, sendCount); totalSend += sendCount; } } } private async void SendAsyncPacket(ISTcpAsyncPacket packet, TcpClient requester) { byte[] responseByte = await packet.Serialize(); if (!IsClientConnected(requester)) return; requester.GetStream().WriteAsync(responseByte, 0, responseByte.Length); } private void BroadcastMessage(string message, TcpClient sender) { byte[] resonseByte = MakeTextPacket(message); foreach (TcpClient client in _clients) { if (client != sender && IsClientConnected(client)) client.GetStream().WriteAsync(resonseByte, 0, resonseByte.Length); } } private bool IsClientConnected(TcpClient client) { try { if (client != null && client.Client != null && client.Client.Connected) return true; } catch (Exception ex) { WriteError(ex); } return false; } private void WriteMessage(string message) { if (this.MessageCallback != null) this.MessageCallback(this, message); } private void WriteError(Exception ex) { if (this.ErrorCallback != null) this.ErrorCallback(this, ex); } } }