Skip to content

Commit 31a2a95

Browse files
authored
feat(ITcpSocketClient): use ReadOnlyMemory improve performance (#6264)
* refactor: 使用 ReadOnlyMemory 只读类提供性能 * test: 更新单元测试
1 parent c2343f5 commit 31a2a95

File tree

7 files changed

+43
-44
lines changed

7 files changed

+43
-44
lines changed

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public abstract class DataPackageHandlerBase : IDataPackageHandler
1818
/// <summary>
1919
/// 当接收数据处理完成后,回调该函数执行接收
2020
/// </summary>
21-
public Func<Memory<byte>, Task>? ReceivedCallBack { get; set; }
21+
public Func<ReadOnlyMemory<byte>, ValueTask>? ReceivedCallBack { get; set; }
2222

2323
/// <summary>
2424
/// Sends the specified data asynchronously to the target destination.
@@ -29,19 +29,19 @@ public abstract class DataPackageHandlerBase : IDataPackageHandler
2929
/// <param name="data">The data to be sent, represented as a block of memory.</param>
3030
/// <returns>A task that represents the asynchronous operation. The task result contains a <see cref="Memory{T}"/> of <see
3131
/// cref="byte"/> representing the response or acknowledgment received from the target destination.</returns>
32-
public virtual Task<Memory<byte>> SendAsync(Memory<byte> data)
32+
public virtual ValueTask<ReadOnlyMemory<byte>> SendAsync(ReadOnlyMemory<byte> data)
3333
{
34-
return Task.FromResult(data);
34+
return ValueTask.FromResult(data);
3535
}
3636

3737
/// <summary>
3838
/// Processes the received data asynchronously.
3939
/// </summary>
4040
/// <param name="data">A memory buffer containing the data to be processed. The buffer must not be empty.</param>
4141
/// <returns>A task that represents the asynchronous operation.</returns>
42-
public virtual Task ReceiveAsync(Memory<byte> data)
42+
public virtual ValueTask ReceiveAsync(ReadOnlyMemory<byte> data)
4343
{
44-
return Task.CompletedTask;
44+
return ValueTask.CompletedTask;
4545
}
4646

4747
/// <summary>
@@ -52,7 +52,7 @@ public virtual Task ReceiveAsync(Memory<byte> data)
5252
/// for the specified <paramref name="length"/>.</remarks>
5353
/// <param name="buffer">The memory buffer containing the data to process.</param>
5454
/// <param name="length">The length of the valid data within the buffer.</param>
55-
protected void SlicePackage(Memory<byte> buffer, int length)
55+
protected void SlicePackage(ReadOnlyMemory<byte> buffer, int length)
5656
{
5757
_lastReceiveBuffer = buffer[length..].ToArray().AsMemory();
5858
}
@@ -66,7 +66,7 @@ protected void SlicePackage(Memory<byte> buffer, int length)
6666
/// <param name="buffer">The buffer to concatenate with the previously stored data. Must not be empty.</param>
6767
/// <returns>A <see cref="Memory{T}"/> instance containing the concatenated data. If no previously stored data exists, the
6868
/// method returns the input <paramref name="buffer"/>.</returns>
69-
protected Memory<byte> ConcatBuffer(Memory<byte> buffer)
69+
protected ReadOnlyMemory<byte> ConcatBuffer(ReadOnlyMemory<byte> buffer)
7070
{
7171
if (_lastReceiveBuffer.IsEmpty)
7272
{

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public DelimiterDataPackageHandler(byte[] delimiter)
5151
/// </summary>
5252
/// <param name="data"></param>
5353
/// <returns></returns>
54-
public override async Task ReceiveAsync(Memory<byte> data)
54+
public override async ValueTask ReceiveAsync(ReadOnlyMemory<byte> data)
5555
{
5656
data = ConcatBuffer(data);
5757

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public class FixLengthDataPackageHandler(int length) : DataPackageHandlerBase
2323
/// </summary>
2424
/// <param name="data"></param>
2525
/// <returns></returns>
26-
public override async Task ReceiveAsync(Memory<byte> data)
26+
public override async ValueTask ReceiveAsync(ReadOnlyMemory<byte> data)
2727
{
2828
while (data.Length > 0)
2929
{

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public interface IDataPackageHandler
1616
/// <summary>
1717
/// Gets or sets the callback function to be invoked when data is received asynchronously.
1818
/// </summary>
19-
Func<Memory<byte>, Task>? ReceivedCallBack { get; set; }
19+
Func<ReadOnlyMemory<byte>, ValueTask>? ReceivedCallBack { get; set; }
2020

2121
/// <summary>
2222
/// Sends the specified data asynchronously to the target destination.
@@ -27,7 +27,7 @@ public interface IDataPackageHandler
2727
/// <param name="data">The data to be sent, represented as a block of memory.</param>
2828
/// <returns>A task that represents the asynchronous operation. The task result contains a <see cref="Memory{T}"/> of <see
2929
/// cref="byte"/> representing the response or acknowledgment received from the target destination.</returns>
30-
Task<Memory<byte>> SendAsync(Memory<byte> data);
30+
ValueTask<ReadOnlyMemory<byte>> SendAsync(ReadOnlyMemory<byte> data);
3131

3232
/// <summary>
3333
/// Asynchronously receives data from a source and writes it into the provided memory buffer.
@@ -37,5 +37,5 @@ public interface IDataPackageHandler
3737
/// <param name="data">The memory buffer to store the received data. The buffer must be writable and have sufficient capacity.</param>
3838
/// <returns>A task that represents the asynchronous operation. The task result contains the number of bytes written to the
3939
/// buffer. Returns 0 if the end of the data stream is reached.</returns>
40-
Task ReceiveAsync(Memory<byte> data);
40+
ValueTask ReceiveAsync(ReadOnlyMemory<byte> data);
4141
}

src/BootstrapBlazor/Services/TcpSocket/DefaultTcpSocketClient.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,13 @@ public void SetDataHandler(IDataPackageHandler handler)
4545
_dataPackageHandler = handler;
4646
}
4747

48-
public Task<bool> ConnectAsync(string host, int port, CancellationToken token = default)
48+
public ValueTask<bool> ConnectAsync(string host, int port, CancellationToken token = default)
4949
{
5050
var endPoint = new IPEndPoint(GetIPAddress(host), port);
5151
return ConnectAsync(endPoint, token);
5252
}
5353

54-
public async Task<bool> ConnectAsync(IPEndPoint endPoint, CancellationToken token = default)
54+
public async ValueTask<bool> ConnectAsync(IPEndPoint endPoint, CancellationToken token = default)
5555
{
5656
var ret = false;
5757
try
@@ -81,7 +81,7 @@ public async Task<bool> ConnectAsync(IPEndPoint endPoint, CancellationToken toke
8181
return ret;
8282
}
8383

84-
public async Task<bool> SendAsync(Memory<byte> data, CancellationToken token = default)
84+
public async ValueTask<bool> SendAsync(ReadOnlyMemory<byte> data, CancellationToken token = default)
8585
{
8686
if (_client is not { Connected: true })
8787
{
@@ -110,7 +110,7 @@ public async Task<bool> SendAsync(Memory<byte> data, CancellationToken token = d
110110
return ret;
111111
}
112112

113-
private async Task ReceiveAsync()
113+
private async ValueTask ReceiveAsync()
114114
{
115115
_receiveCancellationTokenSource ??= new();
116116
while (_receiveCancellationTokenSource is { IsCancellationRequested: false })

src/BootstrapBlazor/Services/TcpSocket/ITcpSocketClient.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public interface ITcpSocketClient : IDisposable
4545
/// langword="default"/> if not provided.</param>
4646
/// <returns>A task that represents the asynchronous operation. The task result is <see langword="true"/> if the connection
4747
/// is successfully established; otherwise, <see langword="false"/>.</returns>
48-
Task<bool> ConnectAsync(string host, int port, CancellationToken token = default);
48+
ValueTask<bool> ConnectAsync(string host, int port, CancellationToken token = default);
4949

5050
/// <summary>
5151
/// Establishes an asynchronous connection to the specified endpoint.
@@ -57,7 +57,7 @@ public interface ITcpSocketClient : IDisposable
5757
/// langword="default"/> if not provided.</param>
5858
/// <returns>A task that represents the asynchronous operation. The task result is <see langword="true"/> if the connection
5959
/// is successfully established; otherwise, <see langword="false"/>.</returns>
60-
Task<bool> ConnectAsync(IPEndPoint endPoint, CancellationToken token = default);
60+
ValueTask<bool> ConnectAsync(IPEndPoint endPoint, CancellationToken token = default);
6161

6262
/// <summary>
6363
/// Sends the specified data asynchronously to the target endpoint.
@@ -69,7 +69,7 @@ public interface ITcpSocketClient : IDisposable
6969
/// <param name="token">An optional <see cref="CancellationToken"/> to observe while waiting for the operation to complete.</param>
7070
/// <returns>A task that represents the asynchronous operation. The task result is <see langword="true"/> if the data was
7171
/// sent successfully; otherwise, <see langword="false"/>.</returns>
72-
Task<bool> SendAsync(Memory<byte> data, CancellationToken token = default);
72+
ValueTask<bool> SendAsync(ReadOnlyMemory<byte> data, CancellationToken token = default);
7373

7474
/// <summary>
7575
/// Closes the current connection or resource, releasing any associated resources.

test/UnitTest/Services/TcpSocketFactoryTest.cs

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ public void GetOrCreate_Ok()
4141
Assert.NotNull(client5);
4242

4343
client5.Dispose();
44-
4544
factory.Dispose();
4645
}
4746

@@ -73,8 +72,8 @@ public async Task SendAsync_Error()
7372
var client = CreateClient();
7473

7574
// 测试未建立连接前调用 SendAsync 方法报异常逻辑
76-
var data = new Memory<byte>([1, 2, 3, 4, 5]);
77-
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => client.SendAsync(data));
75+
var data = new ReadOnlyMemory<byte>([1, 2, 3, 4, 5]);
76+
var ex = await Assert.ThrowsAsync<InvalidOperationException>(async () => await client.SendAsync(data));
7877
Assert.Equal("TCP Socket is not connected 127.0.0.1:0", ex.Message);
7978
}
8079

@@ -95,7 +94,7 @@ public async Task SendAsync_Cancel()
9594
var cst = new CancellationTokenSource();
9695
cst.Cancel();
9796

98-
var data = new Memory<byte>([1, 2, 3, 4, 5]);
97+
var data = new ReadOnlyMemory<byte>([1, 2, 3, 4, 5]);
9998
var result = await client.SendAsync(data, cst.Token);
10099
Assert.False(result);
101100

@@ -131,7 +130,7 @@ public async Task ReceiveAsync_Error()
131130
var methodInfo = client.GetType().GetMethod("ReceiveAsync", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
132131
Assert.NotNull(methodInfo);
133132

134-
var task = (Task)methodInfo.Invoke(client, null)!;
133+
var task = (ValueTask)methodInfo.Invoke(client, null)!;
135134
var ex = await Assert.ThrowsAsync<InvalidOperationException>(async () => await task);
136135
Assert.NotNull(ex);
137136

@@ -147,7 +146,7 @@ public async Task ReceiveAsync_Error()
147146
await client.ConnectAsync("localhost", port);
148147

149148
// 发送数据导致接收数据异常
150-
var data = new Memory<byte>([1, 2, 3, 4, 5]);
149+
var data = new ReadOnlyMemory<byte>([1, 2, 3, 4, 5]);
151150
await client.SendAsync(data);
152151

153152
// 关闭连接
@@ -168,7 +167,7 @@ public async Task CloseByRemote_Ok()
168167
await client.ConnectAsync("localhost", port);
169168

170169
// 发送数据
171-
await client.SendAsync(new Memory<byte>([1, 2, 3, 4, 5]));
170+
await client.SendAsync(new ReadOnlyMemory<byte>([1, 2, 3, 4, 5]));
172171

173172
// 关闭连接
174173
StopTcpServer(server);
@@ -187,7 +186,7 @@ public async Task FixLengthDataPackageHandler_Ok()
187186
Assert.True(client.IsConnected);
188187

189188
var tcs = new TaskCompletionSource();
190-
Memory<byte> receivedBuffer = Memory<byte>.Empty;
189+
ReadOnlyMemory<byte> receivedBuffer = ReadOnlyMemory<byte>.Empty;
191190

192191
// 增加数据处理适配器
193192
client.SetDataHandler(new FixLengthDataPackageHandler(7)
@@ -196,12 +195,12 @@ public async Task FixLengthDataPackageHandler_Ok()
196195
{
197196
receivedBuffer = buffer;
198197
tcs.SetResult();
199-
return Task.CompletedTask;
198+
return ValueTask.CompletedTask;
200199
}
201200
});
202201

203202
// 测试 SendAsync 方法
204-
var data = new Memory<byte>([1, 2, 3, 4, 5]);
203+
var data = new ReadOnlyMemory<byte>([1, 2, 3, 4, 5]);
205204
var result = await client.SendAsync(data);
206205
Assert.True(result);
207206

@@ -227,7 +226,7 @@ public async Task FixLengthDataPackageHandler_Sticky()
227226
var connect = await client.ConnectAsync("localhost", port);
228227

229228
var tcs = new TaskCompletionSource();
230-
Memory<byte> receivedBuffer = Memory<byte>.Empty;
229+
ReadOnlyMemory<byte> receivedBuffer = ReadOnlyMemory<byte>.Empty;
231230

232231
// 增加数据库处理适配器
233232
client.SetDataHandler(new FixLengthDataPackageHandler(7)
@@ -236,20 +235,20 @@ public async Task FixLengthDataPackageHandler_Sticky()
236235
{
237236
receivedBuffer = buffer;
238237
tcs.SetResult();
239-
return Task.CompletedTask;
238+
return ValueTask.CompletedTask;
240239
}
241240
});
242241

243242
// 发送数据
244-
var data = new Memory<byte>([1, 2, 3, 4, 5]);
243+
var data = new ReadOnlyMemory<byte>([1, 2, 3, 4, 5]);
245244
await client.SendAsync(data);
246245

247246
// 等待接收数据处理完成
248247
await tcs.Task;
249248

250249
// 验证接收到的数据
251250
Assert.Equal(receivedBuffer.ToArray(), [1, 2, 3, 4, 5, 3, 4]);
252-
receivedBuffer = Memory<byte>.Empty;
251+
receivedBuffer = ReadOnlyMemory<byte>.Empty;
253252
tcs = new TaskCompletionSource();
254253

255254
// 等待第二次数据
@@ -282,7 +281,7 @@ public async Task DelimiterDataPackageHandler_Ok()
282281
var connect = await client.ConnectAsync("localhost", port);
283282

284283
var tcs = new TaskCompletionSource();
285-
Memory<byte> receivedBuffer = Memory<byte>.Empty;
284+
ReadOnlyMemory<byte> receivedBuffer = ReadOnlyMemory<byte>.Empty;
286285

287286
// 增加数据库处理适配器
288287
client.SetDataHandler(new DelimiterDataPackageHandler([0x13, 0x10])
@@ -291,12 +290,12 @@ public async Task DelimiterDataPackageHandler_Ok()
291290
{
292291
receivedBuffer = buffer;
293292
tcs.SetResult();
294-
return Task.CompletedTask;
293+
return ValueTask.CompletedTask;
295294
}
296295
});
297296

298297
// 发送数据
299-
var data = new Memory<byte>([1, 2, 3, 4, 5]);
298+
var data = new ReadOnlyMemory<byte>([1, 2, 3, 4, 5]);
300299
await client.SendAsync(data);
301300

302301
// 等待接收数据处理完成
@@ -306,7 +305,7 @@ public async Task DelimiterDataPackageHandler_Ok()
306305
Assert.Equal(receivedBuffer.ToArray(), [1, 2, 3, 4, 5, 0x13, 0x10]);
307306

308307
// 等待第二次数据
309-
receivedBuffer = Memory<byte>.Empty;
308+
receivedBuffer = ReadOnlyMemory<byte>.Empty;
310309
tcs = new TaskCompletionSource();
311310
await tcs.Task;
312311

@@ -355,7 +354,7 @@ private static async Task MockDelimiterPackageAsync(TcpClient client)
355354
}
356355

357356
// 回写数据到客户端
358-
var block = new Memory<byte>(buffer, 0, len);
357+
var block = new ReadOnlyMemory<byte>(buffer, 0, len);
359358
await stream.WriteAsync(block, CancellationToken.None);
360359

361360
await Task.Delay(20);
@@ -378,7 +377,7 @@ private static async Task MockSplitPackageAsync(TcpClient client)
378377
}
379378

380379
// 回写数据到客户端
381-
var block = new Memory<byte>(buffer, 0, len);
380+
var block = new ReadOnlyMemory<byte>(buffer, 0, len);
382381
await stream.WriteAsync(block, CancellationToken.None);
383382

384383
// 模拟延时
@@ -402,7 +401,7 @@ private static async Task MockStickyPackageAsync(TcpClient client)
402401
}
403402

