163 lines
3.3 KiB
C#
163 lines
3.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Security.Cryptography;
|
|
using System.Threading;
|
|
|
|
namespace Crysome.Common.Network.FileTransfer;
|
|
|
|
public sealed class FtReceiver
|
|
{
|
|
private const int SackIntervalMs = 200;
|
|
|
|
private readonly CrysomeClient _client;
|
|
|
|
private uint _id;
|
|
|
|
private string _fileName;
|
|
|
|
private int _totalChunks;
|
|
|
|
private long _totalBytes;
|
|
|
|
private byte[] _expectedSha256;
|
|
|
|
private byte[][] _chunks;
|
|
|
|
private bool[] _received;
|
|
|
|
private int _receivedCount;
|
|
|
|
private int _highestContiguous = -1;
|
|
|
|
private int _lastSackTick;
|
|
|
|
private bool _finalised;
|
|
|
|
public Action<string, byte[]> OnComplete;
|
|
|
|
public Action<string> OnError;
|
|
|
|
public FtReceiver(CrysomeClient client)
|
|
{
|
|
_client = client;
|
|
}
|
|
|
|
public void HandleStart(FtStartPacket p)
|
|
{
|
|
_id = p.TransferId;
|
|
_fileName = p.FileName;
|
|
_totalChunks = Math.Max(1, p.TotalChunks);
|
|
_totalBytes = p.TotalBytes;
|
|
_expectedSha256 = p.Sha256;
|
|
_chunks = new byte[_totalChunks][];
|
|
_received = new bool[_totalChunks];
|
|
_receivedCount = 0;
|
|
_highestContiguous = -1;
|
|
_lastSackTick = Environment.TickCount - 400;
|
|
_finalised = false;
|
|
}
|
|
|
|
public void HandleChunk(FtChunkPacket p)
|
|
{
|
|
if (_chunks == null || p.TransferId != _id || _finalised)
|
|
{
|
|
return;
|
|
}
|
|
int chunkIdx = p.ChunkIdx;
|
|
if (chunkIdx >= 0 && chunkIdx < _totalChunks && !_received[chunkIdx] && p.Data != null && Crc32.Compute(p.Data, 0, p.Data.Length) == p.Crc32)
|
|
{
|
|
_chunks[chunkIdx] = p.Data;
|
|
_received[chunkIdx] = true;
|
|
Interlocked.Increment(ref _receivedCount);
|
|
while (_highestContiguous + 1 < _totalChunks && _received[_highestContiguous + 1])
|
|
{
|
|
_highestContiguous++;
|
|
}
|
|
int tickCount = Environment.TickCount;
|
|
bool num = _receivedCount == _totalChunks;
|
|
if (num || tickCount - _lastSackTick >= 200)
|
|
{
|
|
_lastSackTick = tickCount;
|
|
SendSack();
|
|
}
|
|
if (num)
|
|
{
|
|
Finalise();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void HandleAbort(FtAbortPacket p)
|
|
{
|
|
if (p.TransferId == _id)
|
|
{
|
|
OnError?.Invoke("Transfer aborted by sender: " + p.Reason);
|
|
}
|
|
}
|
|
|
|
private void SendSack()
|
|
{
|
|
List<int> list = new List<int>();
|
|
int num = _highestContiguous + 1;
|
|
int num2 = Math.Min(_totalChunks, num + 64);
|
|
for (int i = num; i < num2; i++)
|
|
{
|
|
if (!_received[i])
|
|
{
|
|
list.Add(i);
|
|
}
|
|
}
|
|
_client.SendPacket(new FtSackPacket
|
|
{
|
|
TransferId = _id,
|
|
HighestContiguous = _highestContiguous,
|
|
NackList = list.ToArray()
|
|
});
|
|
}
|
|
|
|
private void Finalise()
|
|
{
|
|
if (_finalised)
|
|
{
|
|
return;
|
|
}
|
|
_finalised = true;
|
|
byte[] array = new byte[_totalBytes];
|
|
long num = 0L;
|
|
for (int i = 0; i < _totalChunks; i++)
|
|
{
|
|
byte[] array2 = _chunks[i];
|
|
Buffer.BlockCopy(array2, 0, array, (int)num, array2.Length);
|
|
num += array2.Length;
|
|
}
|
|
byte[] array3;
|
|
using (SHA256 sHA = SHA256.Create())
|
|
{
|
|
array3 = sHA.ComputeHash(array);
|
|
}
|
|
bool flag = true;
|
|
for (int j = 0; j < 32; j++)
|
|
{
|
|
if (array3[j] != _expectedSha256[j])
|
|
{
|
|
flag = false;
|
|
break;
|
|
}
|
|
}
|
|
_client.SendPacket(new FtDonePacket
|
|
{
|
|
TransferId = _id,
|
|
Success = flag,
|
|
Message = (flag ? "OK" : "HASH_MISMATCH")
|
|
});
|
|
if (flag)
|
|
{
|
|
OnComplete?.Invoke(_fileName, array);
|
|
}
|
|
else
|
|
{
|
|
OnError?.Invoke("SHA-256 mismatch — file rejected");
|
|
}
|
|
}
|
|
}
|