Skip to content

Commit 0672413

Browse files
committed
feat: 增加 Socket 相关服务
1 parent 7071e2f commit 0672413

66 files changed

Lines changed: 4346 additions & 8 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

BootstrapBlazor.Extensions.sln

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.Mermaid", "
156156
EndProject
157157
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.MeiliSearch", "src\components\BootstrapBlazor.MeiliSearch\BootstrapBlazor.MeiliSearch.csproj", "{4B086A62-5F5A-47BC-921F-35803F26DD68}"
158158
EndProject
159-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniTestSvgIcon", "test\UniTestSvgIcon\UniTestSvgIcon.csproj", "{F965576B-A801-4473-85FE-E100125FDEF5}"
160-
EndProject
161159
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.RDKit", "src\components\BootstrapBlazor.RDKit\BootstrapBlazor.RDKit.csproj", "{7328E464-AE3C-4277-BEC3-422C56637066}"
162160
EndProject
163161
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.SmilesDrawer", "src\components\BootstrapBlazor.SmilesDrawer\BootstrapBlazor.SmilesDrawer.csproj", "{84823875-1B07-4CCE-A009-29AEF90C6C10}"
@@ -190,6 +188,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.Vditor", "s
190188
EndProject
191189
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.OfficeViewer", "src\components\BootstrapBlazor.OfficeViewer\BootstrapBlazor.OfficeViewer.csproj", "{2436940C-5920-D801-8A81-721F4C20A355}"
192190
EndProject
191+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.Socket", "src\extensions\BootstrapBlazor.Socket\BootstrapBlazor.Socket.csproj", "{965F1512-57DC-4621-9C74-E059A14BB866}"
192+
EndProject
193+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BootstrapBlazor.TcpSocket", "src\extensions\BootstrapBlazor.TcpSocket\BootstrapBlazor.TcpSocket.csproj", "{3F7C6E3F-5AC2-4B13-A57F-9329E34C1F5C}"
194+
EndProject
195+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTestTcpSocket", "test\UnitTestTcpSocket\UnitTestTcpSocket.csproj", "{10D35EE5-FA31-4C80-B113-CD7A0FB76B4E}"
196+
EndProject
197+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTestSvgIcon", "test\UnitTestSvgIcon\UnitTestSvgIcon.csproj", "{7CAD5915-CE3E-31ED-B1AC-15C61C3ED8C3}"
198+
EndProject
193199
Global
194200
GlobalSection(SolutionConfigurationPlatforms) = preSolution
195201
Debug|Any CPU = Debug|Any CPU
@@ -448,10 +454,6 @@ Global
448454
{4B086A62-5F5A-47BC-921F-35803F26DD68}.Debug|Any CPU.Build.0 = Debug|Any CPU
449455
{4B086A62-5F5A-47BC-921F-35803F26DD68}.Release|Any CPU.ActiveCfg = Release|Any CPU
450456
{4B086A62-5F5A-47BC-921F-35803F26DD68}.Release|Any CPU.Build.0 = Release|Any CPU
451-
{F965576B-A801-4473-85FE-E100125FDEF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
452-
{F965576B-A801-4473-85FE-E100125FDEF5}.Debug|Any CPU.Build.0 = Debug|Any CPU
453-
{F965576B-A801-4473-85FE-E100125FDEF5}.Release|Any CPU.ActiveCfg = Release|Any CPU
454-
{F965576B-A801-4473-85FE-E100125FDEF5}.Release|Any CPU.Build.0 = Release|Any CPU
455457
{7328E464-AE3C-4277-BEC3-422C56637066}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
456458
{7328E464-AE3C-4277-BEC3-422C56637066}.Debug|Any CPU.Build.0 = Debug|Any CPU
457459
{7328E464-AE3C-4277-BEC3-422C56637066}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -516,6 +518,22 @@ Global
516518
{2436940C-5920-D801-8A81-721F4C20A355}.Debug|Any CPU.Build.0 = Debug|Any CPU
517519
{2436940C-5920-D801-8A81-721F4C20A355}.Release|Any CPU.ActiveCfg = Release|Any CPU
518520
{2436940C-5920-D801-8A81-721F4C20A355}.Release|Any CPU.Build.0 = Release|Any CPU
521+
{965F1512-57DC-4621-9C74-E059A14BB866}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
522+
{965F1512-57DC-4621-9C74-E059A14BB866}.Debug|Any CPU.Build.0 = Debug|Any CPU
523+
{965F1512-57DC-4621-9C74-E059A14BB866}.Release|Any CPU.ActiveCfg = Release|Any CPU
524+
{965F1512-57DC-4621-9C74-E059A14BB866}.Release|Any CPU.Build.0 = Release|Any CPU
525+
{3F7C6E3F-5AC2-4B13-A57F-9329E34C1F5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
526+
{3F7C6E3F-5AC2-4B13-A57F-9329E34C1F5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
527+
{3F7C6E3F-5AC2-4B13-A57F-9329E34C1F5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
528+
{3F7C6E3F-5AC2-4B13-A57F-9329E34C1F5C}.Release|Any CPU.Build.0 = Release|Any CPU
529+
{10D35EE5-FA31-4C80-B113-CD7A0FB76B4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
530+
{10D35EE5-FA31-4C80-B113-CD7A0FB76B4E}.Debug|Any CPU.Build.0 = Debug|Any CPU
531+
{10D35EE5-FA31-4C80-B113-CD7A0FB76B4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
532+
{10D35EE5-FA31-4C80-B113-CD7A0FB76B4E}.Release|Any CPU.Build.0 = Release|Any CPU
533+
{7CAD5915-CE3E-31ED-B1AC-15C61C3ED8C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
534+
{7CAD5915-CE3E-31ED-B1AC-15C61C3ED8C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
535+
{7CAD5915-CE3E-31ED-B1AC-15C61C3ED8C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
536+
{7CAD5915-CE3E-31ED-B1AC-15C61C3ED8C3}.Release|Any CPU.Build.0 = Release|Any CPU
519537
EndGlobalSection
520538
GlobalSection(SolutionProperties) = preSolution
521539
HideSolutionNode = FALSE
@@ -588,7 +606,6 @@ Global
588606
{29E47F4A-CE03-42B5-BDAA-FB4B40D4C897} = {FF1089BE-C704-4374-B629-C57C08E1798F}
589607
{DA2198E1-2CA9-EE53-926B-7950AB4B5EBF} = {FF1089BE-C704-4374-B629-C57C08E1798F}
590608
{4B086A62-5F5A-47BC-921F-35803F26DD68} = {FF1089BE-C704-4374-B629-C57C08E1798F}
591-
{F965576B-A801-4473-85FE-E100125FDEF5} = {B6A98ADE-D26A-4D0B-8978-AB7AC915F5AE}
592609
{7328E464-AE3C-4277-BEC3-422C56637066} = {FF1089BE-C704-4374-B629-C57C08E1798F}
593610
{84823875-1B07-4CCE-A009-29AEF90C6C10} = {FF1089BE-C704-4374-B629-C57C08E1798F}
594611
{AA4EDA37-1D81-4235-A7F6-F1C112B364EF} = {FF1089BE-C704-4374-B629-C57C08E1798F}
@@ -605,6 +622,10 @@ Global
605622
{4757B038-70E4-40B0-9B73-700EE5632B07} = {FF1089BE-C704-4374-B629-C57C08E1798F}
606623
{D417E1B9-D146-4983-81D0-79F3193B322B} = {FF1089BE-C704-4374-B629-C57C08E1798F}
607624
{2436940C-5920-D801-8A81-721F4C20A355} = {FF1089BE-C704-4374-B629-C57C08E1798F}
625+
{965F1512-57DC-4621-9C74-E059A14BB866} = {7B29E81D-92DE-46C8-8EDC-1B48C8F12BC2}
626+
{3F7C6E3F-5AC2-4B13-A57F-9329E34C1F5C} = {7B29E81D-92DE-46C8-8EDC-1B48C8F12BC2}
627+
{10D35EE5-FA31-4C80-B113-CD7A0FB76B4E} = {B6A98ADE-D26A-4D0B-8978-AB7AC915F5AE}
628+
{7CAD5915-CE3E-31ED-B1AC-15C61C3ED8C3} = {B6A98ADE-D26A-4D0B-8978-AB7AC915F5AE}
608629
EndGlobalSection
609630
GlobalSection(ExtensibilityGlobals) = postSolution
610631
SolutionGuid = {D5EB1960-6F30-4CE1-B375-EAE1F787D6FF}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<Version>9.0.0</Version>
5+
</PropertyGroup>
6+
7+
<PropertyGroup>
8+
<PackageTags>BootstrapBlazor Socket</PackageTags>
9+
<Description>BootstrapBlazor extensions of Socket</Description>
10+
</PropertyGroup>
11+
12+
</Project>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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+
namespace BootstrapBlazor.Components;
6+
7+
/// <summary>
8+
/// Socket 数据转换器接口
9+
/// </summary>
10+
public interface ISocketDataConverter
11+
{
12+
13+
}
14+
15+
/// <summary>
16+
/// Defines a method to convert raw socket data into a specified entity type.
17+
/// </summary>
18+
/// <typeparam name="TEntity">The type of entity to convert the data into.</typeparam>
19+
public interface ISocketDataConverter<TEntity> : ISocketDataConverter
20+
{
21+
/// <summary>
22+
/// Attempts to convert the specified data to an instance of <typeparamref name="TEntity"/>.
23+
/// </summary>
24+
/// <remarks>This method does not throw an exception if the conversion fails. Instead, it returns <see
25+
/// langword="false"/> and sets <paramref name="entity"/> to <see langword="null"/>.</remarks>
26+
/// <param name="data">The data to be converted, represented as a read-only memory block of bytes.</param>
27+
/// <param name="entity">When this method returns, contains the converted <typeparamref name="TEntity"/> if the conversion succeeded;
28+
/// otherwise, <see langword="null"/>.</param>
29+
/// <returns><see langword="true"/> if the conversion was successful; otherwise, <see langword="false"/>.</returns>
30+
bool TryConvertTo(ReadOnlyMemory<byte> data, [NotNullWhen(true)] out TEntity? entity);
31+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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.Reflection;
6+
7+
namespace BootstrapBlazor.Components;
8+
9+
/// <summary>
10+
/// Provides a base class for converting socket data into a specified entity type.
11+
/// </summary>
12+
/// <typeparam name="TEntity">The type of entity to convert the socket data into.</typeparam>
13+
public class SocketDataConverter<TEntity>(SocketDataConverterCollections converters) : ISocketDataConverter<TEntity>
14+
{
15+
/// <summary>
16+
/// 构造函数
17+
/// </summary>
18+
public SocketDataConverter() : this(new())
19+
{
20+
21+
}
22+
23+
/// <summary>
24+
/// <inheritdoc/>
25+
/// </summary>
26+
/// <param name="data"></param>
27+
/// <param name="entity"></param>
28+
/// <returns></returns>
29+
public virtual bool TryConvertTo(ReadOnlyMemory<byte> data, [NotNullWhen(true)] out TEntity? entity)
30+
{
31+
var v = CreateEntity();
32+
var ret = Parse(data, v);
33+
entity = ret ? v : default;
34+
return ret;
35+
}
36+
37+
/// <summary>
38+
/// 创建实体实例方法
39+
/// </summary>
40+
/// <returns></returns>
41+
protected virtual TEntity CreateEntity() => Activator.CreateInstance<TEntity>();
42+
43+
/// <summary>
44+
/// 将字节数据转换为指定实体类型的实例。
45+
/// </summary>
46+
/// <param name="data"></param>
47+
/// <param name="entity"></param>
48+
protected virtual bool Parse(ReadOnlyMemory<byte> data, TEntity entity)
49+
{
50+
// 使用 SocketDataPropertyAttribute 特性获取数据转换规则
51+
var ret = false;
52+
if (entity != null)
53+
{
54+
var unuseProperties = new List<PropertyInfo>(32);
55+
56+
// 通过 SocketDataPropertyConverterAttribute 特性获取属性转换器
57+
var properties = entity.GetType().GetProperties().Where(p => p.CanWrite).ToList();
58+
foreach (var p in properties)
59+
{
60+
var attr = p.GetCustomAttribute<SocketDataPropertyConverterAttribute>(false)
61+
?? GetPropertyConverterAttribute(p);
62+
if (attr != null)
63+
{
64+
p.SetValue(entity, attr.ConvertTo(data));
65+
}
66+
}
67+
ret = true;
68+
}
69+
return ret;
70+
}
71+
72+
private SocketDataPropertyConverterAttribute? GetPropertyConverterAttribute(PropertyInfo propertyInfo)
73+
{
74+
SocketDataPropertyConverterAttribute? attr = null;
75+
if (converters.TryGetPropertyConverter<TEntity>(propertyInfo, out var v))
76+
{
77+
attr = v;
78+
}
79+
return attr;
80+
}
81+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
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.Collections.Concurrent;
6+
using System.Linq.Expressions;
7+
using System.Reflection;
8+
9+
namespace BootstrapBlazor.Components;
10+
11+
/// <summary>
12+
/// 数据转换器集合类
13+
/// </summary>
14+
public sealed class SocketDataConverterCollections
15+
{
16+
readonly ConcurrentDictionary<Type, ISocketDataConverter> _converters = new();
17+
readonly ConcurrentDictionary<MemberInfo, SocketDataPropertyConverterAttribute> _propertyConverters = new();
18+
19+
/// <summary>
20+
/// 增加指定 <see cref="ISocketDataConverter{TEntity}"/> 数据类型转换器方法
21+
/// </summary>
22+
/// <typeparam name="TEntity"></typeparam>
23+
/// <param name="converter"></param>
24+
public void AddTypeConverter<TEntity>(ISocketDataConverter<TEntity> converter)
25+
{
26+
var type = typeof(TEntity);
27+
_converters.AddOrUpdate(type, t => converter, (t, v) => converter);
28+
}
29+
30+
/// <summary>
31+
/// 增加默认数据类型转换器方法 转换器使用 <see cref="SocketDataConverter{TEntity}"/>
32+
/// </summary>
33+
/// <typeparam name="TEntity"></typeparam>
34+
public void AddTypeConverter<TEntity>() => AddTypeConverter(new SocketDataConverter<TEntity>(this));
35+
36+
/// <summary>
37+
/// 添加属性类型转化器方法
38+
/// </summary>
39+
/// <typeparam name="TEntity"></typeparam>
40+
/// <param name="propertyExpression"></param>
41+
/// <param name="attribute"></param>
42+
public void AddPropertyConverter<TEntity>(Expression<Func<TEntity, object?>> propertyExpression, SocketDataPropertyConverterAttribute attribute)
43+
{
44+
if (propertyExpression.Body is MemberExpression memberExpression)
45+
{
46+
if (attribute.Type == null)
47+
{
48+
attribute.Type = memberExpression.Type;
49+
}
50+
_propertyConverters.AddOrUpdate(memberExpression.Member, m => attribute, (m, v) => attribute);
51+
}
52+
}
53+
54+
/// <summary>
55+
/// 获得指定数据类型转换器方法
56+
/// </summary>
57+
/// <typeparam name="TEntity"></typeparam>
58+
public bool TryGetTypeConverter<TEntity>([NotNullWhen(true)] out ISocketDataConverter<TEntity>? converter)
59+
{
60+
converter = null;
61+
var ret = false;
62+
if (_converters.TryGetValue(typeof(TEntity), out var v) && v is ISocketDataConverter<TEntity> c)
63+
{
64+
converter = c;
65+
ret = true;
66+
}
67+
return ret;
68+
}
69+
70+
/// <summary>
71+
/// 获得指定数据类型属性转换器方法
72+
/// </summary>
73+
/// <typeparam name="TEntity"></typeparam>
74+
public bool TryGetPropertyConverter<TEntity>(Expression<Func<TEntity, object?>> propertyExpression, [NotNullWhen(true)] out SocketDataPropertyConverterAttribute? converterAttribute)
75+
{
76+
converterAttribute = null;
77+
var ret = false;
78+
if (propertyExpression.Body is MemberExpression memberExpression && TryGetPropertyConverter<TEntity>(memberExpression.Member, out var v))
79+
{
80+
converterAttribute = v;
81+
ret = true;
82+
}
83+
return ret;
84+
}
85+
86+
/// <summary>
87+
/// 获得指定数据类型属性转换器方法
88+
/// </summary>
89+
/// <typeparam name="TEntity"></typeparam>
90+
public bool TryGetPropertyConverter<TEntity>(MemberInfo memberInfo, [NotNullWhen(true)] out SocketDataPropertyConverterAttribute? converterAttribute)
91+
{
92+
converterAttribute = null;
93+
var ret = false;
94+
if (_propertyConverters.TryGetValue(memberInfo, out var v))
95+
{
96+
converterAttribute = v;
97+
ret = true;
98+
}
99+
return ret;
100+
}
101+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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+
namespace BootstrapBlazor.Components;
6+
7+
/// <summary>
8+
/// Represents an attribute used to mark a field as a socket data field.
9+
/// </summary>
10+
/// <remarks>This attribute can be applied to fields to indicate that they are part of the data transmitted over a
11+
/// socket connection. It is intended for use in scenarios where socket communication requires specific fields to be
12+
/// identified for processing.</remarks>
13+
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
14+
public class SocketDataPropertyConverterAttribute : Attribute
15+
{
16+
/// <summary>
17+
/// 获得/设置 数据类型
18+
/// </summary>
19+
public Type? Type { get; set; }
20+
21+
/// <summary>
22+
/// 获得/设置 数据偏移量
23+
/// </summary>
24+
public int Offset { get; set; }
25+
26+
/// <summary>
27+
/// 获得/设置 数据长度
28+
/// </summary>
29+
public int Length { get; set; }
30+
31+
/// <summary>
32+
/// 获得/设置 数据编码名称
33+
/// </summary>
34+
public string? EncodingName { get; set; }
35+
36+
/// <summary>
37+
/// 获得/设置 数据转换器类型
38+
/// </summary>
39+
public Type? ConverterType { get; set; }
40+
41+
/// <summary>
42+
/// 获得/设置 数据转换器构造函数参数
43+
/// </summary>
44+
public object?[]? ConverterParameters { get; set; }
45+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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+
namespace BootstrapBlazor.Components;
6+
7+
/// <summary>
8+
///
9+
/// </summary>
10+
[AttributeUsage(AttributeTargets.Class)]
11+
public class SocketDataTypeConverterAttribute : Attribute
12+
{
13+
/// <summary>
14+
/// Gets or sets the type of the <see cref="ISocketDataConverter{TEntity}"/>.
15+
/// </summary>
16+
public Type? Type { get; set; }
17+
}

0 commit comments

Comments
 (0)