Skip to content

Commit 8775218

Browse files
authored
doc(ITcpSocketClient): add DataHandler sample code (#6331)
* doc: 增加接收数据示例文档说明 * refactor: 重构代码 * doc: 更新配置类说明 * doc: 增加数据适配器文档 * refactor: 更新接收数据示例代码 * doc: 增加数据适配器菜单 * doc: 更新示例 * doc: 增加后台服务
1 parent ef82709 commit 8775218

File tree

12 files changed

+299
-25
lines changed

12 files changed

+299
-25
lines changed

src/BootstrapBlazor.Server/Components/Samples/SocketFactories.razor

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@
1515
[NotNull]
1616
private ITcpSocketFactory? TcpSocketFactory { get; set; }</Pre>
1717

18-
<Pre>var client = factory.GetOrCreate("192.168.1.100", 0);</Pre>
18+
<Pre>var client = TcpSocketFactory.GetOrCreate("bb", options =>
19+
{
20+
options.LocalEndPoint = new IPEndPoint(IPAddress.Loopback, 0);
21+
});</Pre>
1922

2023
<p class="code-label">3. 使用方法</p>
2124

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
@page "/socket/adapter"
2+
@inject IStringLocalizer<Adapters> Localizer
3+
4+
<h3>@Localizer["AdaptersTitle"]</h3>
5+
<h4>@Localizer["AdaptersDescription"]</h4>
6+
7+
<Notice></Notice>
8+
9+
<DemoBlock Title="@Localizer["NormalTitle"]"
10+
Introduction="@Localizer["NormalIntro"]"
11+
Name="Normal" ShowCode="false">
12+
<p>本例中连接一个模拟自定义协议服务,每次接收到客户端发来的特定数据后,返回业务数据。这类应用在我们实际应用中非常常见</p>
13+
<p>通过 <code>SocketClientOptions</code> 配置类关闭自动接收数据功能 <code>IsAutoReceive="false"</code></p>
14+
<Pre>_client = TcpSocketFactory.GetOrCreate("demo-adapter", options =>
15+
{
16+
options.IsAutoReceive = false;
17+
options.LocalEndPoint = new IPEndPoint(IPAddress.Loopback, 0);
18+
});</Pre>
19+
<ul class="ul-demo">
20+
<li>点击 <b>连接</b> 按钮后通过 <code>ITcpSocketFactory</code> 服务实例创建的 <code>ITcpSocketClient</code> 对象连接到网站模拟 <code>TcpServer</code></li>
21+
<li>点击 <b>断开</b> 按钮调用 <code>CloseAsync</code> 方法断开 Socket 连接</li>
22+
<li>点击 <b>发送</b> 按钮调用 <code>SendAsync</code> 方法发送请求数据</li>
23+
</ul>
24+
<p class="code-label">通讯协议讲解:</p>
25+
<p>在实际应用开发中,通讯数据协议很多时候是双方约定的。我们假设本示例通讯协议规约为定长格式具体如下:</p>
26+
<ul class="ul-demo">
27+
<li>发送数据包格式为 <code>请求头(Header)+ 请求体(Body)</code> 长度总和为 12 个字节</li>
28+
<li>请求头为 4 字节定长,请求体为 8 个字节定长</li>
29+
<li>请求体为字符串类型数据</li>
30+
<li>返回数据包格式为 <code>响应头(Header)+ 响应体(Body)</code> 长度总和为 12 个字节</li>
31+
<li>响应头为 4 字节定长,响应体为 8 个字节定长</li>
32+
<li>响应体为字符串类型数据</li>
33+
</ul>
34+
35+
<div class="row form-inline g-3">
36+
<div class="col-12 col-sm-6">
37+
<Button Text="连接" Icon="fa-solid fa-play"
38+
OnClick="OnConnectAsync" IsDisabled="@_client.IsConnected"></Button>
39+
<Button Text="断开" Icon="fa-solid fa-stop" class="ms-2"
40+
OnClick="OnCloseAsync" IsDisabled="@(!_client.IsConnected)"></Button>
41+
<Button Text="发送" Icon="fa-solid fa-paper-plane" class="ms-2" IsAsync="true"
42+
OnClick="OnSendAsync" IsDisabled="@(!_client.IsConnected)"></Button>
43+
</div>
44+
<div class="col-12">
45+
<Console Items="@_items" Height="496" HeaderText="模拟通讯示例"
46+
ShowAutoScroll="true" OnClear="@OnClear"></Console>
47+
</div>
48+
</div>
49+
</DemoBlock>
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the Apache 2.0 License
3+
// See the LICENSE file in the project root for more information.
4+
// Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone
5+
6+
using BootstrapBlazor.Server.Components.Components;
7+
using System.Net;
8+
using System.Text;
9+
10+
namespace BootstrapBlazor.Server.Components.Samples.Sockets;
11+
12+
/// <summary>
13+
/// 数据适配器示例
14+
/// </summary>
15+
public partial class Adapters : IDisposable
16+
{
17+
[Inject, NotNull]
18+
private ITcpSocketFactory? TcpSocketFactory { get; set; }
19+
20+
private ITcpSocketClient _client = null!;
21+
22+
private List<ConsoleMessageItem> _items = [];
23+
24+
private readonly IPEndPoint _serverEndPoint = new(IPAddress.Loopback, 8900);
25+
26+
private readonly CancellationTokenSource _connectTokenSource = new();
27+
private readonly CancellationTokenSource _sendTokenSource = new();
28+
private readonly CancellationTokenSource _receiveTokenSource = new();
29+
30+
/// <summary>
31+
/// <inheritdoc/>
32+
/// </summary>
33+
protected override void OnInitialized()
34+
{
35+
base.OnInitialized();
36+
37+
// 从服务中获取 ITcpSocketClient 实例
38+
_client = TcpSocketFactory.GetOrCreate("demo-adapter", options =>
39+
{
40+
// 关闭自动接收功能
41+
options.IsAutoReceive = false;
42+
// 设置本地使用的 IP地址与端口
43+
options.LocalEndPoint = new IPEndPoint(IPAddress.Loopback, 0);
44+
});
45+
}
46+
47+
private async Task OnConnectAsync()
48+
{
49+
if (_client is { IsConnected: false })
50+
{
51+
await _client.ConnectAsync(_serverEndPoint, _connectTokenSource.Token);
52+
var state = _client.IsConnected ? "成功" : "失败";
53+
_items.Add(new ConsoleMessageItem()
54+
{
55+
Message = $"{DateTime.Now}: 连接 {_client.LocalEndPoint} - {_serverEndPoint} {state}",
56+
Color = _client.IsConnected ? Color.Success : Color.Danger
57+
});
58+
}
59+
}
60+
61+
private async Task OnSendAsync()
62+
{
63+
if (_client is { IsConnected: true })
64+
{
65+
// 准备通讯数据
66+
var data = new byte[12];
67+
"2025"u8.CopyTo(data);
68+
Encoding.UTF8.GetBytes(DateTime.Now.ToString("ddHHmmss")).CopyTo(data, 4);
69+
var result = await _client.SendAsync(data, _sendTokenSource.Token);
70+
if (result)
71+
{
72+
// 发送成功
73+
var payload = await _client.ReceiveAsync(_receiveTokenSource.Token);
74+
if (!payload.IsEmpty)
75+
{
76+
// 解析接收到的数据
77+
// 响应头: 4 字节表示响应体长度 [0x32, 0x30, 0x32, 0x35]
78+
// 响应体: 8 字节当前时间戳字符串
79+
data = payload.ToArray();
80+
var body = BitConverter.ToString(data);
81+
_items.Add(new ConsoleMessageItem()
82+
{
83+
Message = $"{DateTime.Now}: 接收到来自 {_serverEndPoint} 数据: {Encoding.UTF8.GetString(data)} HEX: {body}"
84+
});
85+
}
86+
}
87+
}
88+
}
89+
90+
private async Task OnCloseAsync()
91+
{
92+
if (_client is { IsConnected: true })
93+
{
94+
await _client.CloseAsync();
95+
var state = _client.IsConnected ? "失败" : "成功";
96+
_items.Add(new ConsoleMessageItem()
97+
{
98+
Message = $"{DateTime.Now}: 关闭 {_client.LocalEndPoint} - {_serverEndPoint} {state}",
99+
Color = _client.IsConnected ? Color.Danger : Color.Success
100+
});
101+
}
102+
}
103+
104+
private Task OnClear()
105+
{
106+
_items = [];
107+
return Task.CompletedTask;
108+
}
109+
110+
private void Dispose(bool disposing)
111+
{
112+
if (disposing)
113+
{
114+
// 释放连接令牌资源
115+
_connectTokenSource.Cancel();
116+
_connectTokenSource.Dispose();
117+
118+
// 释放发送令牌资源
119+
_sendTokenSource.Cancel();
120+
_sendTokenSource.Dispose();
121+
122+
// 释放接收令牌资源
123+
_receiveTokenSource.Cancel();
124+
_receiveTokenSource.Dispose();
125+
}
126+
}
127+
128+
/// <summary>
129+
/// <inheritdoc/>
130+
/// </summary>
131+
public void Dispose()
132+
{
133+
Dispose(true);
134+
GC.SuppressFinalize(this);
135+
}
136+
}

src/BootstrapBlazor.Server/Components/Samples/Sockets/Receives.razor.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
namespace BootstrapBlazor.Server.Components.Samples.Sockets;
99

1010
/// <summary>
11-
///
11+
/// 接收电文示例
1212
/// </summary>
13-
public partial class Receives : ComponentBase, IDisposable
13+
public partial class Receives : IDisposable
1414
{
1515
[Inject, NotNull]
1616
private ITcpSocketFactory? TcpSocketFactory { get; set; }
@@ -19,6 +19,8 @@ public partial class Receives : ComponentBase, IDisposable
1919

2020
private List<ConsoleMessageItem> _items = [];
2121

22+
private readonly IPEndPoint _serverEndPoint = new(IPAddress.Loopback, 8800);
23+
2224
/// <summary>
2325
/// <inheritdoc/>
2426
/// </summary>
@@ -27,15 +29,18 @@ protected override void OnInitialized()
2729
base.OnInitialized();
2830

2931
// 从服务中获取 Socket 实例
30-
_client = TcpSocketFactory.GetOrCreate("bb", key => new IPEndPoint(IPAddress.Loopback, 0));
32+
_client = TcpSocketFactory.GetOrCreate("demo-receive", options =>
33+
{
34+
options.LocalEndPoint = new IPEndPoint(IPAddress.Loopback, 0);
35+
});
3136
_client.ReceivedCallBack += OnReceivedAsync;
3237
}
3338

3439
private async Task OnConnectAsync()
3540
{
3641
if (_client is { IsConnected: false })
3742
{
38-
await _client.ConnectAsync("127.0.0.1", 8800, CancellationToken.None);
43+
await _client.ConnectAsync(_serverEndPoint, CancellationToken.None);
3944
}
4045
}
4146

src/BootstrapBlazor.Server/Extensions/MenusLocalizerExtensions.cs

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,15 @@ void AddSocket(DemoMenuItem item)
218218
{
219219
new()
220220
{
221+
IsNew = true,
221222
Text = Localizer["SocketReceive"],
222223
Url = "socket/receive"
224+
},
225+
new()
226+
{
227+
IsNew = true,
228+
Text = Localizer["SocketDataAdapter"],
229+
Url = "socket/adapter"
223230
}
224231
};
225232
AddBadge(item, count: 1);
@@ -257,7 +264,6 @@ void AddQuickStar(DemoMenuItem item)
257264
},
258265
new()
259266
{
260-
IsUpdate = true,
261267
Text = Localizer["Labels"],
262268
Url = "label"
263269
},
@@ -430,7 +436,6 @@ void AddForm(DemoMenuItem item)
430436
},
431437
new()
432438
{
433-
IsNew = true,
434439
Text = Localizer["OtpInput"],
435440
Url = "otp-input"
436441
},
@@ -467,13 +472,11 @@ void AddForm(DemoMenuItem item)
467472
},
468473
new()
469474
{
470-
IsUpdate = true,
471475
Text = Localizer["SelectTable"],
472476
Url = "select-table"
473477
},
474478
new()
475479
{
476-
IsUpdate = true,
477480
Text = Localizer["SelectTree"],
478481
Url = "select-tree"
479482
},
@@ -539,7 +542,6 @@ void AddForm(DemoMenuItem item)
539542
},
540543
new()
541544
{
542-
IsNew = true,
543545
Text = Localizer["Vditor"],
544546
Url = "vditor"
545547
}
@@ -830,13 +832,11 @@ void AddData(DemoMenuItem item)
830832
},
831833
new()
832834
{
833-
IsNew = true,
834835
Text = Localizer["Typed"],
835836
Url = "typed"
836837
},
837838
new()
838839
{
839-
IsNew = true,
840840
Text = Localizer["UniverSheet"],
841841
Url = "univer-sheet"
842842
},
@@ -1241,7 +1241,6 @@ void AddNotice(DemoMenuItem item)
12411241
},
12421242
new()
12431243
{
1244-
IsNew = true,
12451244
Text = Localizer["FullScreenButton"],
12461245
Url = "fullscreen-button"
12471246
},
@@ -1267,7 +1266,6 @@ void AddNotice(DemoMenuItem item)
12671266
},
12681267
new()
12691268
{
1270-
IsNew = true,
12711269
Text = Localizer["Meet"],
12721270
Url = "meet"
12731271
},
@@ -1568,7 +1566,6 @@ void AddServices(DemoMenuItem item)
15681566
},
15691567
new()
15701568
{
1571-
IsNew = true,
15721569
Text = Localizer["Html2Image"],
15731570
Url = "html2image"
15741571
},
@@ -1615,7 +1612,6 @@ void AddServices(DemoMenuItem item)
16151612
},
16161613
new()
16171614
{
1618-
IsNew = true,
16191615
Text = Localizer["TotpService"],
16201616
Url = "otp-service"
16211617
},
@@ -1626,13 +1622,11 @@ void AddServices(DemoMenuItem item)
16261622
},
16271623
new()
16281624
{
1629-
IsNew = true,
16301625
Text = Localizer["AudioDevice"],
16311626
Url = "audio-device"
16321627
},
16331628
new()
16341629
{
1635-
IsNew = true,
16361630
Text = Localizer["VideoDevice"],
16371631
Url = "video-device"
16381632
},

