feat(network)!: add v2 framing, pooled buffers and bounded dispatch
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
using System.Net.Sockets;
|
||||
using System.Net.Security;
|
||||
@@ -10,7 +11,7 @@ using Cysharp.Threading.Tasks;
|
||||
|
||||
namespace ShrinkNetwork
|
||||
{
|
||||
public sealed class ShrinkTcpClientTransport : IShrinkNetworkAsyncTransport
|
||||
public sealed class ShrinkTcpClientTransport : IShrinkNetworkMemoryTransport
|
||||
{
|
||||
private readonly string _host;
|
||||
private readonly int _port;
|
||||
@@ -86,7 +87,9 @@ namespace ShrinkNetwork
|
||||
SendAsync(sessionId, packetData).Forget();
|
||||
}
|
||||
|
||||
public UniTask SendAsync(long sessionId, byte[] packetData)
|
||||
public UniTask SendAsync(long sessionId, byte[] packetData) => SendAsync(sessionId, (ReadOnlyMemory<byte>)(packetData ?? Array.Empty<byte>()));
|
||||
|
||||
public UniTask SendAsync(long sessionId, ReadOnlyMemory<byte> packetData, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!IsStarted)
|
||||
throw new InvalidOperationException("Transport is not started.");
|
||||
@@ -94,11 +97,11 @@ namespace ShrinkNetwork
|
||||
throw new InvalidOperationException($"Unsupported session id {sessionId}. This transport only supports {_sessionId}.");
|
||||
if (_client == null || !_client.Connected || _stream == null)
|
||||
throw new InvalidOperationException("TCP client is not connected.");
|
||||
if ((packetData?.Length ?? 0) > _maxPacketSize)
|
||||
throw new InvalidOperationException($"TCP packet is too large. Size={(packetData?.Length ?? 0)}, Limit={_maxPacketSize}.");
|
||||
if (packetData.Length > _maxPacketSize)
|
||||
throw new InvalidOperationException($"TCP packet is too large. Size={packetData.Length}, Limit={_maxPacketSize}.");
|
||||
|
||||
return SendInternalAsync(_stream, _sendLock, packetData ?? Array.Empty<byte>(),
|
||||
_cts?.Token ?? CancellationToken.None);
|
||||
return SendInternalAsync(_stream, _sendLock, packetData,
|
||||
cancellationToken.CanBeCanceled ? cancellationToken : _cts?.Token ?? CancellationToken.None);
|
||||
}
|
||||
|
||||
private async UniTaskVoid ConnectAsync(CancellationToken cancellationToken)
|
||||
@@ -209,18 +212,20 @@ namespace ShrinkNetwork
|
||||
ex.SocketErrorCode == SocketError.Shutdown;
|
||||
}
|
||||
|
||||
private static async UniTask SendInternalAsync(Stream stream, SemaphoreSlim sendLock, byte[] packetData,
|
||||
private static async UniTask SendInternalAsync(Stream stream, SemaphoreSlim sendLock, ReadOnlyMemory<byte> packetData,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
await sendLock.WaitAsync(cancellationToken);
|
||||
try
|
||||
{
|
||||
var header = new byte[4];
|
||||
BinaryPrimitives.WriteInt32LittleEndian(header, packetData.Length);
|
||||
await stream.WriteAsync(header, cancellationToken);
|
||||
if (packetData.Length > 0)
|
||||
await stream.WriteAsync(packetData, cancellationToken);
|
||||
await stream.FlushAsync(cancellationToken);
|
||||
var frame = ArrayPool<byte>.Shared.Rent(checked(4 + packetData.Length));
|
||||
try
|
||||
{
|
||||
BinaryPrimitives.WriteInt32LittleEndian(frame, packetData.Length);
|
||||
packetData.CopyTo(frame.AsMemory(4));
|
||||
await stream.WriteAsync(frame.AsMemory(0, 4 + packetData.Length), cancellationToken);
|
||||
}
|
||||
finally { ArrayPool<byte>.Shared.Return(frame, clearArray: true); }
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user