Skip to content

Commit 486349a

Browse files
committed
feat: 实现发送超时逻辑
1 parent 2f77b96 commit 486349a

File tree

6 files changed

+72
-29
lines changed

6 files changed

+72
-29
lines changed

src/BootstrapBlazor/Services/TcpSocket/DataPackage/DataPackageHandlerBase.cs

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,33 +16,28 @@ public abstract class DataPackageHandlerBase : IDataPackageHandler
1616
private Memory<byte> _lastReceiveBuffer = Memory<byte>.Empty;
1717

1818
/// <summary>
19-
/// Gets or sets the callback function to handle received data.
19+
/// <inheritdoc/>
2020
/// </summary>
21-
/// <remarks>The callback function should be designed to handle the received data efficiently and
22-
/// asynchronously. Ensure that the implementation does not block or perform long-running operations, as this may
23-
/// impact performance.</remarks>
2421
public Func<ReadOnlyMemory<byte>, ValueTask>? ReceivedCallBack { get; set; }
2522

2623
/// <summary>
27-
/// Sends the specified data asynchronously to the target destination.
24+
/// <inheritdoc/>
2825
/// </summary>
29-
/// <remarks>The method performs an asynchronous operation to send the provided data. The caller must
30-
/// ensure that the data is valid and non-empty. The returned memory block may contain a response or acknowledgment
31-
/// depending on the implementation of the target destination.</remarks>
32-
/// <param name="data">The data to be sent, represented as a block of memory.</param>
33-
/// <returns>A task that represents the asynchronous operation. The task result contains a <see cref="Memory{T}"/> of <see
34-
/// cref="byte"/> representing the response or acknowledgment received from the target destination.</returns>
35-
public virtual ValueTask<ReadOnlyMemory<byte>> SendAsync(ReadOnlyMemory<byte> data)
26+
/// <param name="data"></param>
27+
/// <param name="token"></param>
28+
/// <returns></returns>
29+
public virtual ValueTask<ReadOnlyMemory<byte>> SendAsync(ReadOnlyMemory<byte> data, CancellationToken token = default)
3630
{
3731
return ValueTask.FromResult(data);
3832
}
3933

4034
/// <summary>
41-
/// Processes the received data asynchronously.
35+
/// <inheritdoc/>
4236
/// </summary>
43-
/// <param name="data">A memory buffer containing the data to be processed. The buffer must not be empty.</param>
44-
/// <returns>A task that represents the asynchronous operation.</returns>
45-
public virtual ValueTask ReceiveAsync(ReadOnlyMemory<byte> data)
37+
/// <param name="data"></param>
38+
/// <param name="token"></param>
39+
/// <returns></returns>
40+
public virtual ValueTask ReceiveAsync(ReadOnlyMemory<byte> data, CancellationToken token = default)
4641
{
4742
return ValueTask.CompletedTask;
4843
}

