using System; using System.Net; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; //IP Range Updater namespace Mistox.Client { public class mTCPClient : IDisposable { private readonly TcpClient _connection; private readonly MistoxClient _parent; private readonly SemaphoreSlim _sendLock = new SemaphoreSlim(1, 1); public mTCPClient( MistoxClient parent, IPEndPoint ServerAddress ) { _connection = new TcpClient( AddressFamily.InterNetwork ); _connection.NoDelay = true; _connection.Connect( ServerAddress ); _parent.InvokeOnServerConnected(); // Start the receive thread Task.Run(ReceiveThread); } async Task ReceiveThread() { using( NetworkStream ns = _connection.GetStream() ) { try { byte[] headerBuffer = new byte[DataPacket.HeaderLength]; byte[] payloadBuffer = new byte[65535]; // Upper limit for safety while( _parent.Alive ) { // Read the header off await ns.ReadExactlyAsync(headerBuffer, 0, 3); // Parse the header DataPacketHeader Header = new DataPacketHeader { Type = headerBuffer[0], DataLength = (ushort)((headerBuffer[1] << 8) | headerBuffer[2]) }; // Make sure the data looks valid if (Header.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, Header.DataLength); // Create the dataobject dynamic dataobject = DataPacket.Deserialize( Header, payloadBuffer.AsMemory(DataPacket.HeaderLength, Header.DataLength) ); // Return the object _parent.InvokeOnReceived(dataobject); } } catch (Exception e) { _parent.Alive = false; Dispose(); Console.WriteLine("TCP Receive exception: " + e.Message); } } } public async Task Send(T packet) { byte[] data = DataPacket.Serialize( packet ); await _sendLock.WaitAsync(); try { await _connection.GetStream().WriteAsync( data, 0, data.Length ); } finally { _sendLock.Release(); } } public void Dispose() { _parent.InvokeOnServerDisconnected(); _connection.Close(); _connection.Dispose(); } } }