diff --git a/src/BootstrapBlazor/Extensions/ITcpSocketClientExtensions.cs b/src/BootstrapBlazor/Extensions/ITcpSocketClientExtensions.cs
index cf58a2b31ad..9fadf2ab442 100644
--- a/src/BootstrapBlazor/Extensions/ITcpSocketClientExtensions.cs
+++ b/src/BootstrapBlazor/Extensions/ITcpSocketClientExtensions.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
+using System.Reflection;
using System.Runtime.Versioning;
using System.Text;
@@ -76,20 +77,18 @@ public static void SetDataPackageAdapter(this ITcpSocketClient client, IDataPack
}
///
- /// Configures the specified to use a custom data package adapter and a callback
- /// function for processing received data.
+ /// Configures the specified to use a data package adapter and a callback function
+ /// for processing received data.
///
- /// This method sets up the to use the provided for handling incoming data. The adapter processes the raw data received by the client and
- /// attempts to convert it into an instance of . If the conversion is successful, the
- /// is invoked with the converted entity; otherwise, it is invoked with .
- /// The type of the entity that the data package adapter will attempt to convert the received data into.
- /// The instance to configure.
- /// The instance responsible for handling and processing incoming data.
- /// A callback function to be invoked with the processed data of type . The callback
- /// receives if the data cannot be converted to .
- public static void SetDataPackageAdapter(this ITcpSocketClient client, IDataPackageAdapter adapter, Func callback)
+ /// This method sets up the to process incoming data using the
+ /// specified and . The is called with the converted entity whenever data is received.
+ /// The type of the entity that the data will be converted to.
+ /// The TCP socket client to configure.
+ /// The data package adapter responsible for handling incoming data.
+ /// The converter used to transform the received data into the specified entity type.
+ /// The callback function to be invoked with the converted entity.
+ public static void SetDataPackageAdapter(this ITcpSocketClient client, IDataPackageAdapter adapter, ISocketDataConverter socketDataConverter, Func callback)
{
// 设置 ITcpSocketClient 的回调函数
client.ReceivedCallBack = async buffer =>
@@ -102,14 +101,57 @@ public static void SetDataPackageAdapter(this ITcpSocketClient client,
adapter.ReceivedCallBack = async buffer =>
{
TEntity? ret = default;
- if (adapter.TryConvertTo(buffer, out var t))
+ if (socketDataConverter.TryConvertTo(buffer, out var t))
{
- if (t is TEntity entity)
- {
- ret = entity;
- }
+ ret = t;
}
await callback(ret);
};
}
+
+ ///
+ /// Configures the specified to use a custom data package adapter and callback
+ /// function.
+ ///
+ /// This method sets up the to use the specified for handling incoming data. If the type is decorated with a , the associated converter is used to transform the data before invoking
+ /// the . The callback is called with the converted entity or if
+ /// conversion fails.
+ /// The type of entity that the data package adapter will handle.
+ /// The TCP socket client to configure.
+ /// The data package adapter responsible for processing incoming data.
+ /// The callback function to invoke with the processed entity of type .
+ public static void SetDataPackageAdapter(this ITcpSocketClient client, IDataPackageAdapter adapter, Func callback)
+ {
+ // 设置 ITcpSocketClient 的回调函数
+ client.ReceivedCallBack = async buffer =>
+ {
+ // 将接收到的数据传递给 DataPackageAdapter 进行数据处理合规数据触发 ReceivedCallBack 回调
+ await adapter.HandlerAsync(buffer);
+ };
+
+ var type = typeof(TEntity);
+ var converterType = type.GetCustomAttribute();
+ if (converterType is { Type: not null })
+ {
+ if (Activator.CreateInstance(converterType.Type) is ISocketDataConverter socketDataConverter)
+ {
+ // 设置 DataPackageAdapter 的回调函数
+ adapter.ReceivedCallBack = async buffer =>
+ {
+ TEntity? ret = default;
+ if (socketDataConverter.TryConvertTo(buffer, out var t))
+ {
+ ret = t;
+ }
+ await callback(ret);
+ };
+ }
+ }
+ else
+ {
+ adapter.ReceivedCallBack = async buffer => await callback(default);
+ }
+ }
}
diff --git a/src/BootstrapBlazor/Services/TcpSocket/DataConverter/ISocketDataConverter.cs b/src/BootstrapBlazor/Services/TcpSocket/DataConverter/ISocketDataConverter.cs
new file mode 100644
index 00000000000..1d5f3f6b0d7
--- /dev/null
+++ b/src/BootstrapBlazor/Services/TcpSocket/DataConverter/ISocketDataConverter.cs
@@ -0,0 +1,24 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License
+// See the LICENSE file in the project root for more information.
+// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
+
+namespace BootstrapBlazor.Components;
+
+///
+/// Defines a method to convert raw socket data into a specified entity type.
+///
+/// The type of entity to convert the data into.
+public interface ISocketDataConverter
+{
+ ///
+ /// Attempts to convert the specified data to an instance of .
+ ///
+ /// This method does not throw an exception if the conversion fails. Instead, it returns and sets to .
+ /// The data to be converted, represented as a read-only memory block of bytes.
+ /// When this method returns, contains the converted if the conversion succeeded;
+ /// otherwise, .
+ /// if the conversion was successful; otherwise, .
+ bool TryConvertTo(ReadOnlyMemory data, [NotNullWhen(true)] out TEntity? entity);
+}
diff --git a/src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataConverterAttribute.cs b/src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataConverterAttribute.cs
new file mode 100644
index 00000000000..4a44475bc53
--- /dev/null
+++ b/src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataConverterAttribute.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License
+// See the LICENSE file in the project root for more information.
+// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
+
+namespace BootstrapBlazor.Components;
+
+///
+///
+///
+[AttributeUsage(AttributeTargets.Class)]
+public class SocketDataConverterAttribute : Attribute
+{
+ ///
+ /// Gets or sets the type of the .
+ ///
+ public Type? Type { get; set; }
+}
diff --git a/src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataConverterBase.cs b/src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataConverterBase.cs
new file mode 100644
index 00000000000..b4b7b971cc4
--- /dev/null
+++ b/src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataConverterBase.cs
@@ -0,0 +1,48 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License
+// See the LICENSE file in the project root for more information.
+// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
+
+using System.Reflection;
+
+namespace BootstrapBlazor.Components;
+
+///
+/// Provides a base class for converting socket data into a specified entity type.
+///
+/// The type of entity to convert the socket data into.
+public abstract class SocketDataConverterBase : ISocketDataConverter
+{
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public abstract bool TryConvertTo(ReadOnlyMemory data, [NotNullWhen(true)] out TEntity? entity);
+
+ ///
+ /// 将字节数据转换为指定实体类型的实例。
+ ///
+ ///
+ ///
+ protected virtual bool Parse(ReadOnlyMemory data, TEntity entity)
+ {
+ // 使用 SocketDataFieldAttribute 特性获取数据转换规则
+ var ret = false;
+ if (entity != null)
+ {
+ var properties = entity.GetType().GetProperties().Where(p => p.CanWrite).ToList();
+ foreach (var p in properties)
+ {
+ var attr = p.GetCustomAttribute(false);
+ if (attr != null)
+ {
+ p.SetValue(entity, attr.ConvertTo(data));
+ }
+ }
+ ret = true;
+ }
+ return ret;
+ }
+}
diff --git a/src/BootstrapBlazor/Services/TcpSocket/DataPackageAdapter/DataPackageAdapter.cs b/src/BootstrapBlazor/Services/TcpSocket/DataPackageAdapter/DataPackageAdapter.cs
index a1b7e98e940..77733014514 100644
--- a/src/BootstrapBlazor/Services/TcpSocket/DataPackageAdapter/DataPackageAdapter.cs
+++ b/src/BootstrapBlazor/Services/TcpSocket/DataPackageAdapter/DataPackageAdapter.cs
@@ -48,12 +48,18 @@ public virtual async ValueTask HandlerAsync(ReadOnlyMemory data, Cancellat
///
///
///
+ ///
///
///
- public virtual bool TryConvertTo(ReadOnlyMemory data, [NotNullWhen(true)] out object? entity)
+ public virtual bool TryConvertTo(ReadOnlyMemory data, ISocketDataConverter socketDataConverter, out TEntity? entity)
{
- entity = null;
- return false;
+ entity = default;
+ var ret = socketDataConverter.TryConvertTo(data, out var v);
+ if (ret)
+ {
+ entity = v;
+ }
+ return ret;
}
///
diff --git a/src/BootstrapBlazor/Services/TcpSocket/DataPackageAdapter/IDataPackageAdapter.cs b/src/BootstrapBlazor/Services/TcpSocket/DataPackageAdapter/IDataPackageAdapter.cs
index f603c528460..e37d9272495 100644
--- a/src/BootstrapBlazor/Services/TcpSocket/DataPackageAdapter/IDataPackageAdapter.cs
+++ b/src/BootstrapBlazor/Services/TcpSocket/DataPackageAdapter/IDataPackageAdapter.cs
@@ -41,13 +41,15 @@ public interface IDataPackageAdapter
ValueTask HandlerAsync(ReadOnlyMemory data, CancellationToken token = default);
///
- /// Attempts to convert the specified binary data into an object representation.
+ /// Attempts to convert the specified byte data into an entity of type .
///
- /// This method does not throw an exception if the conversion fails. Instead, it returns and sets to .
- /// The binary data to be converted. Must not be empty.
- /// When this method returns , contains the converted object. When this method returns , contains .
+ /// This method does not throw an exception if the conversion fails. Instead, it returns and sets to its default value.
+ /// The type of the entity to convert the data to.
+ /// The byte data to be converted.
+ /// The converter used to transform the byte data into an entity.
+ /// When this method returns, contains the converted entity if the conversion was successful; otherwise, the default
+ /// value for the type of the entity.
/// if the conversion was successful; otherwise, .
- bool TryConvertTo(ReadOnlyMemory data, [NotNullWhen(true)] out object? entity);
+ bool TryConvertTo(ReadOnlyMemory data, ISocketDataConverter socketDataConverter, out TEntity? entity);
}
diff --git a/src/BootstrapBlazor/Services/TcpSocket/Extensions/SocketDataPropertyExtensions.cs b/src/BootstrapBlazor/Services/TcpSocket/Extensions/SocketDataPropertyExtensions.cs
new file mode 100644
index 00000000000..f24d5e02580
--- /dev/null
+++ b/src/BootstrapBlazor/Services/TcpSocket/Extensions/SocketDataPropertyExtensions.cs
@@ -0,0 +1,106 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License
+// See the LICENSE file in the project root for more information.
+// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
+
+namespace BootstrapBlazor.Components;
+
+static class SocketDataPropertyExtensions
+{
+ public static ISocketDataPropertyConverter? GetConverter(this SocketDataPropertyAttribute attribute)
+ {
+ return attribute.GetConverterByType() ?? attribute.GetDefaultConverter();
+ }
+
+ private static ISocketDataPropertyConverter? GetConverterByType(this SocketDataPropertyAttribute attribute)
+ {
+ ISocketDataPropertyConverter? converter = null;
+ var converterType = attribute.ConverterType;
+ if (converterType != null)
+ {
+ var converterParameters = attribute.ConverterParameters;
+ var c = Activator.CreateInstance(converterType, converterParameters);
+ if(c is ISocketDataPropertyConverter v)
+ {
+ converter = v;
+ }
+ }
+ return converter;
+ }
+
+ private static ISocketDataPropertyConverter? GetDefaultConverter(this SocketDataPropertyAttribute attribute)
+ {
+ ISocketDataPropertyConverter? converter = null;
+ var type = attribute.Type;
+ if (type != null)
+ {
+ if (type == typeof(byte[]))
+ {
+ converter = new SocketDataByteArrayConverter();
+ }
+ else if (type == typeof(string))
+ {
+ converter = new SocketDataStringConverter(attribute.EncodingName);
+ }
+ else if (type.IsEnum)
+ {
+ converter = new SocketDataEnumConverter(attribute.Type);
+ }
+ else if (type == typeof(bool))
+ {
+ converter = new SocketDataBoolConverter();
+ }
+ else if (type == typeof(short))
+ {
+ converter = new SocketDataInt16BigEndianConverter();
+ }
+ else if (type == typeof(int))
+ {
+ converter = new SocketDataInt32BigEndianConverter();
+ }
+ else if (type == typeof(long))
+ {
+ converter = new SocketDataInt64BigEndianConverter();
+ }
+ else if (type == typeof(float))
+ {
+ converter = new SocketDataSingleBigEndianConverter();
+ }
+ else if (type == typeof(double))
+ {
+ converter = new SocketDataDoubleBigEndianConverter();
+ }
+ else if (type == typeof(ushort))
+ {
+ converter = new SocketDataUInt16BigEndianConverter();
+ }
+ else if (type == typeof(uint))
+ {
+ converter = new SocketDataUInt32BigEndianConverter();
+ }
+ else if (type == typeof(ulong))
+ {
+ converter = new SocketDataUInt64BigEndianConverter();
+ }
+ }
+ return converter;
+ }
+
+ public static object? ConvertTo(this SocketDataPropertyAttribute attribute, ReadOnlyMemory data)
+ {
+ object? ret = null;
+ var start = attribute.Offset;
+ var length = attribute.Length;
+
+ if (data.Length >= start + length)
+ {
+ var buffer = data.Slice(start, length);
+ var converter = attribute.GetConverter();
+ if (converter != null)
+ {
+ ret = converter.Convert(buffer);
+ }
+ }
+ return ret;
+ }
+}
diff --git a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/ISocketDataPropertyConverter.cs b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/ISocketDataPropertyConverter.cs
new file mode 100644
index 00000000000..b49b0a2a383
--- /dev/null
+++ b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/ISocketDataPropertyConverter.cs
@@ -0,0 +1,19 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License
+// See the LICENSE file in the project root for more information.
+// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
+
+namespace BootstrapBlazor.Components;
+
+///
+/// Socket 数据转换器接口
+///
+public interface ISocketDataPropertyConverter
+{
+ ///
+ /// 将数据转换为指定类型的对象
+ ///
+ ///
+ ///
+ object? Convert(ReadOnlyMemory data);
+}
diff --git a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataBoolConverter.cs b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataBoolConverter.cs
new file mode 100644
index 00000000000..de8d2162e84
--- /dev/null
+++ b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataBoolConverter.cs
@@ -0,0 +1,26 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License
+// See the LICENSE file in the project root for more information.
+// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
+
+namespace BootstrapBlazor.Components;
+
+///
+/// Sokcet 数据转换为 bool 数据转换器
+///
+public class SocketDataBoolConverter : ISocketDataPropertyConverter
+{
+ ///
+ ///
+ ///
+ ///
+ public object? Convert(ReadOnlyMemory data)
+ {
+ var ret = false;
+ if (data.Length == 1)
+ {
+ ret = data.Span[0] != 0x00;
+ }
+ return ret;
+ }
+}
diff --git a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataByteArrayConverter.cs b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataByteArrayConverter.cs
new file mode 100644
index 00000000000..cea9b606969
--- /dev/null
+++ b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataByteArrayConverter.cs
@@ -0,0 +1,21 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License
+// See the LICENSE file in the project root for more information.
+// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
+
+namespace BootstrapBlazor.Components;
+
+///
+/// Sokcet 数据转换为 byte[] 数组转换器
+///
+public class SocketDataByteArrayConverter : ISocketDataPropertyConverter
+{
+ ///
+ ///
+ ///
+ ///
+ public object? Convert(ReadOnlyMemory data)
+ {
+ return data.ToArray();
+ }
+}
diff --git a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataDoubleBigEndianConverter.cs b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataDoubleBigEndianConverter.cs
new file mode 100644
index 00000000000..89a806c699a
--- /dev/null
+++ b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataDoubleBigEndianConverter.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License
+// See the LICENSE file in the project root for more information.
+// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
+
+using System.Buffers.Binary;
+
+namespace BootstrapBlazor.Components;
+
+///
+/// Sokcet 数据转换为 double 数据大端转换器
+///
+public class SocketDataDoubleBigEndianConverter : ISocketDataPropertyConverter
+{
+ ///
+ ///
+ ///
+ ///
+ public object? Convert(ReadOnlyMemory data)
+ {
+ double ret = 0;
+ Span paddedSpan = stackalloc byte[8];
+ data.Span.CopyTo(paddedSpan[(8 - data.Length)..]);
+ if (BinaryPrimitives.TryReadDoubleBigEndian(paddedSpan, out var v))
+ {
+ ret = v;
+ }
+ return ret;
+ }
+}
diff --git a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataDoubleLittleEndianConverter.cs b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataDoubleLittleEndianConverter.cs
new file mode 100644
index 00000000000..fd2f36cd2ee
--- /dev/null
+++ b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataDoubleLittleEndianConverter.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License
+// See the LICENSE file in the project root for more information.
+// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
+
+using System.Buffers.Binary;
+
+namespace BootstrapBlazor.Components;
+
+///
+/// Sokcet 数据转换为 double 数据小端转换器
+///
+public class SocketDataDoubleLittleEndianConverter : ISocketDataPropertyConverter
+{
+ ///
+ ///
+ ///
+ ///
+ public object? Convert(ReadOnlyMemory data)
+ {
+ double ret = 0;
+ Span paddedSpan = stackalloc byte[8];
+ data.Span.CopyTo(paddedSpan[(8 - data.Length)..]);
+ if (BinaryPrimitives.TryReadDoubleLittleEndian(paddedSpan, out var v))
+ {
+ ret = v;
+ }
+ return ret;
+ }
+}
diff --git a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataEnumConverter.cs b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataEnumConverter.cs
new file mode 100644
index 00000000000..80aec33cc4c
--- /dev/null
+++ b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataEnumConverter.cs
@@ -0,0 +1,33 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License
+// See the LICENSE file in the project root for more information.
+// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
+
+namespace BootstrapBlazor.Components;
+
+///
+/// Sokcet 数据转换为 Enum 数据转换器
+///
+public class SocketDataEnumConverter(Type? type) : ISocketDataPropertyConverter
+{
+ ///
+ ///
+ ///
+ ///
+ public object? Convert(ReadOnlyMemory data)
+ {
+ object? ret = null;
+ if (type != null)
+ {
+ if (data.Length == 1)
+ {
+ var v = data.Span[0];
+ if (Enum.TryParse(type, v.ToString(), out var enumValue))
+ {
+ ret = enumValue;
+ }
+ }
+ }
+ return ret;
+ }
+}
diff --git a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt16BigEndianConverter.cs b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt16BigEndianConverter.cs
new file mode 100644
index 00000000000..ca3d50fd112
--- /dev/null
+++ b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt16BigEndianConverter.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License
+// See the LICENSE file in the project root for more information.
+// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
+
+using System.Buffers.Binary;
+
+namespace BootstrapBlazor.Components;
+
+///
+/// Sokcet 数据转换为 short 数据大端转换器
+///
+public class SocketDataInt16BigEndianConverter : ISocketDataPropertyConverter
+{
+ ///
+ ///
+ ///
+ ///
+ public object? Convert(ReadOnlyMemory data)
+ {
+ short ret = 0;
+ Span paddedSpan = stackalloc byte[2];
+ data.Span.CopyTo(paddedSpan[(2 - data.Length)..]);
+ if (BinaryPrimitives.TryReadInt16BigEndian(paddedSpan, out var v))
+ {
+ ret = v;
+ }
+ return ret;
+ }
+}
diff --git a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt16LittleEndianConverter.cs b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt16LittleEndianConverter.cs
new file mode 100644
index 00000000000..11025eabe63
--- /dev/null
+++ b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt16LittleEndianConverter.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License
+// See the LICENSE file in the project root for more information.
+// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
+
+using System.Buffers.Binary;
+
+namespace BootstrapBlazor.Components;
+
+///
+/// Sokcet 数据转换为 short 数据小端转换器
+///
+public class SocketDataInt16LittleEndianConverter : ISocketDataPropertyConverter
+{
+ ///
+ ///
+ ///
+ ///
+ public object? Convert(ReadOnlyMemory data)
+ {
+ short ret = 0;
+ Span paddedSpan = stackalloc byte[2];
+ data.Span.CopyTo(paddedSpan[(2 - data.Length)..]);
+ if (BinaryPrimitives.TryReadInt16LittleEndian(paddedSpan, out var v))
+ {
+ ret = v;
+ }
+ return ret;
+ }
+}
diff --git a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt32BigEndianConverter.cs b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt32BigEndianConverter.cs
new file mode 100644
index 00000000000..3d02f4cbd33
--- /dev/null
+++ b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt32BigEndianConverter.cs
@@ -0,0 +1,31 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License
+// See the LICENSE file in the project root for more information.
+// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
+
+using System.Buffers.Binary;
+
+namespace BootstrapBlazor.Components;
+
+///
+/// Sokcet 数据转换为 int 数据大端转换器
+///
+public class SocketDataInt32BigEndianConverter : ISocketDataPropertyConverter
+{
+ ///
+ ///
+ ///
+ ///
+ public object? Convert(ReadOnlyMemory data)
+ {
+ var ret = 0;
+ Span paddedSpan = stackalloc byte[4];
+ data.Span.CopyTo(paddedSpan[(4 - data.Length)..]);
+
+ if (BinaryPrimitives.TryReadInt32BigEndian(paddedSpan, out var v))
+ {
+ ret = v;
+ }
+ return ret;
+ }
+}
diff --git a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt32LittleEndianConverter.cs b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt32LittleEndianConverter.cs
new file mode 100644
index 00000000000..7e0be3f5d1d
--- /dev/null
+++ b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt32LittleEndianConverter.cs
@@ -0,0 +1,31 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License
+// See the LICENSE file in the project root for more information.
+// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
+
+using System.Buffers.Binary;
+
+namespace BootstrapBlazor.Components;
+
+///
+/// Sokcet 数据转换为 int 数据小端转换器
+///
+public class SocketDataInt32LittleEndianConverter : ISocketDataPropertyConverter
+{
+ ///
+ ///
+ ///
+ ///
+ public object? Convert(ReadOnlyMemory data)
+ {
+ var ret = 0;
+ Span paddedSpan = stackalloc byte[4];
+ data.Span.CopyTo(paddedSpan[(4 - data.Length)..]);
+
+ if (BinaryPrimitives.TryReadInt32LittleEndian(paddedSpan, out var v))
+ {
+ ret = v;
+ }
+ return ret;
+ }
+}
diff --git a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt64BigEndianConverter.cs b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt64BigEndianConverter.cs
new file mode 100644
index 00000000000..1c5ffb0fa48
--- /dev/null
+++ b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt64BigEndianConverter.cs
@@ -0,0 +1,31 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License
+// See the LICENSE file in the project root for more information.
+// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
+
+using System.Buffers.Binary;
+
+namespace BootstrapBlazor.Components;
+
+///
+/// Sokcet 数据转换为 long 数据大端转换器
+///
+public class SocketDataInt64BigEndianConverter : ISocketDataPropertyConverter
+{
+ ///
+ ///
+ ///
+ ///
+ public object? Convert(ReadOnlyMemory data)
+ {
+ long ret = 0;
+ Span paddedSpan = stackalloc byte[8];
+ data.Span.CopyTo(paddedSpan[(8 - data.Length)..]);
+
+ if (BinaryPrimitives.TryReadInt64BigEndian(paddedSpan, out var v))
+ {
+ ret = v;
+ }
+ return ret;
+ }
+}
diff --git a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt64LittleEndianConverter.cs b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt64LittleEndianConverter.cs
new file mode 100644
index 00000000000..91e920d23cd
--- /dev/null
+++ b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt64LittleEndianConverter.cs
@@ -0,0 +1,31 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License
+// See the LICENSE file in the project root for more information.
+// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
+
+using System.Buffers.Binary;
+
+namespace BootstrapBlazor.Components;
+
+///
+/// Sokcet 数据转换为 long 数据小端转换器
+///
+public class SocketDataInt64LittleEndianConverter : ISocketDataPropertyConverter
+{
+ ///
+ ///
+ ///
+ ///
+ public object? Convert(ReadOnlyMemory data)
+ {
+ long ret = 0;
+ Span paddedSpan = stackalloc byte[8];
+ data.Span.CopyTo(paddedSpan[(8 - data.Length)..]);
+
+ if (BinaryPrimitives.TryReadInt64LittleEndian(paddedSpan, out var v))
+ {
+ ret = v;
+ }
+ return ret;
+ }
+}
diff --git a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataPropertyAttribute.cs b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataPropertyAttribute.cs
new file mode 100644
index 00000000000..f766b404f14
--- /dev/null
+++ b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataPropertyAttribute.cs
@@ -0,0 +1,46 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License
+// See the LICENSE file in the project root for more information.
+// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
+
+namespace BootstrapBlazor.Components;
+
+///
+/// Represents an attribute used to mark a field as a socket data field.
+///
+/// This attribute can be applied to fields to indicate that they are part of the data transmitted over a
+/// socket connection. It is intended for use in scenarios where socket communication requires specific fields to be
+/// identified for processing.
+[AttributeUsage(AttributeTargets.Property)]
+public class SocketDataPropertyAttribute : Attribute
+{
+ ///
+ /// 获得/设置 数据类型
+ ///
+ public Type? Type { get; set; }
+
+ ///
+ /// 获得/设置 数据偏移量
+ ///
+ public int Offset { get; set; }
+
+ ///
+ /// 获得/设置 数据长度
+ ///
+ public int Length { get; set; }
+
+ ///
+ /// 获得/设置 数据编码名称
+ ///
+ public string? EncodingName { get; set; }
+
+ ///
+ /// 获得/设置 数据转换器类型
+ ///
+ public Type? ConverterType { get; set; }
+
+ ///
+ /// 获得/设置 数据转换器构造函数参数
+ ///
+ public object?[]? ConverterParameters { get; set; }
+}
diff --git a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataSingleBigEndianConverter.cs b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataSingleBigEndianConverter.cs
new file mode 100644
index 00000000000..233040aa894
--- /dev/null
+++ b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataSingleBigEndianConverter.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License
+// See the LICENSE file in the project root for more information.
+// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
+
+using System.Buffers.Binary;
+
+namespace BootstrapBlazor.Components;
+
+///
+/// Sokcet 数据转换为 float 数据大端转换器
+///
+public class SocketDataSingleBigEndianConverter : ISocketDataPropertyConverter
+{
+ ///
+ ///
+ ///
+ ///
+ public object? Convert(ReadOnlyMemory data)
+ {
+ float ret = 0;
+ Span paddedSpan = stackalloc byte[4];
+ data.Span.CopyTo(paddedSpan[(4 - data.Length)..]);
+ if (BinaryPrimitives.TryReadSingleBigEndian(paddedSpan, out var v))
+ {
+ ret = v;
+ }
+ return ret;
+ }
+}
diff --git a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataSingleLittleEndianConverter.cs b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataSingleLittleEndianConverter.cs
new file mode 100644
index 00000000000..7858af38904
--- /dev/null
+++ b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataSingleLittleEndianConverter.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License
+// See the LICENSE file in the project root for more information.
+// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
+
+using System.Buffers.Binary;
+
+namespace BootstrapBlazor.Components;
+
+///
+/// Sokcet 数据转换为 float 数据小端转换器
+///
+public class SocketDataSingleLittleEndianConverter : ISocketDataPropertyConverter
+{
+ ///
+ ///
+ ///
+ ///
+ public object? Convert(ReadOnlyMemory data)
+ {
+ float ret = 0;
+ Span paddedSpan = stackalloc byte[4];
+ data.Span.CopyTo(paddedSpan[(4 - data.Length)..]);
+ if (BinaryPrimitives.TryReadSingleLittleEndian(paddedSpan, out var v))
+ {
+ ret = v;
+ }
+ return ret;
+ }
+}
diff --git a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataStringConverter.cs b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataStringConverter.cs
new file mode 100644
index 00000000000..9b87b5577e0
--- /dev/null
+++ b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataStringConverter.cs
@@ -0,0 +1,24 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License
+// See the LICENSE file in the project root for more information.
+// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
+
+using System.Text;
+
+namespace BootstrapBlazor.Components;
+
+///
+/// Sokcet 数据转换为 string 数据转换器
+///
+public class SocketDataStringConverter(string? encodingName) : ISocketDataPropertyConverter
+{
+ ///
+ ///
+ ///
+ ///
+ public object? Convert(ReadOnlyMemory data)
+ {
+ var encoding = string.IsNullOrEmpty(encodingName) ? Encoding.UTF8 : Encoding.GetEncoding(encodingName);
+ return encoding.GetString(data.Span);
+ }
+}
diff --git a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt16BigEndianConverter.cs b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt16BigEndianConverter.cs
new file mode 100644
index 00000000000..cf5c7c793c7
--- /dev/null
+++ b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt16BigEndianConverter.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License
+// See the LICENSE file in the project root for more information.
+// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
+
+using System.Buffers.Binary;
+
+namespace BootstrapBlazor.Components;
+
+///
+/// Sokcet 数据转换为 ushort 数据大端转换器
+///
+public class SocketDataUInt16BigEndianConverter : ISocketDataPropertyConverter
+{
+ ///
+ ///
+ ///
+ ///
+ public object? Convert(ReadOnlyMemory data)
+ {
+ ushort ret = 0;
+ Span paddedSpan = stackalloc byte[2];
+ data.Span.CopyTo(paddedSpan[(2 - data.Length)..]);
+ if (BinaryPrimitives.TryReadUInt16BigEndian(paddedSpan, out var v))
+ {
+ ret = v;
+ }
+ return ret;
+ }
+}
diff --git a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt16LittleEndianConverter.cs b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt16LittleEndianConverter.cs
new file mode 100644
index 00000000000..288e83db658
--- /dev/null
+++ b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt16LittleEndianConverter.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License
+// See the LICENSE file in the project root for more information.
+// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
+
+using System.Buffers.Binary;
+
+namespace BootstrapBlazor.Components;
+
+///
+/// Sokcet 数据转换为 ushort 数据小端转换器
+///
+public class SocketDataUInt16LittleEndianConverter : ISocketDataPropertyConverter
+{
+ ///
+ ///
+ ///
+ ///
+ public object? Convert(ReadOnlyMemory data)
+ {
+ ushort ret = 0;
+ Span paddedSpan = stackalloc byte[2];
+ data.Span.CopyTo(paddedSpan[(2 - data.Length)..]);
+ if (BinaryPrimitives.TryReadUInt16LittleEndian(paddedSpan, out var v))
+ {
+ ret = v;
+ }
+ return ret;
+ }
+}
diff --git a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt32BigEndianConverter.cs b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt32BigEndianConverter.cs
new file mode 100644
index 00000000000..c731501fad6
--- /dev/null
+++ b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt32BigEndianConverter.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License
+// See the LICENSE file in the project root for more information.
+// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
+
+using System.Buffers.Binary;
+
+namespace BootstrapBlazor.Components;
+
+///
+/// Sokcet 数据转换为 uint 数据大端转换器
+///
+public class SocketDataUInt32BigEndianConverter : ISocketDataPropertyConverter
+{
+ ///
+ ///
+ ///
+ ///
+ public object? Convert(ReadOnlyMemory data)
+ {
+ uint ret = 0;
+ Span paddedSpan = stackalloc byte[4];
+ data.Span.CopyTo(paddedSpan[(4 - data.Length)..]);
+ if (BinaryPrimitives.TryReadUInt32BigEndian(paddedSpan, out var v))
+ {
+ ret = v;
+ }
+ return ret;
+ }
+}
diff --git a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt32LittleEndianConverter.cs b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt32LittleEndianConverter.cs
new file mode 100644
index 00000000000..509edb7a02b
--- /dev/null
+++ b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt32LittleEndianConverter.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License
+// See the LICENSE file in the project root for more information.
+// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
+
+using System.Buffers.Binary;
+
+namespace BootstrapBlazor.Components;
+
+///
+/// Sokcet 数据转换为 uint 数据小端转换器
+///
+public class SocketDataUInt32LittleEndianConverter : ISocketDataPropertyConverter
+{
+ ///
+ ///
+ ///
+ ///
+ public object? Convert(ReadOnlyMemory data)
+ {
+ uint ret = 0;
+ Span paddedSpan = stackalloc byte[4];
+ data.Span.CopyTo(paddedSpan[(4 - data.Length)..]);
+ if (BinaryPrimitives.TryReadUInt32LittleEndian(paddedSpan, out var v))
+ {
+ ret = v;
+ }
+ return ret;
+ }
+}
diff --git a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt64BigEndianConverter.cs b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt64BigEndianConverter.cs
new file mode 100644
index 00000000000..1003b1d3d19
--- /dev/null
+++ b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt64BigEndianConverter.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License
+// See the LICENSE file in the project root for more information.
+// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
+
+using System.Buffers.Binary;
+
+namespace BootstrapBlazor.Components;
+
+///
+/// Sokcet 数据转换为 ulong 数据大端转换器
+///
+public class SocketDataUInt64BigEndianConverter : ISocketDataPropertyConverter
+{
+ ///
+ ///
+ ///
+ ///
+ public object? Convert(ReadOnlyMemory data)
+ {
+ ulong ret = 0;
+ Span paddedSpan = stackalloc byte[8];
+ data.Span.CopyTo(paddedSpan[(8 - data.Length)..]);
+ if (BinaryPrimitives.TryReadUInt64BigEndian(paddedSpan, out var v))
+ {
+ ret = v;
+ }
+ return ret;
+ }
+}
diff --git a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt64LittleEndianConverter.cs b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt64LittleEndianConverter.cs
new file mode 100644
index 00000000000..533f4c35ef1
--- /dev/null
+++ b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt64LittleEndianConverter.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License
+// See the LICENSE file in the project root for more information.
+// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
+
+using System.Buffers.Binary;
+
+namespace BootstrapBlazor.Components;
+
+///
+/// Sokcet 数据转换为 ulong 数据小端转换器
+///
+public class SocketDataUInt64LittleEndianConverter : ISocketDataPropertyConverter
+{
+ ///
+ ///
+ ///
+ ///
+ public object? Convert(ReadOnlyMemory data)
+ {
+ ulong ret = 0;
+ Span paddedSpan = stackalloc byte[8];
+ data.Span.CopyTo(paddedSpan[(8 - data.Length)..]);
+ if (BinaryPrimitives.TryReadUInt64LittleEndian(paddedSpan, out var v))
+ {
+ ret = v;
+ }
+ return ret;
+ }
+}
diff --git a/test/UnitTest/Services/TcpSocketFactoryTest.cs b/test/UnitTest/Services/TcpSocketFactoryTest.cs
index b552daedf06..d9c977c9108 100644
--- a/test/UnitTest/Services/TcpSocketFactoryTest.cs
+++ b/test/UnitTest/Services/TcpSocketFactoryTest.cs
@@ -645,17 +645,17 @@ public async Task DelimiterDataPackageHandler_Ok()
public async Task TryConvertTo_Ok()
{
var port = 8886;
- var server = StartTcpServer(port, MockSplitPackageAsync);
+ var server = StartTcpServer(port, MockEntityPackageAsync);
var client = CreateClient();
var tcs = new TaskCompletionSource();
MockEntity? entity = null;
// 设置数据适配器
- var adapter = new MockEntityDataPackageAdapter
+ var adapter = new DataPackageAdapter
{
- DataPackageHandler = new FixLengthDataPackageHandler(7),
+ DataPackageHandler = new FixLengthDataPackageHandler(29),
};
- client.SetDataPackageAdapter(adapter, t =>
+ client.SetDataPackageAdapter(adapter, new MockEntitySocketDataConverter(), t =>
{
entity = t;
tcs.SetResult();
@@ -674,43 +674,83 @@ public async Task TryConvertTo_Ok()
Assert.Equal([1, 2, 3, 4, 5], entity.Header);
Assert.Equal([3, 4], entity.Body);
- // 测试异常流程
- var adapter2 = new DataPackageAdapter();
- var result = adapter2.TryConvertTo(data, out var t);
- Assert.False(result);
- Assert.Null(t);
- }
+ // string
+ Assert.Equal("1", entity.Value1);
- [Fact]
- public async Task TryConvertTo_Null()
- {
- var port = 8890;
- var server = StartTcpServer(port, MockSplitPackageAsync);
- var client = CreateClient();
- var tcs = new TaskCompletionSource();
- MockEntity? entity = null;
+ // string
+ Assert.Equal("1", entity.Value14);
- // 设置数据适配器
- var adapter = new MockErrorEntityDataPackageAdapter
- {
- DataPackageHandler = new FixLengthDataPackageHandler(7),
- };
+ // int
+ Assert.Equal(9, entity.Value2);
+
+ // long
+ Assert.Equal(16, entity.Value3);
+
+ // double
+ Assert.Equal(3.14, entity.Value4);
+
+ // single
+ Assert.NotEqual(0, entity.Value5);
+
+ // short
+ Assert.Equal(0x23, entity.Value6);
+
+ // ushort
+ Assert.Equal(0x24, entity.Value7);
+
+ // uint
+ Assert.Equal((uint)0x25, entity.Value8);
+
+ // ulong
+ Assert.Equal((ulong)0x26, entity.Value9);
+
+ // bool
+ Assert.True(entity.Value10);
+
+ // enum
+ Assert.Equal(EnumEducation.Middle, entity.Value11);
+
+ // foo
+ Assert.NotNull(entity.Value12);
+ Assert.Equal(0x29, entity.Value12.Id);
+ Assert.Equal("test", entity.Value12.Name);
+
+ // no attribute
+ Assert.Null(entity.Value13);
+
+ // 测试 SocketDataConverter 标签功能
+ tcs = new TaskCompletionSource();
client.SetDataPackageAdapter(adapter, t =>
{
entity = t;
tcs.SetResult();
return Task.CompletedTask;
});
+ await client.SendAsync(data);
+ await tcs.Task;
- // 连接 TCP Server
- var connect = await client.ConnectAsync("localhost", port);
+ Assert.NotNull(entity);
+ Assert.Equal([1, 2, 3, 4, 5], entity.Header);
- // 发送数据
- var data = new ReadOnlyMemory([1, 2, 3, 4, 5]);
+ // 测试数据适配器直接调用 TryConvertTo 方法转换数据
+ var adapter2 = new DataPackageAdapter();
+ var result = adapter2.TryConvertTo(data, new MockEntitySocketDataConverter(), out var t);
+ Assert.True(result);
+ Assert.NotNull(t);
+ Assert.Equal([1, 2, 3, 4, 5], entity.Header);
+
+ // 测试 SetDataPackageAdapter 泛型无标签情况
+ tcs = new TaskCompletionSource();
+ NoConvertEntity? noConvertEntity = null;
+ client.SetDataPackageAdapter(adapter, t =>
+ {
+ noConvertEntity = t;
+ tcs.SetResult();
+ return Task.CompletedTask;
+ });
await client.SendAsync(data);
await tcs.Task;
-
- Assert.Null(entity);
+ Assert.Null(noConvertEntity);
}
private static TcpListener StartTcpServer(int port, Func handler)
@@ -777,6 +817,23 @@ private static async Task MockSplitPackageAsync(TcpClient client)
}
}
+ private static async Task MockEntityPackageAsync(TcpClient client)
+ {
+ using var stream = client.GetStream();
+ while (true)
+ {
+ var buffer = new byte[1024];
+ var len = await stream.ReadAsync(buffer);
+ if (len == 0)
+ {
+ break;
+ }
+
+ // 回写数据到客户端
+ await stream.WriteAsync(new byte[] { 0x1, 0x2, 0x3, 0x4, 0x5, 0x3, 0x4, 0x31, 0x09, 0x10, 0x40, 0x09, 0x1E, 0xB8, 0x51, 0xEB, 0x85, 0x1F, 0x40, 0x49, 0x0F, 0xDB, 0x23, 0x24, 0x25, 0x26, 0x01, 0x01, 0x29 }, CancellationToken.None);
+ }
+ }
+
private static async Task MockStickyPackageAsync(TcpClient client)
{
using var stream = client.GetStream();
@@ -1080,29 +1137,77 @@ public void SetReceive(bool state)
}
}
- class MockEntityDataPackageAdapter : DataPackageAdapter
+ class MockEntitySocketDataConverter : SocketDataConverterBase
{
- public override bool TryConvertTo(ReadOnlyMemory data, [NotNullWhen(true)] out object? entity)
+ public override bool TryConvertTo(ReadOnlyMemory data, [NotNullWhen(true)] out MockEntity? entity)
{
- entity = new MockEntity
- {
- Header = data[..5].ToArray(),
- Body = data[5..].ToArray()
- };
- return true;
+ var v = new MockEntity();
+ var ret = Parse(data, v);
+ entity = ret ? v : null;
+ return ret;
}
}
- class MockErrorEntityDataPackageAdapter : DataPackageAdapter
+ [SocketDataConverter(Type = typeof(MockEntitySocketDataConverter))]
+ class MockEntity
{
- public override bool TryConvertTo(ReadOnlyMemory data, [NotNullWhen(true)] out object? entity)
+ [SocketDataProperty(Type = typeof(byte[]), Offset = 0, Length = 5)]
+ public byte[]? Header { get; set; }
+
+ [SocketDataProperty(Type = typeof(byte[]), Offset = 5, Length = 2)]
+ public byte[]? Body { get; set; }
+
+ [SocketDataProperty(Type = typeof(string), Offset = 7, Length = 1, EncodingName = "utf-8")]
+ public string? Value1 { get; set; }
+
+ [SocketDataProperty(Type = typeof(int), Offset = 8, Length = 1)]
+ public int Value2 { get; set; }
+
+ [SocketDataProperty(Type = typeof(long), Offset = 9, Length = 1)]
+ public long Value3 { get; set; }
+
+ [SocketDataProperty(Type = typeof(double), Offset = 10, Length = 8)]
+ public double Value4 { get; set; }
+
+ [SocketDataProperty(Type = typeof(float), Offset = 18, Length = 4)]
+ public float Value5 { get; set; }
+
+ [SocketDataProperty(Type = typeof(short), Offset = 22, Length = 1)]
+ public short Value6 { get; set; }
+
+ [SocketDataProperty(Type = typeof(ushort), Offset = 23, Length = 1)]
+ public ushort Value7 { get; set; }
+
+ [SocketDataProperty(Type = typeof(uint), Offset = 24, Length = 1)]
+ public uint Value8 { get; set; }
+
+ [SocketDataProperty(Type = typeof(ulong), Offset = 25, Length = 1)]
+ public ulong Value9 { get; set; }
+
+ [SocketDataProperty(Type = typeof(bool), Offset = 26, Length = 1)]
+ public bool Value10 { get; set; }
+
+ [SocketDataProperty(Type = typeof(EnumEducation), Offset = 27, Length = 1)]
+ public EnumEducation Value11 { get; set; }
+
+ [SocketDataProperty(Type = typeof(Foo), Offset = 28, Length = 1, ConverterType = typeof(FooConverter), ConverterParameters = ["test"])]
+ public Foo? Value12 { get; set; }
+
+ [SocketDataProperty(Type = typeof(string), Offset = 7, Length = 1)]
+ public string? Value14 { get; set; }
+
+ public string? Value13 { get; set; }
+ }
+
+ class FooConverter(string name) : ISocketDataPropertyConverter
+ {
+ public object? Convert(ReadOnlyMemory data)
{
- entity = new Foo();
- return true;
+ return new Foo() { Id = data.Span[0], Name = name };
}
}
- class MockEntity
+ class NoConvertEntity
{
public byte[]? Header { get; set; }
diff --git a/test/UnitTest/Services/TcpSocketPropertyConverterTest.cs b/test/UnitTest/Services/TcpSocketPropertyConverterTest.cs
new file mode 100644
index 00000000000..60a63ab4533
--- /dev/null
+++ b/test/UnitTest/Services/TcpSocketPropertyConverterTest.cs
@@ -0,0 +1,73 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the Apache 2.0 License
+// See the LICENSE file in the project root for more information.
+// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
+
+namespace UnitTest.Services;
+
+public class TcpSocketPropertyConverterTest
+{
+ [Fact]
+ public void UInt16Converter_Ok()
+ {
+ var converter = new SocketDataUInt16LittleEndianConverter();
+ var actual = converter.Convert(new byte[] { 0xFF, 0x00 });
+ Assert.Equal((ushort)0xFF, actual);
+ }
+
+ [Fact]
+ public void Int16Converter_Ok()
+ {
+ var converter = new SocketDataInt16LittleEndianConverter();
+ var actual = converter.Convert(new byte[] { 0xFF, 0x00 });
+ Assert.Equal((short)0xFF, actual);
+ }
+
+ [Fact]
+ public void UInt32Converter_Ok()
+ {
+ var converter = new SocketDataUInt32LittleEndianConverter();
+ var actual = converter.Convert(new byte[] { 0xFF, 0x00, 0x00, 0x00 });
+ Assert.Equal((uint)0xFF, actual);
+ }
+
+ [Fact]
+ public void Int32Converter_Ok()
+ {
+ var converter = new SocketDataInt32LittleEndianConverter();
+ var actual = converter.Convert(new byte[] { 0xFF, 0x00, 0x00, 0x00 });
+ Assert.Equal(0xFF, actual);
+ }
+
+ [Fact]
+ public void UInt64Converter_Ok()
+ {
+ var converter = new SocketDataUInt64LittleEndianConverter();
+ var actual = converter.Convert(new byte[] { 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 });
+ Assert.Equal((ulong)0xFF, actual);
+ }
+
+ [Fact]
+ public void Int64Converter_Ok()
+ {
+ var converter = new SocketDataInt64LittleEndianConverter();
+ var actual = converter.Convert(new byte[] { 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 });
+ Assert.Equal((long)0xFF, actual);
+ }
+
+ [Fact]
+ public void SingleConverter_Ok()
+ {
+ var converter = new SocketDataSingleLittleEndianConverter();
+ var actual = converter.Convert(new byte[] { 0xC3, 0xF5, 0x48, 0x40 });
+ Assert.Equal((float)3.14, actual);
+ }
+
+ [Fact]
+ public void DoubleConverter_Ok()
+ {
+ var converter = new SocketDataDoubleLittleEndianConverter();
+ var actual = converter.Convert(new byte[] { 0x1F, 0x85, 0xEB, 0x51, 0xB8, 0x1E, 0x09, 0x40 });
+ Assert.Equal((double)3.14, actual);
+ }
+}