From de60e6370a0d5da538ecea7d1f058db1f629f675 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 20 Jun 2025 11:47:38 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=20ReceivedCallBa?= =?UTF-8?q?ck=20=E5=9B=9E=E8=B0=83=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TcpSocket/DataPackage/DataPackageHandlerBase.cs | 5 ++++- .../Services/TcpSocket/DefaultTcpSocketClient.cs | 7 +++++++ .../Services/TcpSocket/ITcpSocketClient.cs | 8 ++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/BootstrapBlazor/Services/TcpSocket/DataPackage/DataPackageHandlerBase.cs b/src/BootstrapBlazor/Services/TcpSocket/DataPackage/DataPackageHandlerBase.cs index e3040ba0e3d..3cb6c4500e5 100644 --- a/src/BootstrapBlazor/Services/TcpSocket/DataPackage/DataPackageHandlerBase.cs +++ b/src/BootstrapBlazor/Services/TcpSocket/DataPackage/DataPackageHandlerBase.cs @@ -16,8 +16,11 @@ public abstract class DataPackageHandlerBase : IDataPackageHandler private Memory _lastReceiveBuffer = Memory.Empty; /// - /// 当接收数据处理完成后,回调该函数执行接收 + /// Gets or sets the callback function to handle received data. /// + /// The callback function should be designed to handle the received data efficiently and + /// asynchronously. Ensure that the implementation does not block or perform long-running operations, as this may + /// impact performance. public Func, ValueTask>? ReceivedCallBack { get; set; } /// diff --git a/src/BootstrapBlazor/Services/TcpSocket/DefaultTcpSocketClient.cs b/src/BootstrapBlazor/Services/TcpSocket/DefaultTcpSocketClient.cs index 84b3f462393..33679cb473b 100644 --- a/src/BootstrapBlazor/Services/TcpSocket/DefaultTcpSocketClient.cs +++ b/src/BootstrapBlazor/Services/TcpSocket/DefaultTcpSocketClient.cs @@ -28,6 +28,8 @@ class DefaultTcpSocketClient : ITcpSocketClient public int ReceiveBufferSize { get; set; } = 1024 * 10; + public Func, ValueTask>? ReceivedCallBack { get; set; } + public DefaultTcpSocketClient(string host, int port = 0) { LocalEndPoint = new IPEndPoint(GetIPAddress(host), port); @@ -136,6 +138,11 @@ private async ValueTask ReceiveAsync() { buffer = buffer[..len]; + if (ReceivedCallBack != null) + { + await ReceivedCallBack(buffer); + } + if (_dataPackageHandler != null) { await _dataPackageHandler.ReceiveAsync(buffer); diff --git a/src/BootstrapBlazor/Services/TcpSocket/ITcpSocketClient.cs b/src/BootstrapBlazor/Services/TcpSocket/ITcpSocketClient.cs index 211203c2be5..339ad56c42a 100644 --- a/src/BootstrapBlazor/Services/TcpSocket/ITcpSocketClient.cs +++ b/src/BootstrapBlazor/Services/TcpSocket/ITcpSocketClient.cs @@ -30,6 +30,14 @@ public interface ITcpSocketClient : IDisposable /// specific local endpoint, this property may return . IPEndPoint LocalEndPoint { get; } + /// + /// Gets or sets the callback function to handle received data. + /// + /// The callback function should be designed to handle the received data efficiently and + /// asynchronously. Ensure that the implementation does not block or perform long-running operations, as this may + /// impact performance. + Func, ValueTask>? ReceivedCallBack { get; set; } + /// /// Configures the data handler to process incoming data packages. /// From e6e6741c627cc81fd3bda1092d69613fb4f31c74 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 20 Jun 2025 11:52:38 +0800 Subject: [PATCH 2/3] =?UTF-8?q?test:=20=E5=A2=9E=E5=8A=A0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/UnitTest/Services/TcpSocketFactoryTest.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/UnitTest/Services/TcpSocketFactoryTest.cs b/test/UnitTest/Services/TcpSocketFactoryTest.cs index c3bdf59bb67..9991ee021d8 100644 --- a/test/UnitTest/Services/TcpSocketFactoryTest.cs +++ b/test/UnitTest/Services/TcpSocketFactoryTest.cs @@ -143,12 +143,27 @@ public async Task ReceiveAsync_Error() Assert.Equal(1024 * 20, client.ReceiveBufferSize); client.SetDataHandler(new MockReceiveErrorHandler()); + + ReadOnlyMemory buffer = ReadOnlyMemory.Empty; + var tcs = new TaskCompletionSource(); + + // 增加接收回调方法 + client.ReceivedCallBack = b => + { + buffer = b; + tcs.SetResult(); + return ValueTask.CompletedTask; + }; + await client.ConnectAsync("localhost", port); // 发送数据导致接收数据异常 var data = new ReadOnlyMemory([1, 2, 3, 4, 5]); await client.SendAsync(data); + await tcs.Task; + Assert.Equal(buffer.ToArray(), [1, 2, 3, 4, 5]); + // 关闭连接 StopTcpServer(server); } From 8d361475f93a6c6a085c3c674c033b0f09e3ff77 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Fri, 20 Jun 2025 12:09:14 +0800 Subject: [PATCH 3/3] =?UTF-8?q?doc:=20=E6=9B=B4=E6=96=B0=E7=A4=BA=E4=BE=8B?= =?UTF-8?q?=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Samples/SocketFactories.razor | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/BootstrapBlazor.Server/Components/Samples/SocketFactories.razor b/src/BootstrapBlazor.Server/Components/Samples/SocketFactories.razor index 267c5c135c9..a17c58fe686 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/SocketFactories.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/SocketFactories.razor @@ -22,6 +22,7 @@ private ITcpSocketFactory? TcpSocketFactory { get; set; }
  • 通过 ITcpSocketClient 实例方法 SendAsync 发送协议数据
  • 通过 ITcpSocketClient 实例方法 Close 关闭连接
  • 通过 ITcpSocketClient 实例方法 SetDataHandler 方法设置数据处理器
  • +
  • 通过 ITcpSocketClient 实例属性 ReceivedCallBack 方法设置接收数据处理器(注意:此回调未做任何数据处理为原始数据)
  • 4. 数据处理器

    @@ -31,13 +32,13 @@ private ITcpSocketFactory? TcpSocketFactory { get; set; }

    数据处理器设计初衷就是为了契合通讯协议大大简化我们开发逻辑,我们已通讯协议每次通讯电文均为 4 位定长举例说明,在实际的通讯过程中,我们接收到的通讯数据存在粘包或者分包的现象

      -
    • 粘包:比如我们期望收到 1234 四个字符,实际上我们接收到的是 123412 多出来的 12 其实是下一个通讯电文的内容,相邻两个通讯数据包的粘连称为粘包
    • -
    • 分包:比如我们期望收到 1234 四个字符,实际上我们接收到的是 1234 两个数据包,这种情况称为分包
    • +
    • 粘包:比如我们期望收到 1234 四个字符,实际上我们接收到的是 123412 多出来的 12 其实是下一个通讯电文的内容,相邻两个通讯数据包的粘连称为粘包
    • +
    • 分包:比如我们期望收到 1234 四个字符,实际上我们接收到的是 1234 两个数据包,这种情况称为分包
    -

    我们内置了 IDataPackageHandler 数据包处理接口,已经虚类 DataPackageHandlerBase 作为数据处理器基类已经内置了 粘包 分包 的逻辑,继承此类后专注自己处理的业务即可

    +

    我们内置了一些常用的数据处理类 IDataPackageHandler 接口为数据包处理接口,虚类 DataPackageHandlerBase 作为数据处理器基类已经内置了 粘包 分包 的逻辑,继承此类后专注自己处理的业务即可

    -

    此外我们还内置了 FixLengthDataPackageHandler 固定长度数据处理器 使用方法如下:

    +

    使用方法如下:

    [Inject]
     [NotNull]
    @@ -64,3 +65,10 @@ private async Task CreateClient()
         var connected = await client.ConnectAsync("192.168.10.100", 6688);
     }
     
    + +

    内置数据库处理器

    + +
      +
    • FixLengthDataPackageHandler 固定长度数据处理器 即每个通讯包都是固定长度
    • +
    • DelimiterDataPackageHandler 分隔符数据处理器 即通讯包以特定一个或一组字节分割
    • +