Skip to content

Commit 5729731

Browse files
ArgoZhangRRQM
andauthored
feat(TcpSocket): add TcpSocket lib (#512)
* feat: 增加 TouchSocket 扩展包 * refactor: 重构 DefaultTcpSocketFactory 实现类 * refactor: 更新 DefaultTcpSocketClient 实现类 * refactor: 根据最新设计更改实现类 * chore: 更新项目依赖关系 * feat: 根据新接口定义增加 ReceivedCallBack 属性 * feat(tcp): 替换 DefaultTcpSocketClient 为 TouchSocketTcpClient 添加 TouchSocket 包引用,删除 DefaultTcpSocketClient 类,使用 TouchSocketTcpClient 类实现 ITcpSocketClient 接口,提供连接、发送和接收数据的功能。修改 DefaultTcpSocketFactory 类构造函数,移除对 ILogger<DefaultTcpSocketClient> 的依赖,改为使用 TouchSocketTcpClient。新增 TouchSocketTcpClient 类实现 TCP 连接逻辑。 * wip: 重构实现方法(未完成) * wip: 临时提交 * test: 增加单元测试 * feat: 根据最新接口更新实现逻辑 * refactor(DefaultTcpSocketClient): 优化 SendAsync 调用 在 DefaultTcpSocketClient 类中,修改 SendAsync 方法的调用方式,去掉 WaitAsync,直接使用 SendAsync(data, sendToken),使代码更简洁并提高异步操作效率 * refactor: 精简服务更新单元测试 * 重构(tcp): 更新TouchSocket版本并优化连接逻辑 在 `BootstrapBlazor.TouchSocket.csproj` 文件中,将 `TouchSocket` 的版本从 `3.1.11.8` 更新为 `4.0.0-Alpha.10`。 在 `DefaultTcpSocketProvider.cs` 文件中,类的访问修饰符从 `sealed` 改为 `internal`,并添加了对 `System.Diagnostics` 的引用。 修改 `IsConnected` 属性的实现,确保调用基类的 `Online` 属性。 添加新的 `CloseAsync` 方法,重写连接逻辑以包含异常处理,并在连接成功后进行调试断言。 更新 `ReceiveAsync` 方法的数据读取逻辑,使用基类的 `Transport.Input` 进行数据读取,并确保正确处理数据缓冲区。 重写 `SendAsync` 方法,添加对取消令牌的处理,并确保在发送数据时正确管理锁。 重写 `ReceiveLoopAsync` 方法,注释说明新的接收循环逻辑,确保数据通过管道直接读取,而不进行额外的数据读取操作 * chore: 恢复项目引用 * chore: bump version 9.0.0 * revert: 撤销其余项目更改 --------- Co-authored-by: 若汝棋茗 <505554090@qq.com>
1 parent 81b5d71 commit 5729731

7 files changed

Lines changed: 889 additions & 2 deletions

File tree

src/extensions/BootstrapBlazor.Socket/BootstrapBlazor.Socket.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<Version>9.0.0-beta01</Version>
4+
<Version>9.0.0</Version>
55
</PropertyGroup>
66

77
<PropertyGroup>

src/extensions/BootstrapBlazor.TcpSocket/BootstrapBlazor.TcpSocket.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<Version>9.0.0-beta01</Version>
4+
<Version>9.0.0</Version>
55
</PropertyGroup>
66

77
<PropertyGroup>
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<Version>9.0.0</Version>
5+
</PropertyGroup>
6+
7+
<PropertyGroup>
8+
<PackageTags>Bootstrap Blazor WebAssembly wasm UI Components Topology FlowChart</PackageTags>
9+
<Description>Bootstrap UI components extensions of FlowChart</Description>
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
<SupportedPlatform Remove="browser" />
14+
</ItemGroup>
15+
16+
<ItemGroup>
17+
<PackageReference Include="TouchSocket" Version="4.0.0-Alpha.10" />
18+
</ItemGroup>
19+
20+
<ItemGroup>
21+
<!--<PackageReference Include="BootstrapBlazor" Version="9.8.0-beta07" />-->
22+
</ItemGroup>
23+
24+
<ItemGroup>
25+
<ProjectReference Include="..\..\..\..\BootstrapBlazor\src\BootstrapBlazor\BootstrapBlazor.csproj" />
26+
</ItemGroup>
27+
28+
<ItemGroup>
29+
<Using Include="Microsoft.JSInterop" />
30+
</ItemGroup>
31+
32+
</Project>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
// Website: https://www.blazor.zone or https://argozhang.github.io/
4+
5+
using BootstrapBlazor.Components;
6+
7+
namespace Microsoft.Extensions.DependencyInjection;
8+
9+
/// <summary>
10+
/// BootstrapBlazor 服务扩展类
11+
/// </summary>
12+
public static class BootstrapBlazorTouchSocketServiceExtensions
13+
{
14+
/// <summary>
15+
/// 添加 TouchSocket 服务
16+
/// </summary>
17+
/// <param name="services"></param>
18+
public static IServiceCollection AddBootstrapBlazorTouchSocketProviderService(this IServiceCollection services)
19+
{
20+
services.AddTransient<ISocketClientProvider, DefaultTcpSocketProvider>();
21+
22+
return services;
23+
}
24+
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
// Website: https://www.blazor.zone or https://argozhang.github.io/
4+
5+
using System.Buffers;
6+
using System.Diagnostics;
7+
using System.IO.Pipelines;
8+
using System.Net;
9+
using TouchSocket.Core;
10+
using TouchSocket.Sockets;
11+
12+
namespace BootstrapBlazor.Components;
13+
14+
internal sealed class DefaultTcpSocketProvider : TcpClientBase, ISocketClientProvider
15+
{
16+
/// <summary>
17+
/// <inheritdoc/>
18+
/// </summary>
19+
public bool IsConnected => base.Online;
20+
21+
/// <summary>
22+
/// <inheritdoc/>
23+
/// </summary>
24+
public IPEndPoint LocalEndPoint { get; set; } = new IPEndPoint(IPAddress.Any, 0);
25+
26+
/// <summary>
27+
/// <inheritdoc/>
28+
/// </summary>
29+
public async ValueTask CloseAsync()
30+
{
31+
await base.CloseAsync(string.Empty);
32+
}
33+
34+
/// <summary>
35+
/// <inheritdoc/>
36+
/// </summary>
37+
public async ValueTask<bool> ConnectAsync(IPEndPoint endPoint, CancellationToken token = default)
38+
{
39+
await SetupAsync(new TouchSocketConfig()
40+
.SetBindIPHost(new IPHost(LocalEndPoint.Address, LocalEndPoint.Port))
41+
.SetRemoteIPHost(new IPHost(endPoint.Address, endPoint.Port)));
42+
43+
try
44+
{
45+
await TcpConnectAsync(int.MaxValue, token);
46+
Debug.Assert(MainSocket != null, "MainSocket cannot be null after connection.");
47+
Debug.Assert(base.Online, "Online should be true after successful connection.");
48+
if (MainSocket.LocalEndPoint is IPEndPoint localEndPoint)
49+
{
50+
LocalEndPoint = localEndPoint;
51+
}
52+
return true;
53+
}
54+
catch (Exception ex)
55+
{
56+
this.Logger?.Exception(this, ex);
57+
return false;
58+
}
59+
}
60+
61+
/// <summary>
62+
/// <inheritdoc/>
63+
/// </summary>
64+
public async ValueTask<int> ReceiveAsync(Memory<byte> buffer, CancellationToken token = default)
65+
{
66+
token.ThrowIfCancellationRequested();
67+
this.ThrowIfTcpClientNotConnected();
68+
this.ThrowIfDisposed();
69+
70+
var result = await base.Transport.Input.ReadAsync(token);
71+
if (result.IsCompleted)
72+
{
73+
return 0;
74+
}
75+
var length = (int)Math.Min(result.Buffer.Length, buffer.Length);
76+
77+
var sequence = result.Buffer.Slice(0, length);
78+
79+
sequence.CopyTo(buffer.Span);
80+
base.Transport.Input.AdvanceTo(sequence.End);
81+
return length;
82+
}
83+
84+
/// <summary>
85+
/// <inheritdoc/>
86+
/// </summary>
87+
public async ValueTask<bool> SendAsync(ReadOnlyMemory<byte> data, CancellationToken token = default)
88+
{
89+
token.ThrowIfCancellationRequested();
90+
base.ThrowIfTcpClientNotConnected();
91+
base.ThrowIfDisposed();
92+
var pipeWriter = base.Transport.Output;
93+
var locker = base.Transport.SemaphoreSlimForWriter;
94+
await locker.WaitAsync(token);
95+
try
96+
{
97+
pipeWriter.Write(data.Span);
98+
var result = await pipeWriter.FlushAsync(token);
99+
if (result.IsCanceled || result.IsCompleted)
100+
{
101+
return false;
102+
}
103+
return true;
104+
}
105+
catch (Exception ex)
106+
{
107+
this.Logger?.Exception(this, ex);
108+
return false;
109+
}
110+
finally
111+
{
112+
locker.Release();
113+
}
114+
}
115+
116+
protected override sealed async Task ReceiveLoopAsync(ITransport transport)
117+
{
118+
//重写接收循环方法
119+
//此处不做任何数据读取
120+
//让数据直接到ReceiveAsync使用管道直接读取数据
121+
await Task.Delay(-1, transport.ClosedToken);
122+
}
123+
}

0 commit comments

Comments
 (0)