src/BootstrapBlazor.Server/Extensions/ServiceCollectionExtensions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ void Invoke(BootstrapBlazorOptions option)
4545
services.AddTaskServices();
4646
services.AddHostedService<ClearTempFilesService>();
4747
services.AddHostedService<MockOnlineContributor>();
48-
services.AddHostedService<MockSocketServerService>();
48+
services.AddHostedService<MockReceiveSocketServerService>();
49+
services.AddHostedService<MockCustomProtocolSocketServerService>();
4950

5051
// 增加通用服务
5152
services.AddBootstrapBlazorServices();

src/BootstrapBlazor.Server/Locales/en-US.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7210,5 +7210,11 @@
72107210
"OfficeViewerNormalTitle": "Basic Usage",
72117211
"OfficeViewerNormalIntro": "Set the document URL for preview by configuring the <code>Url</code> value",
72127212
"OfficeViewerToastSuccessfulContent": "Office document loaded successfully"
7213+
},
7214+
"BootstrapBlazor.Server.Components.Samples.Sockets.Receives": {
7215+
"ReceivesTitle": "Socket Receive",
7216+
"ReceivesDescription": "Receive data through Socket and display it",
7217+
"NormalTitle": "Basic usage",
7218+
"NormalIntro": "After connecting, the timestamp data sent by the server is automatically received through the <code>ReceivedCallBack</code> callback method"
72137219
}
72147220
}

src/BootstrapBlazor.Server/Locales/zh-CN.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7210,5 +7210,11 @@
72107210
"OfficeViewerNormalTitle": "基本用法",
72117211
"OfficeViewerNormalIntro": "通过设置 <code>Url</code> 值设置预览文档地址",
72127212
"OfficeViewerToastSuccessfulContent": "Office 文档加载成功"
7213+
},
7214+
"BootstrapBlazor.Server.Components.Samples.Sockets.Receives": {
7215+
"ReceivesTitle": "Socket 接收示例",
7216+
"ReceivesDescription": "通过 Socket 接收数据并且显示",
7217+
"NormalTitle": "基本用法",
7218+
"NormalIntro": "连接后通过 <code>ReceivedCallBack</code> 回调方法自动接收服务端发送来的时间戳数据"
72137219
}
72147220
}

0 commit comments

Comments
 (0)