Skip to content

Commit d8db8a3

Browse files
committed
feat: 增加 SocketDataConverter 标签
1 parent 9fb2e1f commit d8db8a3

File tree

4 files changed

+217
-0
lines changed

4 files changed

+217
-0
lines changed

src/BootstrapBlazor/Extensions/ITcpSocketClientExtensions.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information.
44
// Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone
55

6+
using System.Reflection;
67
using System.Runtime.Versioning;
78
using System.Text;
89

@@ -107,4 +108,46 @@ public static void SetDataPackageAdapter<TEntity>(this ITcpSocketClient client,
107108
await callback(ret);
108109
};
109110
}
111+
112+
/// <summary>
113+
/// Configures the specified <see cref="ITcpSocketClient"/> to use a custom data package adapter and callback
114+
/// function.
115+
/// </summary>
116+
/// <remarks>This method sets up the <paramref name="client"/> to use the specified <paramref
117+
/// name="adapter"/> for handling incoming data. If the <typeparamref name="TEntity"/> type is decorated with a <see
118+
/// cref="SocketDataConverterAttribute"/>, the associated converter is used to transform the data before invoking
119+
/// the <paramref name="callback"/>. The callback is called with the converted entity or <see langword="null"/> if
120+
/// conversion fails.</remarks>
121+
/// <typeparam name="TEntity">The type of entity that the data package adapter will handle.</typeparam>
122+
/// <param name="client">The TCP socket client to configure.</param>
123+
/// <param name="adapter">The data package adapter responsible for processing incoming data.</param>
124+
/// <param name="callback">The callback function to invoke with the processed entity of type <typeparamref name="TEntity"/>.</param>
125+
public static void SetDataPackageAdapter<TEntity>(this ITcpSocketClient client, IDataPackageAdapter adapter, Func<TEntity?, Task> callback)
126+
{
127+
// 设置 ITcpSocketClient 的回调函数
128+
client.ReceivedCallBack = async buffer =>
129+
{
130+
// 将接收到的数据传递给 DataPackageAdapter 进行数据处理合规数据触发 ReceivedCallBack 回调
131+
await adapter.HandlerAsync(buffer);
132+
};
133+
134+
var type = typeof(TEntity);
135+
var converterType = type.GetCustomAttribute<SocketDataConverterAttribute>();
136+
if (converterType is { Type: not null })
137+
{
138+
if (Activator.CreateInstance(converterType.Type) is ISocketDataConverter<TEntity> socketDataConverter)
139+
{
140+
// 设置 DataPackageAdapter 的回调函数
141+
adapter.ReceivedCallBack = async buffer =>
142+
{
143+
TEntity? ret = default;
144+
if (socketDataConverter.TryConvertTo(buffer, out var t))
145+
{
146+
ret = t;
147+
}
148+
await callback(ret);
149+
};
150+
}
151+
}
152+
}
110153
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
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+
namespace BootstrapBlazor.Components;
7+
8+
/// <summary>
9+
///
10+
/// </summary>
11+
[AttributeUsage(AttributeTargets.Class)]
12+
public class SocketDataConverterAttribute : Attribute
13+
{
14+
/// <summary>
15+
/// Gets or sets the type of the <see cref="ISocketDataConverter{TEntity}"/>.
16+
/// </summary>
17+
public Type? Type { get; set; }
18+
}

src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataConverterBase.cs

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
// See the LICENSE file in the project root for more information.
44
// Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone
55

6+
using System.Reflection;
7+
using System.Text;
8+
69
namespace BootstrapBlazor.Components;
710