404403
// 回写数据到客户端
405-
var block = new Memory<byte>(buffer, 0, len);
404+
var block = new ReadOnlyMemory<byte>(buffer, 0, len);
406405
await stream.WriteAsync(block, CancellationToken.None);
407406

408407
// 模拟延时
@@ -479,7 +478,7 @@ class MockSendErrorHandler : DataPackageHandlerBase
479478
{
480479
public ITcpSocketClient? Socket { get; set; }
481480

482-
public override async Task<Memory<byte>> SendAsync(Memory<byte> data)
481+
public override async ValueTask<ReadOnlyMemory<byte>> SendAsync(ReadOnlyMemory<byte> data)
483482
{
484483
Socket?.Close();
485484
await Task.Delay(10);
@@ -489,12 +488,12 @@ public override async Task<Memory<byte>> SendAsync(Memory<byte> data)
489488

490489
class MockReceiveErrorHandler : DataPackageHandlerBase
491490
{
492-
public override Task<Memory<byte>> SendAsync(Memory<byte> data)
491+
public override ValueTask<ReadOnlyMemory<byte>> SendAsync(ReadOnlyMemory<byte> data)
493492
{
494-
return Task.FromResult(data);
493+
return ValueTask.FromResult(data);
495494
}
496495

497-
public override async Task ReceiveAsync(Memory<byte> data)
496+
public override async ValueTask ReceiveAsync(ReadOnlyMemory<byte> data)
498497
{
499498
await base.ReceiveAsync(data);
500499

0 commit comments

Comments
 (0)