Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ private ITcpSocketFactory? TcpSocketFactory { get; set; }</Pre>
<li>通过 <code>ITcpSocketClient</code> 实例方法 <code>SendAsync</code> 发送协议数据</li>
<li>通过 <code>ITcpSocketClient</code> 实例方法 <code>Close</code> 关闭连接</li>
<li>通过 <code>ITcpSocketClient</code> 实例方法 <code>SetDataHandler</code> 方法设置数据处理器</li>
<li>通过 <code>ITcpSocketClient</code> 实例属性 <code>ReceivedCallBack</code> 方法设置接收数据处理器(注意:此回调未做任何数据处理为原始数据)</li>
</ul>

<p class="code-label">4. 数据处理器</p>
Expand All @@ -31,13 +32,13 @@ private ITcpSocketFactory? TcpSocketFactory { get; set; }</Pre>
<p>数据处理器设计初衷就是为了契合通讯协议大大简化我们开发逻辑,我们已通讯协议每次通讯电文均为 <b>4</b> 位定长举例说明,在实际的通讯过程中,我们接收到的通讯数据存在粘包或者分包的现象</p>

<ul class="ul-demo">
<li>粘包:比如我们期望收到 <b>1234</b> 四个字符,实际上我们接收到的是 <b>123412</b> 多出来的 <b>12</b> 其实是下一个通讯电文的内容,相邻两个通讯数据包的粘连称为粘包</li>
<li>分包:比如我们期望收到 <b>1234</b> 四个字符,实际上我们接收到的是 <b>12</b> 和 <b>34</b> 两个数据包,这种情况称为分包</li>
<li><b>粘包</b>:比如我们期望收到 <b>1234</b> 四个字符,实际上我们接收到的是 <b>123412</b> 多出来的 <b>12</b> 其实是下一个通讯电文的内容,相邻两个通讯数据包的粘连称为<b>粘包</b></li>
<li><b>分包</b>:比如我们期望收到 <b>1234</b> 四个字符,实际上我们接收到的是 <b>12</b> 和 <b>34</b> 两个数据包,这种情况称为<b>分包</b></li>
</ul>

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

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

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

<p>内置数据库处理器</p>

<ul class="ul-demo">
<li><code>FixLengthDataPackageHandler</code> <b>固定长度数据处理器</b> 即每个通讯包都是固定长度</li>
<li><code>DelimiterDataPackageHandler</code> <b>分隔符数据处理器</b> 即通讯包以特定一个或一组字节分割</li>
</ul>
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ public abstract class DataPackageHandlerBase : IDataPackageHandler
private Memory<byte> _lastReceiveBuffer = Memory<byte>.Empty;

/// <summary>
/// 当接收数据处理完成后,回调该函数执行接收
/// Gets or sets the callback function to handle received data.
/// </summary>
/// <remarks>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.</remarks>
public Func<ReadOnlyMemory<byte>, ValueTask>? ReceivedCallBack { get; set; }

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ class DefaultTcpSocketClient : ITcpSocketClient

public int ReceiveBufferSize { get; set; } = 1024 * 10;

public Func<ReadOnlyMemory<byte>, ValueTask>? ReceivedCallBack { get; set; }

public DefaultTcpSocketClient(string host, int port = 0)
{
LocalEndPoint = new IPEndPoint(GetIPAddress(host), port);
Expand Down Expand Up @@ -136,6 +138,11 @@ private async ValueTask ReceiveAsync()
{
buffer = buffer[..len];

if (ReceivedCallBack != null)
{
await ReceivedCallBack(buffer);
}

if (_dataPackageHandler != null)
{
await _dataPackageHandler.ReceiveAsync(buffer);
Expand Down
8 changes: 8 additions & 0 deletions src/BootstrapBlazor/Services/TcpSocket/ITcpSocketClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ public interface ITcpSocketClient : IDisposable
/// specific local endpoint, this property may return <see langword="null"/>.</remarks>
IPEndPoint LocalEndPoint { get; }

/// <summary>
/// Gets or sets the callback function to handle received data.
/// </summary>
/// <remarks>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.</remarks>
Func<ReadOnlyMemory<byte>, ValueTask>? ReceivedCallBack { get; set; }

/// <summary>
/// Configures the data handler to process incoming data packages.
/// </summary>
Expand Down
15 changes: 15 additions & 0 deletions test/UnitTest/Services/TcpSocketFactoryTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,12 +143,27 @@ public async Task ReceiveAsync_Error()
Assert.Equal(1024 * 20, client.ReceiveBufferSize);

client.SetDataHandler(new MockReceiveErrorHandler());

ReadOnlyMemory<byte> buffer = ReadOnlyMemory<byte>.Empty;
var tcs = new TaskCompletionSource();

// 增加接收回调方法
client.ReceivedCallBack = b =>
{
buffer = b;
tcs.SetResult();
return ValueTask.CompletedTask;
};

await client.ConnectAsync("localhost", port);

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

await tcs.Task;
Assert.Equal(buffer.ToArray(), [1, 2, 3, 4, 5]);

// 关闭连接
StopTcpServer(server);
}
Expand Down