using System; using System.Net; using System.Net.Sockets; using System.Numerics; using System.Threading; using System.Threading.Tasks; namespace Mistox.Server { public class ServerConnection : IDisposable { public string UserName { get; set; } = ""; // The Players UserName public bool IsAuthenticated { get; set; } = false; // Has the player authenticated public Vector3 Position { get; set; } // The ingame position of the player public DateTime LastHeartbeat { get; set; } = DateTime.UtcNow; // Connection heartbeat for integrity public bool Connected { get; set; } = true; // Is the connection valid ServerEntity _parent; TcpClient _client; IPEndPoint _remoteAddress; public ServerConnection( TcpClient tcpClient, ServerEntity parent, IPEndPoint remote ) { _parent = parent; _client = tcpClient; _remoteAddress = remote; _client.NoDelay = true; ServerInterface.ServerInstance.InvokeUserConnected( this ); // Start the receive thread Thread receiveThread = new Thread(async () => await ReceiveThread() ); receiveThread.Start(); } async Task ReceiveThread() { using( NetworkStream ns = _client.GetStream() ) { try { byte[] headerBuffer = new byte[DataPacket.HeaderLength]; byte[] payloadBuffer = new byte[65535]; // Upper limit for safety while( Connected && _parent.Alive ) { // Tick the last heartbeat or drop DateTime now = DateTime.UtcNow; if (LastHeartbeat.AddSeconds(_parent.ConnectionTimeoutSeconds) >= now) { Connected = false; Dispose(); break; } LastHeartbeat = DateTime.UtcNow; // Read the header off await ns.ReadExactlyAsync(headerBuffer, 0, 3); // Parse the header byte packetbyte = headerBuffer[0]; ushort dataLength = (ushort)((headerBuffer[1] << 8) | headerBuffer[2]); // Make sure the data looks valid if (dataLength > payloadBuffer.Length) { throw new Exception("Incoming TCP payload is too large for the pre-allocated buffer."); } // Read off the data await ns.ReadExactlyAsync(payloadBuffer, 0, dataLength); // Create the dataobject dynamic dataobject = DataPacket.Deserialize( packetbyte, dataLength, payloadBuffer.AsMemory(DataPacket.HeaderLength, dataLength) ); // Return the object _parent.InvokeDataReceived(dataobject); } } catch (Exception e) { _parent.Alive = false; Dispose(); Console.WriteLine("TCP Receive exception: " + e.Message); } } } public async Task Send( T packet ) { byte[] byteData = DataPacket.Serialize( packet ); await _client.GetStream().WriteAsync( byteData, 0, byteData.Length ); } public void Dispose() { Connected = false; _parent.InvokeUserDisconnected( this ); Connected = false; _parent.Alive = false; // Remove this item from the Dictionary on the Server for garbage collection } } }