Skip to content

Commit 489d284

Browse files
committed
feat: 增加 TouchSocket 扩展包
1 parent 11e7a59 commit 489d284

5 files changed

Lines changed: 278 additions & 0 deletions

File tree

BootstrapBlazor.Extensions.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.PdfViewer",
192192
EndProject
193193
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.Vditor", "src\components\BootstrapBlazor.Vditor\BootstrapBlazor.Vditor.csproj", "{D417E1B9-D146-4983-81D0-79F3193B322B}"
194194
EndProject
195+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.TouchSocket", "src\extensions\BootstrapBlazor.TouchSocket\BootstrapBlazor.TouchSocket.csproj", "{FD23CEA1-78EB-85D7-8EDF-047657355B52}"
196+
EndProject
195197
Global
196198
GlobalSection(SolutionConfigurationPlatforms) = preSolution
197199
Debug|Any CPU = Debug|Any CPU
@@ -522,6 +524,10 @@ Global
522524
{D417E1B9-D146-4983-81D0-79F3193B322B}.Debug|Any CPU.Build.0 = Debug|Any CPU
523525
{D417E1B9-D146-4983-81D0-79F3193B322B}.Release|Any CPU.ActiveCfg = Release|Any CPU
524526
{D417E1B9-D146-4983-81D0-79F3193B322B}.Release|Any CPU.Build.0 = Release|Any CPU
527+
{FD23CEA1-78EB-85D7-8EDF-047657355B52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
528+
{FD23CEA1-78EB-85D7-8EDF-047657355B52}.Debug|Any CPU.Build.0 = Debug|Any CPU
529+
{FD23CEA1-78EB-85D7-8EDF-047657355B52}.Release|Any CPU.ActiveCfg = Release|Any CPU
530+
{FD23CEA1-78EB-85D7-8EDF-047657355B52}.Release|Any CPU.Build.0 = Release|Any CPU
525531
EndGlobalSection
526532
GlobalSection(SolutionProperties) = preSolution
527533
HideSolutionNode = FALSE
@@ -612,6 +618,7 @@ Global
612618
{08458CA3-BF81-48E8-870D-9389DC037808} = {FF1089BE-C704-4374-B629-C57C08E1798F}
613619
{4757B038-70E4-40B0-9B73-700EE5632B07} = {FF1089BE-C704-4374-B629-C57C08E1798F}
614620
{D417E1B9-D146-4983-81D0-79F3193B322B} = {FF1089BE-C704-4374-B629-C57C08E1798F}
621+
{FD23CEA1-78EB-85D7-8EDF-047657355B52} = {7B29E81D-92DE-46C8-8EDC-1B48C8F12BC2}
615622
EndGlobalSection
616623
GlobalSection(ExtensibilityGlobals) = postSolution
617624
SolutionGuid = {D5EB1960-6F30-4CE1-B375-EAE1F787D6FF}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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="BootstrapBlazor" Version="$(BBVersion)" />
18+
</ItemGroup>
19+
20+
<ItemGroup>
21+
<ProjectReference Include="..\..\..\..\BootstrapBlazor\src\BootstrapBlazor\BootstrapBlazor.csproj" />
22+
</ItemGroup>
23+
24+
<ItemGroup>
25+
<Using Include="Microsoft.JSInterop" />
26+
</ItemGroup>
27+
28+
</Project>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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+
/// 添加 AzureOpenAIService 服务
16+
/// </summary>
17+
/// <param name="services"></param>
18+
public static IServiceCollection AddBootstrapBlazorTouchSocketService(this IServiceCollection services)
19+
{
20+
services.AddSingleton<ITcpSocketFactory, DefaultTcpSocketFactory>();
21+
22+
// TBD: 这里注入 TouchSocket 相关服务
23+
return services;
24+
}
25+
}
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
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 Microsoft.Extensions.Logging;
6+
using System.Buffers;
7+
using System.Net;
8+
using System.Net.Sockets;
9+
10+
namespace BootstrapBlazor.Components;
11+
12+
class DefaultTcpSocketClient : ITcpSocketClient
13+
{
14+
private TcpClient? _client;
15+
16+
public bool IsConnected => _client?.Connected ?? false;
17+
18+
public IPEndPoint LocalEndPoint { get; }
19+
20+
public ILogger<DefaultTcpSocketClient>? Logger { get; set; }
21+
22+
public IDataPackageAdapter? DataPackageAdapter { get; set; }
23+
24+
public DefaultTcpSocketClient(string host, int port = 0)
25+
{
26+
LocalEndPoint = new IPEndPoint(GetIPAddress(host), port);
27+
}
28+
29+
private static IPAddress GetIPAddress(string host) => host.Equals("localhost", StringComparison.OrdinalIgnoreCase)
30+
? IPAddress.Loopback
31+
: IPAddress.TryParse(host, out var ip) ? ip : Dns.GetHostAddresses(host).FirstOrDefault() ?? IPAddress.Loopback;
32+
33+
public Task<bool> ConnectAsync(string host, int port, CancellationToken token = default)
34+
{
35+
var endPoint = new IPEndPoint(GetIPAddress(host), port);
36+
return ConnectAsync(endPoint, token);
37+
}
38+
39+
public async Task<bool> ConnectAsync(IPEndPoint endPoint, CancellationToken token = default)
40+
{
41+
var ret = false;
42+
try
43+
{
44+
// 释放资源
45+
Close();
46+
47+
// 创建新的 TouchSocketClient 实例
48+
_client ??= new TcpClient(LocalEndPoint);
49+
await _client.ConnectAsync(endPoint, token);
50+
ret = true;
51+
}
52+
catch (OperationCanceledException ex)
53+
{
54+
LogWarning(ex, $"TCP Socket connect operation was canceled to {endPoint}");
55+
}
56+
catch (Exception ex)
57+
{
58+
LogError(ex, $"TCP Socket connection failed to {endPoint}");
59+
}
60+
return ret;
61+
}
62+
63+
public async Task<bool> SendAsync(Memory<byte> data, CancellationToken token = default)
64+
{
65+
if (_client is not { Connected: true })
66+
{
67+
throw new InvalidOperationException("TCP Socket is not connected.");
68+
}
69+
70+
var ret = false;
71+
try
72+
{
73+
var stream = _client.GetStream();
74+
await stream.WriteAsync(data, token);
75+
ret = true;
76+
}
77+
catch (OperationCanceledException ex)
78+
{
79+
LogWarning(ex, $"TCP Socket send operation was canceled to {_client.Client.RemoteEndPoint}");
80+
}
81+
catch (Exception ex)
82+
{
83+
LogError(ex, $"TCP Socket send failed to {_client.Client.RemoteEndPoint}");
84+
}
85+
return ret;
86+
}
87+
88+
public async Task<Memory<byte>> ReceiveAsync(int bufferSize = 1024 * 10, CancellationToken token = default)
89+
{
90+
if (_client is not { Connected: true })
91+
{
92+
throw new InvalidOperationException("TCP Socket is not connected.");
93+
}
94+
95+
var block = ArrayPool<byte>.Shared.Rent(bufferSize);
96+
var buffer = new Memory<byte>(block);
97+
try
98+
{
99+
var stream = _client.GetStream();
100+
var len = await stream.ReadAsync(buffer, token);
101+
if (len == 0)
102+
{
103+
LogInformation($"TCP Socket received {len} data from {_client.Client.RemoteEndPoint}");
104+
}
105+
else
106+
{
107+
buffer = buffer[..len];
108+
109+
if (DataPackageAdapter != null)
110+
{
111+
buffer = await DataPackageAdapter.ReceiveAsync(buffer);
112+
}
113+
}
114+
}
115+
catch (OperationCanceledException ex)
116+
{
117+
LogWarning(ex, $"TCP Socket receive operation was canceled to {_client.Client.RemoteEndPoint}");
118+
}
119+
catch (Exception ex)
120+
{
121+
LogError(ex, $"TCP Socket receive failed to {_client.Client.RemoteEndPoint}");
122+
}
123+
finally
124+
{
125+
ArrayPool<byte>.Shared.Return(block);
126+
}
127+
return buffer;
128+
}
129+
130+
public void Close()
131+
{
132+
Dispose(true);
133+
}
134+
135+
private void LogInformation(string message)
136+
{
137+
Logger?.LogInformation("{message}", message);
138+
}
139+
140+
private void LogWarning(Exception ex, string message)
141+
{
142+
Logger?.LogWarning(ex, "{message}", message);
143+
}
144+
145+
private void LogError(Exception ex, string message)
146+
{
147+
Logger?.LogError(ex, "{message}", message);
148+
}
149+
150+
private void Dispose(bool disposing)
151+
{
152+
if (disposing)
153+
{
154+
if (_client != null)
155+
{
156+
_client.Close();
157+
_client = null;
158+
}
159+
}
160+
}
161+
162+
/// <summary>
163+
/// <inheritdoc/>
164+
/// </summary>
165+
public void Dispose()
166+
{
167+
Dispose(true);
168+
GC.SuppressFinalize(this);
169+
}
170+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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 Microsoft.Extensions.DependencyInjection;
6+
using Microsoft.Extensions.Logging;
7+
using System.Collections.Concurrent;
8+
9+
namespace BootstrapBlazor.Components;
10+
11+
class DefaultTcpSocketFactory(IServiceProvider provider) : ITcpSocketFactory
12+
{
13+
private readonly ConcurrentDictionary<string, ITcpSocketClient> _pool = new();
14+
15+
public ITcpSocketClient GetOrCreate(string host, int port = 0, SocketMode mode = SocketMode.Client)
16+
{
17+
return _pool.GetOrAdd($"{host}:{port}", key =>
18+
{
19+
var client = new DefaultTcpSocketClient(host, port)
20+
{
21+
Logger = provider.GetService<ILogger<DefaultTcpSocketClient>>()
22+
};
23+
return client;
24+
});
25+
}
26+
27+
private void Dispose(bool disposing)
28+
{
29+
if (disposing)
30+
{
31+
// 释放托管资源
32+
foreach (var socket in _pool.Values)
33+
{
34+
socket.Dispose();
35+
}
36+
_pool.Clear();
37+
}
38+
}
39+
40+
/// <summary>
41+
/// <inheritdoc/>
42+
/// </summary>
43+
public void Dispose()
44+
{
45+
Dispose(true);
46+
GC.SuppressFinalize(this);
47+
}
48+
}

0 commit comments

Comments
 (0)