initial commit
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Crysome.Common.Network.FileTransfer;
|
||||
|
||||
public sealed class FtSender
|
||||
{
|
||||
public const int ChunkSize = 1200;
|
||||
|
||||
private const int AbortTimeoutMs = 600000;
|
||||
|
||||
private const int SackWaitMs = 500;
|
||||
|
||||
private const int ChunkPaceMs = 2;
|
||||
|
||||
private readonly CrysomeClient _client;
|
||||
|
||||
private readonly byte[] _data;
|
||||
|
||||
private readonly string _fileName;
|
||||
|
||||
private readonly byte[] _sha256;
|
||||
|
||||
private volatile bool _done;
|
||||
|
||||
private volatile int _ackedChunks;
|
||||
|
||||
public Action<uint, bool, string> OnDone;
|
||||
|
||||
private static int _nextId;
|
||||
|
||||
public uint TransferId { get; }
|
||||
|
||||
public int TotalChunks => Math.Max(1, (_data.Length + 1200 - 1) / 1200);
|
||||
|
||||
public FtSender(CrysomeClient client, string fileName, byte[] data)
|
||||
{
|
||||
_client = client;
|
||||
_data = data;
|
||||
_fileName = fileName;
|
||||
TransferId = (uint)Interlocked.Increment(ref _nextId);
|
||||
using SHA256 sHA = SHA256.Create();
|
||||
_sha256 = sHA.ComputeHash(data);
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
Task.Run((Action)Run);
|
||||
}
|
||||
|
||||
public void HandleDone(FtDonePacket p)
|
||||
{
|
||||
if (p.TransferId == TransferId)
|
||||
{
|
||||
_done = true;
|
||||
OnDone?.Invoke(TransferId, p.Success, p.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public void HandleSack(FtSackPacket p)
|
||||
{
|
||||
if (p.TransferId != TransferId || _done)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (p.HighestContiguous >= 0)
|
||||
{
|
||||
_ackedChunks = Math.Max(_ackedChunks, p.HighestContiguous + 1);
|
||||
}
|
||||
if (p.NackList == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int[] nackList = p.NackList;
|
||||
foreach (int num in nackList)
|
||||
{
|
||||
if (num >= 0 && num < TotalChunks)
|
||||
{
|
||||
SendChunk(num);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Run()
|
||||
{
|
||||
try
|
||||
{
|
||||
int totalChunks = TotalChunks;
|
||||
_client.SendPacket(new FtStartPacket
|
||||
{
|
||||
TransferId = TransferId,
|
||||
FileName = _fileName,
|
||||
TotalChunks = totalChunks,
|
||||
ChunkSize = 1200,
|
||||
TotalBytes = _data.Length,
|
||||
Sha256 = _sha256
|
||||
});
|
||||
Thread.Sleep(50);
|
||||
DateTime utcNow = DateTime.UtcNow;
|
||||
int num = 0;
|
||||
DateTime utcNow2 = DateTime.UtcNow;
|
||||
for (int i = 0; i < totalChunks; i++)
|
||||
{
|
||||
if (_done)
|
||||
{
|
||||
break;
|
||||
}
|
||||
SendChunk(i);
|
||||
if (i % 32 == 31)
|
||||
{
|
||||
Thread.Sleep(2);
|
||||
}
|
||||
if ((DateTime.UtcNow - utcNow).TotalMilliseconds > 600000.0)
|
||||
{
|
||||
_client.SendPacket(new FtAbortPacket
|
||||
{
|
||||
TransferId = TransferId,
|
||||
Reason = "Sender timeout"
|
||||
});
|
||||
OnDone?.Invoke(TransferId, arg2: false, "ERR: Send timeout");
|
||||
return;
|
||||
}
|
||||
}
|
||||
utcNow2 = DateTime.UtcNow;
|
||||
num = _ackedChunks;
|
||||
while (!_done)
|
||||
{
|
||||
Thread.Sleep(500);
|
||||
int ackedChunks = _ackedChunks;
|
||||
if (ackedChunks > num)
|
||||
{
|
||||
num = ackedChunks;
|
||||
utcNow2 = DateTime.UtcNow;
|
||||
}
|
||||
double totalMilliseconds = (DateTime.UtcNow - utcNow2).TotalMilliseconds;
|
||||
double totalMilliseconds2 = (DateTime.UtcNow - utcNow).TotalMilliseconds;
|
||||
if (totalMilliseconds > 60000.0 || totalMilliseconds2 > 600000.0)
|
||||
{
|
||||
_client.SendPacket(new FtAbortPacket
|
||||
{
|
||||
TransferId = TransferId,
|
||||
Reason = "Done-ack timeout"
|
||||
});
|
||||
OnDone?.Invoke(TransferId, arg2: false, "ERR: No confirmation from client");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_done = true;
|
||||
OnDone?.Invoke(TransferId, arg2: false, "ERR: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private void SendChunk(int idx)
|
||||
{
|
||||
int num = idx * 1200;
|
||||
int num2 = Math.Min(1200, _data.Length - num);
|
||||
byte[] array = new byte[num2];
|
||||
Buffer.BlockCopy(_data, num, array, 0, num2);
|
||||
_client.SendPacket(new FtChunkPacket
|
||||
{
|
||||
TransferId = TransferId,
|
||||
ChunkIdx = idx,
|
||||
Crc32 = Crc32.Compute(_data, num, num2),
|
||||
Data = array
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user