src/BootstrapBlazor/Services/TcpSocket/DataPackage/DelimiterDataPackageHandler.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,9 @@ public DelimiterDataPackageHandler(byte[] delimiter)
5050
/// <inheritdoc/>
5151
/// </summary>
5252
/// <param name="data"></param>
53+
/// <param name="token"></param>
5354
/// <returns></returns>
54-
public override async ValueTask ReceiveAsync(ReadOnlyMemory<byte> data)
55+
public override async ValueTask ReceiveAsync(ReadOnlyMemory<byte> data, CancellationToken token = default)
5556
{
5657
data = ConcatBuffer(data);
5758

src/BootstrapBlazor/Services/TcpSocket/DataPackage/FixLengthDataPackageHandler.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ public class FixLengthDataPackageHandler(int length) : DataPackageHandlerBase
2222
/// <inheritdoc/>
2323
/// </summary>
2424
/// <param name="data"></param>
25+
/// <param name="token"></param>
2526
/// <returns></returns>
26-
public override async ValueTask ReceiveAsync(ReadOnlyMemory<byte> data)
27+
public override async ValueTask ReceiveAsync(ReadOnlyMemory<byte> data, CancellationToken token = default)
2728
{
2829
while (data.Length > 0)
2930
{

src/BootstrapBlazor/Services/TcpSocket/DataPackage/IDataPackageHandler.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,19 @@ public interface IDataPackageHandler
2525
/// ensure that the data is valid and non-empty. The returned memory block may contain a response or acknowledgment
2626
/// depending on the implementation of the target destination.</remarks>
2727
/// <param name="data">The data to be sent, represented as a block of memory.</param>
28+
/// <param name="token">An optional <see cref="CancellationToken"/> to observe while waiting for the operation to complete.</param>
2829
/// <returns>A task that represents the asynchronous operation. The task result contains a <see cref="Memory{T}"/> of <see
2930
/// cref="byte"/> representing the response or acknowledgment received from the target destination.</returns>
30-
ValueTask<ReadOnlyMemory<byte>> SendAsync(ReadOnlyMemory<byte> data);
31+
ValueTask<ReadOnlyMemory<byte>> SendAsync(ReadOnlyMemory<byte> data, CancellationToken token = default);
3132

3233
/// <summary>
3334
/// Asynchronously receives data from a source and writes it into the provided memory buffer.
3435
/// </summary>
3536
/// <remarks>This method does not guarantee that the entire buffer will be filled. The number of bytes
3637
/// written depends on the availability of data.</remarks>
3738
/// <param name="data">The memory buffer to store the received data. The buffer must be writable and have sufficient capacity.</param>
39+
/// <param name="token">A cancellation token that can be used to cancel the operation. The default value is <see langword="default"/>.</param>
3840
/// <returns>A task that represents the asynchronous operation. The task result contains the number of bytes written to the
3941
/// buffer. Returns 0 if the end of the data stream is reached.</returns>
40-
ValueTask ReceiveAsync(ReadOnlyMemory<byte> data);
42+
ValueTask ReceiveAsync(ReadOnlyMemory<byte> data, CancellationToken token = default);
4143
}

src/BootstrapBlazor/Services/TcpSocket/DefaultTcpSocketClient.cs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,17 +102,34 @@ public async ValueTask<bool> SendAsync(ReadOnlyMemory<byte> data, CancellationTo
102102
var ret = false;
103103
try
104104
{
105+
var stream = _client.GetStream();
106+
107+
var sendToken = token;
108+
if (SendTimeout > 0)
109+
{
110+
// 设置发送超时时间
111+
var sendTokenSource = new CancellationTokenSource(SendTimeout);
112+
sendToken = CancellationTokenSource.CreateLinkedTokenSource(token, sendTokenSource.Token).Token;
113+
}
114+
105115
if (_dataPackageHandler != null)
106116
{
107-
data = await _dataPackageHandler.SendAsync(data);
117+
data = await _dataPackageHandler.SendAsync(data, sendToken);
108118
}
109-
var stream = _client.GetStream();
110-
await stream.WriteAsync(data, token);
119+
120+
await stream.WriteAsync(data, sendToken);
111121
ret = true;
112122
}
113123
catch (OperationCanceledException ex)
114124
{
115-
Logger.LogWarning(ex, "TCP Socket send operation was canceled from {LocalEndPoint} to {RemoteEndPoint}", LocalEndPoint, _remoteEndPoint);
125+
if (token.IsCancellationRequested)
126+
{
127+
Logger.LogWarning(ex, "TCP Socket send operation was canceled from {LocalEndPoint} to {RemoteEndPoint}", LocalEndPoint, _remoteEndPoint);
128+
}
129+
else
130+
{
131+
Logger.LogWarning(ex, "TCP Socket send operation timed out from {LocalEndPoint} to {RemoteEndPoint}", LocalEndPoint, _remoteEndPoint);
132+
}
116133
}
117134
catch (Exception ex)
118135
{

test/UnitTest/Services/TcpSocketFactoryTest.cs

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,23 @@ public async Task ConnectAsync_Failed()
7676
Assert.False(connect);
7777
}
7878

79+
[Fact]
80+
public async Task Send_Timeout()
81+
{
82+
var port = 8887;
83+
var server = StartTcpServer(port, MockSplitPackageAsync);
84+
85+
var client = CreateClient();
86+
client.SendTimeout = 100;
87+
client.SetDataHandler(new MockSendTimeoutHandler());
88+
89+
await client.ConnectAsync("localhost", port);
90+
91+
var data = new ReadOnlyMemory<byte>([1, 2, 3, 4, 5]);
92+
var result = await client.SendAsync(data);
93+
Assert.False(result);
94+
}
95+
7996
[Fact]
8097
public async Task SendAsync_Error()
8198
{
@@ -499,27 +516,37 @@ class MockSendErrorHandler : DataPackageHandlerBase
499516
{
500517
public ITcpSocketClient? Socket { get; set; }
501518

502-
public override async ValueTask<ReadOnlyMemory<byte>> SendAsync(ReadOnlyMemory<byte> data)
519+
public override async ValueTask<ReadOnlyMemory<byte>> SendAsync(ReadOnlyMemory<byte> data, CancellationToken token = default)
503520
{
504521
Socket?.Close();
505-
await Task.Delay(10);
522+
await Task.Delay(10, token);
506523
return data;
507524
}
508525
}
509526

510527
class MockReceiveErrorHandler : DataPackageHandlerBase
511528
{
512-
public override ValueTask<ReadOnlyMemory<byte>> SendAsync(ReadOnlyMemory<byte> data)
529+
public override ValueTask<ReadOnlyMemory<byte>> SendAsync(ReadOnlyMemory<byte> data, CancellationToken token = default)
513530
{
514531
return ValueTask.FromResult(data);
515532
}
516533

517-
public override async ValueTask ReceiveAsync(ReadOnlyMemory<byte> data)
534+
public override async ValueTask ReceiveAsync(ReadOnlyMemory<byte> data, CancellationToken token = default)
518535
{
519-
await base.ReceiveAsync(data);
536+
await base.ReceiveAsync(data, token);
520537

521538
// 模拟接收数据时报错
522539
throw new InvalidOperationException("Test Error");
523540
}
524541
}
542+
543+
class MockSendTimeoutHandler : DataPackageHandlerBase
544+
{
545+
public override async ValueTask<ReadOnlyMemory<byte>> SendAsync(ReadOnlyMemory<byte> data, CancellationToken token = default)
546+
{
547+
// 模拟发送超时
548+
await Task.Delay(200, token);
549+
return data;
550+
}
551+
}
525552
}

0 commit comments

Comments
 (0)