811
/// <summary>
@@ -18,4 +21,121 @@ public abstract class SocketDataConverterBase<TEntity> : ISocketDataConverter<TE
1821
/// <param name="entity"></param>
1922
/// <returns></returns>
2023
public abstract bool TryConvertTo(ReadOnlyMemory<byte> data, [NotNullWhen(true)] out TEntity? entity);
24+
25+
/// <summary>
26+
/// 将字节数据转换为指定实体类型的实例。
27+
/// </summary>
28+
/// <param name="data"></param>
29+
/// <param name="entity"></param>
30+
protected virtual bool Parse(ReadOnlyMemory<byte> data, TEntity entity)
31+
{
32+
// 使用 SocketDataFieldAttribute 特性获取数据转换规则
33+
var ret = false;
34+
if (entity != null)
35+
{
36+
var properties = entity.GetType().GetProperties()
37+
.Where(p => p.GetCustomAttribute(typeof(SocketDataFieldAttribute), false) != null && p.CanWrite).ToList();
38+
foreach (var p in properties)
39+
{
40+
var attr = p.GetCustomAttribute<SocketDataFieldAttribute>(false);
41+
if (attr != null)
42+
{
43+
var type = attr.Type;
44+
if (type == null)
45+
{
46+
continue;
47+
}
48+
49+
var encodingName = attr.EncodingName;
50+
var start = attr.Start;
51+
var length = attr.Length;
52+
53+
if (data.Length >= start + length)
54+
{
55+
var buffer = data.Slice(start, length);
56+
p.SetValue(entity, ParseValue(buffer, type, encodingName));
57+
}
58+
}
59+
}
60+
ret = true;
61+
}
62+
return ret;
63+
}
64+
65+
/// <summary>
66+
/// 将字节缓冲区转换为指定类型的值。
67+
/// </summary>
68+
/// <param name="buffer"></param>
69+
/// <param name="type"></param>
70+
/// <param name="encodingName"></param>
71+
/// <returns></returns>
72+
protected virtual object? ParseValue(ReadOnlyMemory<byte> buffer, Type type, string? encodingName)
73+
{
74+
// 根据类型转换数据
75+
if (type == typeof(string))
76+
{
77+
var encoding = string.IsNullOrEmpty(encodingName) ? Encoding.UTF8 : Encoding.GetEncoding(encodingName);
78+
return encoding.GetString(buffer.Span);
79+
}
80+
else if (type == typeof(int))
81+
{
82+
return BitConverter.ToInt32(buffer.Span);
83+
}
84+
else if (type == typeof(long))
85+
{
86+
return BitConverter.ToInt64(buffer.Span);
87+
}
88+
else if (type == typeof(double))
89+
{
90+
return BitConverter.ToDouble(buffer.Span);
91+
}
92+
else if (type == typeof(byte[]))
93+
{
94+
return buffer.ToArray();
95+
}
96+
if (type.IsEnum)
97+
{
98+
return Enum.ToObject(type, BitConverter.ToInt32(buffer.Span));
99+
}
100+
else if (type == typeof(bool))
101+
{
102+
return BitConverter.ToBoolean(buffer.Span);
103+
}
104+
else if (type == typeof(float))
105+
{
106+
return BitConverter.ToSingle(buffer.Span);
107+
}
108+
else if (type == typeof(short))
109+
{
110+
return BitConverter.ToInt16(buffer.Span);
111+
}
112+
else if (type == typeof(ushort))
113+
{
114+
return BitConverter.ToUInt16(buffer.Span);
115+
}
116+
else if (type == typeof(uint))
117+
{
118+
return BitConverter.ToUInt32(buffer.Span);
119+
}
120+
else if (type == typeof(ulong))
121+
{
122+
return BitConverter.ToUInt64(buffer.Span);
123+
}
124+
else
125+
{
126+
return ParseValueResolve(buffer, type, encodingName);
127+
}
128+
}
129+
130+
/// <summary>
131+
/// 将字节缓冲区转换为指定类型的值,允许子类自定义补充解析逻辑。
132+
/// </summary>
133+
/// <param name="buffer"></param>
134+
/// <param name="type"></param>
135+
/// <param name="encodingName"></param>
136+
/// <returns></returns>
137+
protected virtual object? ParseValueResolve(ReadOnlyMemory<byte> buffer, Type type, string? encodingName)
138+
{
139+
return null;
140+
}
21141
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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+
namespace BootstrapBlazor.Components;
7+
8+
/// <summary>
9+
/// Represents an attribute used to mark a field as a socket data field.
10+
/// </summary>
11+
/// <remarks>This attribute can be applied to fields to indicate that they are part of the data transmitted over a
12+
/// socket connection. It is intended for use in scenarios where socket communication requires specific fields to be
13+
/// identified for processing.</remarks>
14+
[AttributeUsage(AttributeTargets.Property)]
15+
public class SocketDataFieldAttribute : Attribute
16+
{
17+
/// <summary>
18+
///
19+
/// </summary>
20+
public Type? Type { get; set; }
21+
22+
/// <summary>
23+
///
24+
/// </summary>
25+
public int Start { get; set; }
26+
27+
/// <summary>
28+
///
29+
/// </summary>
30+
public int Length { get; set; }
31+
32+
/// <summary>
33+
///
34+
/// </summary>
35+
public string? EncodingName { get; set; }
36+
}

0 commit comments

Comments
 (0)