internal class TcpCommunicator : IMessageCommunicator
{
private TcpClient? _TcpClient;
private NetworkStream? _NetworkStream;
private CancellationTokenSource? _ReceiveCts;
private readonly SemaphoreSlim _SendSemaphore = new(1, 1);
private bool _IsDisposed = false;
NetworkCommConfig _Config;
public ICommConfig Config => _Config;
public bool IsConnected => _TcpClient?.Connected ?? false;
public event Func<bool, ValueTask>? ConnectStatusChanged;
public event Func<ReadOnlyMemory<byte>, ValueTask>? OnDataReceived;
public TcpCommunicator(NetworkCommConfig config)
{
_Config = config;
}
public TcpCommunicator(string ipv4, int port)
{
_Config = new NetworkCommConfig(ipv4, port);
}
public async ValueTask ConnectAsync(CancellationToken token = default)
{
if (_IsDisposed) throw new ObjectDisposedException(nameof(TcpCommunicator));
if (IsConnected) return;
await DisconnectAsync(token);
try
{
var client = new TcpClient();
await client.ConnectAsync(_Config.Ipv4, _Config.Port).ConfigureAwait(false);
_TcpClient = client;
_NetworkStream = client.GetStream();
_ReceiveCts = new CancellationTokenSource();
ReceiveLoop();
}
catch
{
await DisconnectAsync(token);
}
if (IsConnected)
ConnectStatusChanged?.Invoke(true);
}
public async ValueTask DisconnectAsync(CancellationToken token = default)
{
if (_ReceiveCts is not null)
{
await _ReceiveCts.CancelAsync();
_ReceiveCts.Dispose();
_ReceiveCts = null;
}
_NetworkStream?.Dispose();
_NetworkStream = null;
_TcpClient?.Dispose();
_TcpClient = null;
}
public async ValueTask DisposeAsync()
{
if (_IsDisposed) return;
_IsDisposed = true;
await DisconnectAsync();
_SendSemaphore.Dispose();
GC.SuppressFinalize(this);
}
MemoryStream _ReceiveStream = new();
/// <summary>
/// 发送回调
/// </summary>
/// <param name="dataReceived"></param>
/// <returns></returns>
async ValueTask SendReceive(ReadOnlyMemory<byte> dataReceived)
{
await _ReceiveStream.WriteAsync(dataReceived);
}
Stopwatch _CilentStopwatch = new();
public async ValueTask<ReadOnlyMemory<byte>> Send(ReadOnlyMemory<byte> data, CancellationToken token = default)
{
if (!IsConnected) throw new InvalidOperationException("Not connected");
await _SendSemaphore.WaitAsync(token);
CancellationTokenSource _timeoutCTS = CancellationTokenSource.CreateLinkedTokenSource(token
, new CancellationTokenSource(TimeSpan.FromSeconds(5)).Token);
try
{
_ReceiveStream.SetLength(0);
OnDataReceived += SendReceive;
await SendAsync(data, token);
long receiveLength = 0;
while (!_timeoutCTS.Token.IsCancellationRequested)
{
await Task.Delay(50, _timeoutCTS.Token);
if (_ReceiveStream.Length == 0) continue;
if (receiveLength != _ReceiveStream.Length) // 数据长度变化,重置计时器
{
receiveLength = _ReceiveStream.Length;
_CilentStopwatch.Restart();
}
else if (_CilentStopwatch.ElapsedMilliseconds > 260) // 超过260 ms 数据无变化,认为接收完成
{
break;
}
}
return _ReceiveStream.ToArray();
}
catch (OperationCanceledException)
{
// 超时
return ReadOnlyMemory<byte>.Empty;
}
finally
{
OnDataReceived -= SendReceive;
_SendSemaphore.Release();
}
}
public ValueTask SendAsync(ReadOnlyMemory<byte> data, CancellationToken token = default) => _NetworkStream!.WriteAsync(data.ToArray(), token);
#region 接收循环(高性能 Memory 版
bool _IsLooping = false;
Lock loopLocker = new Lock();
private void ReceiveLoop()
{
lock (loopLocker)
{
if (_IsLooping) return;
_IsLooping = true;
}
Task.Run(async () =>
{
// 用 Memory<byte> 定义缓冲区(零拷贝)
Memory<byte> buffer = new byte[1024 * 10];
try
{
while (!_ReceiveCts!.IsCancellationRequested)
{
// 直接 ReadAsync into Memory
int n = await _NetworkStream!
.ReadAsync(buffer, _ReceiveCts.Token)
.ConfigureAwait(false);
if (n == 0)
break;
// 切片:零拷贝,不分配新数组
var dataMemory = buffer.Slice(0, n);
// ✅ 直接抛 Memory 给事件,不拷贝!
OnDataReceived?.Invoke(dataMemory);
}
}
catch (OperationCanceledException) { }
catch (Exception ex)
{
// 可在这里触发错误事件
// OnError?.Invoke(ex);
throw;
}
finally
{
await DisconnectAsync(_ReceiveCts!.Token);
}
}).ContinueWith(t =>
{
_IsLooping = false;
ConnectStatusChanged?.Invoke(false);
});
}
#endregion
}