From f5a1a84e87df4bcdf0cc95924577218877032742 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Wed, 23 Jul 2025 16:05:10 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E7=A7=BB=E5=8A=A8=20Socket=20?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E4=BB=A3=E7=A0=81=E5=88=B0=E6=89=A9=E5=B1=95?= =?UTF-8?q?=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...tstrapBlazorServiceCollectionExtensions.cs | 24 - .../Extensions/ITcpSocketClientExtensions.cs | 191 -------- .../DataConverter/ISocketDataConverter.cs | 32 -- .../DataConverter/SocketDataConverter.cs | 82 ---- .../SocketDataConverterCollections.cs | 102 ---- .../SocketDataPropertyConverterAttribute.cs | 46 -- .../SocketDataTypeConverterAttribute.cs | 18 - .../DataPackageAdapter/DataPackageAdapter.cs | 81 ---- .../DataPackageAdapter/IDataPackageAdapter.cs | 55 --- .../DataPackageHandlerBase.cs | 69 --- .../DelimiterDataPackageHandler.cs | 86 ---- .../FixLengthDataPackageHandler.cs | 54 --- .../DataPackageHandler/IDataPackageHandler.cs | 33 -- .../TcpSocket/DefaultSocketClientProvider.cs | 93 ---- .../TcpSocket/DefaultTcpSocketClient.cs | 441 ------------------ .../TcpSocket/DefaultTcpSocketFactory.cs | 61 --- .../SocketDataPropertyExtensions.cs | 106 ----- .../Extensions/TcpSocketExtensions.cs | 32 -- .../TcpSocket/ISocketClientProvider.cs | 73 --- .../Services/TcpSocket/ITcpSocketClient.cs | 93 ---- .../Services/TcpSocket/ITcpSocketFactory.cs | 30 -- .../ISocketDataPropertyConverter.cs | 19 - .../SocketDataBoolConverter.cs | 26 -- .../SocketDataByteArrayConverter.cs | 21 - .../SocketDataDoubleBigEndianConverter.cs | 33 -- .../SocketDataDoubleLittleEndianConverter.cs | 33 -- .../SocketDataEnumConverter.cs | 33 -- .../SocketDataInt16BigEndianConverter.cs | 33 -- .../SocketDataInt16LittleEndianConverter.cs | 33 -- .../SocketDataInt32BigEndianConverter.cs | 34 -- .../SocketDataInt32LittleEndianConverter.cs | 34 -- .../SocketDataInt64BigEndianConverter.cs | 34 -- .../SocketDataInt64LittleEndianConverter.cs | 34 -- .../SocketDataSingleBigEndianConverter.cs | 33 -- .../SocketDataSingleLittleEndianConverter.cs | 33 -- .../SocketDataStringConverter.cs | 24 - .../SocketDataUInt16BigEndianConverter.cs | 33 -- .../SocketDataUInt16LittleEndianConverter.cs | 33 -- .../SocketDataUInt32BigEndianConverter.cs | 33 -- .../SocketDataUInt32LittleEndianConverter.cs | 33 -- .../SocketDataUInt64BigEndianConverter.cs | 33 -- .../SocketDataUInt64LittleEndianConverter.cs | 33 -- .../Services/TcpSocket/SocketClientOptions.cs | 68 --- src/BootstrapBlazor/Utils/Utility.cs | 62 --- 44 files changed, 2587 deletions(-) delete mode 100644 src/BootstrapBlazor/Extensions/ITcpSocketClientExtensions.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/DataConverter/ISocketDataConverter.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataConverter.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataConverterCollections.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataPropertyConverterAttribute.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataTypeConverterAttribute.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/DataPackageAdapter/DataPackageAdapter.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/DataPackageAdapter/IDataPackageAdapter.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/DataPackageHandler/DataPackageHandlerBase.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/DataPackageHandler/DelimiterDataPackageHandler.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/DataPackageHandler/FixLengthDataPackageHandler.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/DataPackageHandler/IDataPackageHandler.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/DefaultSocketClientProvider.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/DefaultTcpSocketClient.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/DefaultTcpSocketFactory.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/Extensions/SocketDataPropertyExtensions.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/Extensions/TcpSocketExtensions.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/ISocketClientProvider.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/ITcpSocketClient.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/ITcpSocketFactory.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/ISocketDataPropertyConverter.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataBoolConverter.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataByteArrayConverter.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataDoubleBigEndianConverter.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataDoubleLittleEndianConverter.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataEnumConverter.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt16BigEndianConverter.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt16LittleEndianConverter.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt32BigEndianConverter.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt32LittleEndianConverter.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt64BigEndianConverter.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt64LittleEndianConverter.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataSingleBigEndianConverter.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataSingleLittleEndianConverter.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataStringConverter.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt16BigEndianConverter.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt16LittleEndianConverter.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt32BigEndianConverter.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt32LittleEndianConverter.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt64BigEndianConverter.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt64LittleEndianConverter.cs delete mode 100644 src/BootstrapBlazor/Services/TcpSocket/SocketClientOptions.cs diff --git a/src/BootstrapBlazor/Extensions/BootstrapBlazorServiceCollectionExtensions.cs b/src/BootstrapBlazor/Extensions/BootstrapBlazorServiceCollectionExtensions.cs index e18ce3df56f..fac50c21b7e 100644 --- a/src/BootstrapBlazor/Extensions/BootstrapBlazorServiceCollectionExtensions.cs +++ b/src/BootstrapBlazor/Extensions/BootstrapBlazorServiceCollectionExtensions.cs @@ -117,7 +117,6 @@ public static IServiceCollection AddBootstrapBlazor(this IServiceCollection serv services.AddTabItemBindOptions(); services.AddIconTheme(); - services.AddSocketDataConverters(); return services; } @@ -212,29 +211,6 @@ static IServiceCollection AddTabItemBindOptions(this IServiceCollection services return services; } - /// - /// 增加 Socket 数据转换器集合配置项服务 - /// - /// - /// - static IServiceCollection AddSocketDataConverters(this IServiceCollection services) - { - services.AddOptionsMonitor(); - return services; - } - - /// - /// 配置第三方数据模型与 数据转换器集合配置扩展方法 - /// - /// - /// - /// - public static IServiceCollection ConfigureSocketDataConverters(this IServiceCollection services, Action configureOptions) - { - services.Configure(configureOptions); - return services; - } - /// /// 配置第三方菜单路由与 Tab 标签页捆绑字典扩展方法 /// diff --git a/src/BootstrapBlazor/Extensions/ITcpSocketClientExtensions.cs b/src/BootstrapBlazor/Extensions/ITcpSocketClientExtensions.cs deleted file mode 100644 index ccd03c1d31e..00000000000 --- a/src/BootstrapBlazor/Extensions/ITcpSocketClientExtensions.cs +++ /dev/null @@ -1,191 +0,0 @@ -// 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 Microsoft.Extensions.DependencyInjection; -using System.Reflection; -using System.Runtime.Versioning; -using System.Text; - -namespace BootstrapBlazor.Components; - -/// -/// 扩展方法类 -/// -[UnsupportedOSPlatform("browser")] -public static class ITcpSocketClientExtensions -{ - /// - /// Sends the specified string content to the connected TCP socket client asynchronously. - /// - /// This method converts the provided string content into a byte array using the specified - /// encoding (or UTF-8 by default) and sends it to the connected TCP socket client. Ensure the client is connected - /// before calling this method. - /// The TCP socket client to which the content will be sent. Cannot be . - /// The string content to send. Cannot be or empty. - /// The character encoding to use for converting the string content to bytes. If , UTF-8 - /// encoding is used by default. - /// A to observe while waiting for the operation to complete. - /// A that represents the asynchronous operation. The result is if the content was sent successfully; otherwise, . - public static ValueTask SendAsync(this ITcpSocketClient client, string content, Encoding? encoding = null, CancellationToken token = default) - { - var buffer = encoding?.GetBytes(content) ?? Encoding.UTF8.GetBytes(content); - return client.SendAsync(buffer, token); - } - - /// - /// Establishes an asynchronous connection to the specified host and port. - /// - /// The TCP socket client to which the content will be sent. Cannot be . - /// The hostname or IP address of the server to connect to. Cannot be null or empty. - /// The port number on the server to connect to. Must be a valid port number between 0 and 65535. - /// An optional to cancel the connection attempt. Defaults to if not provided. - /// A task that represents the asynchronous operation. The task result is if the connection - /// is successfully established; otherwise, . - public static ValueTask ConnectAsync(this ITcpSocketClient client, string ipString, int port, CancellationToken token = default) - { - var endPoint = Utility.ConvertToIpEndPoint(ipString, port); - return client.ConnectAsync(endPoint, token); - } - - /// - /// Configures the specified to use the provided - /// for processing received data and sets a callback to handle processed data. - /// - /// This method sets up a two-way data processing pipeline: - /// The is configured to pass received data to the - /// for processing. The is configured to invoke - /// the provided with the processed data. Use this method - /// to integrate a custom data processing adapter with a TCP socket client. - /// The instance to configure. - /// The used to process incoming data. - /// A callback function invoked with the processed data. The function receives a - /// containing the processed data and returns a . - public static void SetDataPackageAdapter(this ITcpSocketClient client, IDataPackageAdapter adapter, Func, ValueTask> callback) - { - // 设置 ITcpSocketClient 的回调函数 - client.ReceivedCallBack = async buffer => - { - // 将接收到的数据传递给 DataPackageAdapter 进行数据处理合规数据触发 ReceivedCallBack 回调 - await adapter.HandlerAsync(buffer); - }; - - // 设置 DataPackageAdapter 的回调函数 - adapter.ReceivedCallBack = buffer => callback(buffer); - } - - /// - /// Configures the specified to use a data package adapter and a callback function - /// for processing received data. - /// - /// 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 => - { - // 将接收到的数据传递给 DataPackageAdapter 进行数据处理合规数据触发 ReceivedCallBack 回调 - await adapter.HandlerAsync(buffer); - }; - - // 设置 DataPackageAdapter 的回调函数 - adapter.ReceivedCallBack = async buffer => - { - TEntity? ret = default; - if (socketDataConverter.TryConvertTo(buffer, out var t)) - { - 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); - }; - - ISocketDataConverter? converter = null; - - var type = typeof(TEntity); - var converterType = type.GetCustomAttribute(); - if (converterType is { Type: not null }) - { - // 如果类型上有 SocketDataTypeConverterAttribute 特性则使用特性中指定的转换器 - if (Activator.CreateInstance(converterType.Type) is ISocketDataConverter socketDataConverter) - { - converter = socketDataConverter; - } - } - else - { - // 如果没有特性则从 ITcpSocketClient 中的服务容器获取转换器 - converter = client.GetSocketDataConverter(); - } - - if (converter == null) - { - // 设置正常回调 - adapter.ReceivedCallBack = async buffer => await callback(default); - } - else - { - // 设置转化器 - adapter.SetDataAdapterCallback(converter, callback); - } - } - - private static void SetDataAdapterCallback(this IDataPackageAdapter adapter, ISocketDataConverter converter, Func callback) - { - adapter.ReceivedCallBack = async buffer => - { - TEntity? ret = default; - if (converter.TryConvertTo(buffer, out var t)) - { - ret = t; - } - await callback(ret); - }; - } - - private static ISocketDataConverter? GetSocketDataConverter(this ITcpSocketClient client) - { - ISocketDataConverter? converter = null; - if (client is IServiceProvider provider) - { - var converters = provider.GetRequiredService>().Value; - if (converters.TryGetTypeConverter(out var v)) - { - converter = v; - } - } - return converter; - } -} diff --git a/src/BootstrapBlazor/Services/TcpSocket/DataConverter/ISocketDataConverter.cs b/src/BootstrapBlazor/Services/TcpSocket/DataConverter/ISocketDataConverter.cs deleted file mode 100644 index aaf31b557e7..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/DataConverter/ISocketDataConverter.cs +++ /dev/null @@ -1,32 +0,0 @@ -// 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 ISocketDataConverter -{ - -} - -/// -/// 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 : 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/SocketDataConverter.cs b/src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataConverter.cs deleted file mode 100644 index 324da0cafa2..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataConverter.cs +++ /dev/null @@ -1,82 +0,0 @@ -// 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 class SocketDataConverter(SocketDataConverterCollections converters) : ISocketDataConverter -{ - /// - /// 构造函数 - /// - public SocketDataConverter() : this(new()) - { - - } - - /// - /// - /// - /// - /// - /// - public virtual bool TryConvertTo(ReadOnlyMemory data, [NotNullWhen(true)] out TEntity? entity) - { - var v = CreateEntity(); - var ret = Parse(data, v); - entity = ret ? v : default; - return ret; - } - - /// - /// 创建实体实例方法 - /// - /// - protected virtual TEntity CreateEntity() => Activator.CreateInstance(); - - /// - /// 将字节数据转换为指定实体类型的实例。 - /// - /// - /// - protected virtual bool Parse(ReadOnlyMemory data, TEntity entity) - { - // 使用 SocketDataPropertyAttribute 特性获取数据转换规则 - var ret = false; - if (entity != null) - { - var unuseProperties = new List(32); - - // 通过 SocketDataPropertyConverterAttribute 特性获取属性转换器 - var properties = entity.GetType().GetProperties().Where(p => p.CanWrite).ToList(); - foreach (var p in properties) - { - var attr = p.GetCustomAttribute(false) - ?? GetPropertyConverterAttribute(p); - if (attr != null) - { - p.SetValue(entity, attr.ConvertTo(data)); - } - } - ret = true; - } - return ret; - } - - private SocketDataPropertyConverterAttribute? GetPropertyConverterAttribute(PropertyInfo propertyInfo) - { - SocketDataPropertyConverterAttribute? attr = null; - if (converters.TryGetPropertyConverter(propertyInfo, out var v)) - { - attr = v; - } - return attr; - } -} diff --git a/src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataConverterCollections.cs b/src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataConverterCollections.cs deleted file mode 100644 index 1651988e988..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataConverterCollections.cs +++ /dev/null @@ -1,102 +0,0 @@ -// 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.Collections.Concurrent; -using System.Linq.Expressions; -using System.Reflection; - -namespace BootstrapBlazor.Components; - -/// -/// 数据转换器集合类 -/// -public sealed class SocketDataConverterCollections -{ - readonly ConcurrentDictionary _converters = new(); - readonly ConcurrentDictionary _propertyConverters = new(); - - /// - /// 增加指定 数据类型转换器方法 - /// - /// - /// - public void AddTypeConverter(ISocketDataConverter converter) - { - var type = typeof(TEntity); - _converters.AddOrUpdate(type, t => converter, (t, v) => converter); - } - - /// - /// 增加默认数据类型转换器方法 转换器使用 - /// - /// - public void AddTypeConverter() => AddTypeConverter(new SocketDataConverter(this)); - - /// - /// 添加属性类型转化器方法 - /// - /// - /// - /// - public void AddPropertyConverter(Expression> propertyExpression, SocketDataPropertyConverterAttribute attribute) - { - if (propertyExpression.Body is MemberExpression memberExpression) - { - if (attribute.Type == null) - { - attribute.Type = memberExpression.Type; - } - _propertyConverters.AddOrUpdate(memberExpression.Member, m => attribute, (m, v) => attribute); - } - } - - /// - /// 获得指定数据类型转换器方法 - /// - /// - public bool TryGetTypeConverter([NotNullWhen(true)] out ISocketDataConverter? converter) - { - converter = null; - var ret = false; - if (_converters.TryGetValue(typeof(TEntity), out var v) && v is ISocketDataConverter c) - { - converter = c; - ret = true; - } - return ret; - } - - /// - /// 获得指定数据类型属性转换器方法 - /// - /// - public bool TryGetPropertyConverter(Expression> propertyExpression, [NotNullWhen(true)] out SocketDataPropertyConverterAttribute? converterAttribute) - { - converterAttribute = null; - var ret = false; - if (propertyExpression.Body is MemberExpression memberExpression && TryGetPropertyConverter(memberExpression.Member, out var v)) - { - converterAttribute = v; - ret = true; - } - return ret; - } - - /// - /// 获得指定数据类型属性转换器方法 - /// - /// - public bool TryGetPropertyConverter(MemberInfo memberInfo, [NotNullWhen(true)] out SocketDataPropertyConverterAttribute? converterAttribute) - { - converterAttribute = null; - var ret = false; - if (_propertyConverters.TryGetValue(memberInfo, out var v)) - { - converterAttribute = v; - ret = true; - } - return ret; - } -} diff --git a/src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataPropertyConverterAttribute.cs b/src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataPropertyConverterAttribute.cs deleted file mode 100644 index a8f171e5fd3..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataPropertyConverterAttribute.cs +++ /dev/null @@ -1,46 +0,0 @@ -// 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 | AttributeTargets.Field)] -public class SocketDataPropertyConverterAttribute : 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/DataConverter/SocketDataTypeConverterAttribute.cs b/src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataTypeConverterAttribute.cs deleted file mode 100644 index 61b33a1ed31..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/DataConverter/SocketDataTypeConverterAttribute.cs +++ /dev/null @@ -1,18 +0,0 @@ -// 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 SocketDataTypeConverterAttribute : Attribute -{ - /// - /// Gets or sets the type of the . - /// - public Type? Type { get; set; } -} diff --git a/src/BootstrapBlazor/Services/TcpSocket/DataPackageAdapter/DataPackageAdapter.cs b/src/BootstrapBlazor/Services/TcpSocket/DataPackageAdapter/DataPackageAdapter.cs deleted file mode 100644 index 77733014514..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/DataPackageAdapter/DataPackageAdapter.cs +++ /dev/null @@ -1,81 +0,0 @@ -// 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; - -/// -/// Provides a base implementation for adapting data packages between different systems or formats. -/// -/// This abstract class serves as a foundation for implementing custom data package adapters. It defines -/// common methods for sending, receiving, and handling data packages, as well as a property for accessing the -/// associated data package handler. Derived classes should override the virtual methods to provide specific behavior -/// for handling data packages. -public class DataPackageAdapter : IDataPackageAdapter -{ - /// - /// - /// - public Func, ValueTask>? ReceivedCallBack { get; set; } - - /// - /// - /// - public IDataPackageHandler? DataPackageHandler { get; set; } - - /// - /// - /// - /// - /// - /// - public virtual async ValueTask HandlerAsync(ReadOnlyMemory data, CancellationToken token = default) - { - if (DataPackageHandler != null) - { - if (DataPackageHandler.ReceivedCallBack == null) - { - DataPackageHandler.ReceivedCallBack = OnHandlerReceivedCallBack; - } - - // 如果存在数据处理器则调用其处理方法 - await DataPackageHandler.HandlerAsync(data, token); - } - } - - /// - /// - /// - /// - /// - /// - /// - public virtual bool TryConvertTo(ReadOnlyMemory data, ISocketDataConverter socketDataConverter, out TEntity? entity) - { - entity = default; - var ret = socketDataConverter.TryConvertTo(data, out var v); - if (ret) - { - entity = v; - } - return ret; - } - - /// - /// Handles incoming data by invoking a callback method, if one is defined. - /// - /// This method is designed to be overridden in derived classes to provide custom handling of - /// incoming data. If a callback method is assigned, it will be invoked asynchronously with the provided - /// data. - /// The incoming data to be processed, represented as a read-only memory block of bytes. - /// - protected virtual async ValueTask OnHandlerReceivedCallBack(ReadOnlyMemory data) - { - if (ReceivedCallBack != null) - { - // 调用接收回调方法处理数据 - await ReceivedCallBack(data); - } - } -} diff --git a/src/BootstrapBlazor/Services/TcpSocket/DataPackageAdapter/IDataPackageAdapter.cs b/src/BootstrapBlazor/Services/TcpSocket/DataPackageAdapter/IDataPackageAdapter.cs deleted file mode 100644 index e37d9272495..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/DataPackageAdapter/IDataPackageAdapter.cs +++ /dev/null @@ -1,55 +0,0 @@ -// 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 an adapter for handling and transmitting data packages to a target destination. -/// -/// This interface provides methods for sending data asynchronously and configuring a data handler. -/// Implementations of this interface are responsible for managing the interaction between the caller and the underlying -/// data transmission mechanism. -public interface IDataPackageAdapter -{ - /// - /// Gets or sets the callback function to be invoked when data is received. - /// - /// The callback function is expected to handle the received data asynchronously. Ensure that the - /// implementation of the callback does not block the calling thread and completes promptly to avoid performance - /// issues. - Func, ValueTask>? ReceivedCallBack { get; set; } - - /// - /// Gets the handler responsible for processing data packages. - /// - IDataPackageHandler? DataPackageHandler { get; } - - /// - /// Asynchronously receives data from a source and processes it. - /// - /// This method does not return any result directly. It is intended for scenarios where data is received - /// and processed asynchronously. Ensure that the parameter contains valid data before calling - /// this method. - /// A read-only memory region containing the data to be received. The caller must ensure the memory is valid and - /// populated. - /// An optional cancellation token that can be used to cancel the operation. Defaults to if - /// not provided. - /// A representing the asynchronous operation. The task completes when the data has been - /// successfully received and processed. - ValueTask HandlerAsync(ReadOnlyMemory data, CancellationToken token = default); - - /// - /// 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 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, ISocketDataConverter socketDataConverter, out TEntity? entity); -} diff --git a/src/BootstrapBlazor/Services/TcpSocket/DataPackageHandler/DataPackageHandlerBase.cs b/src/BootstrapBlazor/Services/TcpSocket/DataPackageHandler/DataPackageHandlerBase.cs deleted file mode 100644 index 41677e72a7b..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/DataPackageHandler/DataPackageHandlerBase.cs +++ /dev/null @@ -1,69 +0,0 @@ -// 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; - -/// -/// Provides a base implementation for handling data packages in a communication system. -/// -/// This abstract class defines the core contract for receiving and sending data packages. Derived -/// classes should override and extend its functionality to implement specific data handling logic. The default -/// implementation simply returns the provided data. -public abstract class DataPackageHandlerBase : IDataPackageHandler -{ - private Memory _lastReceiveBuffer = Memory.Empty; - - /// - /// - /// - public Func, ValueTask>? ReceivedCallBack { get; set; } - - /// - /// - /// - /// - /// - /// - public abstract ValueTask HandlerAsync(ReadOnlyMemory data, CancellationToken token = default); - - /// - /// Handles the processing of a sticky package by adjusting the provided buffer and length. - /// - /// This method processes the portion of the buffer beyond the specified length and updates the - /// internal state accordingly. The caller must ensure that the contains sufficient data - /// for the specified . - /// The memory buffer containing the data to process. - /// The length of the valid data within the buffer. - protected void SlicePackage(ReadOnlyMemory buffer, int length) - { - _lastReceiveBuffer = buffer[length..].ToArray().AsMemory(); - } - - /// - /// Concatenates the provided buffer with any previously stored data and returns the combined result. - /// - /// This method combines the provided buffer with any data stored in the internal buffer. After - /// concatenation, the internal buffer is cleared. The returned memory block is allocated from a shared memory pool - /// and should be used promptly to avoid holding onto pooled resources. - /// The buffer to concatenate with the previously stored data. Must not be empty. - /// A instance containing the concatenated data. If no previously stored data exists, the - /// method returns the input . - protected ReadOnlyMemory ConcatBuffer(ReadOnlyMemory buffer) - { - if (_lastReceiveBuffer.IsEmpty) - { - return buffer; - } - - // 计算缓存区长度 - Memory merged = new byte[_lastReceiveBuffer.Length + buffer.Length]; - _lastReceiveBuffer.CopyTo(merged); - buffer.CopyTo(merged[_lastReceiveBuffer.Length..]); - - // Clear the sticky buffer - _lastReceiveBuffer = Memory.Empty; - return merged; - } -} diff --git a/src/BootstrapBlazor/Services/TcpSocket/DataPackageHandler/DelimiterDataPackageHandler.cs b/src/BootstrapBlazor/Services/TcpSocket/DataPackageHandler/DelimiterDataPackageHandler.cs deleted file mode 100644 index e8923643006..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/DataPackageHandler/DelimiterDataPackageHandler.cs +++ /dev/null @@ -1,86 +0,0 @@ -// 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; -using System.Text; - -namespace BootstrapBlazor.Components; - -/// -/// Handles data packages that are delimited by a specific sequence of bytes or characters. -/// -/// This class provides functionality for processing data packages that are separated by a defined -/// delimiter. The delimiter can be specified as a string with an optional encoding or as a byte array. -public class DelimiterDataPackageHandler : DataPackageHandlerBase -{ - private readonly ReadOnlyMemory _delimiter; - - /// - /// Initializes a new instance of the class with the specified delimiter - /// and optional encoding. - /// - /// The string delimiter used to separate data packages. This value cannot be null or empty. - /// The character encoding used to convert the delimiter to bytes. If null, is used as - /// the default. - /// Thrown if is null or empty. - public DelimiterDataPackageHandler(string delimiter, Encoding? encoding = null) - { - if (string.IsNullOrEmpty(delimiter)) - { - throw new ArgumentNullException(nameof(delimiter), "Delimiter cannot be null or empty."); - } - - encoding ??= Encoding.UTF8; - _delimiter = encoding.GetBytes(delimiter); - } - - /// - /// Initializes a new instance of the class with the specified delimiters. - /// - /// An array of bytes representing the delimiters used to parse data packages. Cannot be . - /// Thrown if is . - public DelimiterDataPackageHandler(byte[] delimiter) - { - _delimiter = delimiter ?? throw new ArgumentNullException(nameof(delimiter), "Delimiter cannot be null."); - } - - /// - /// - /// - /// - /// - /// - public override async ValueTask HandlerAsync(ReadOnlyMemory data, CancellationToken token = default) - { - data = ConcatBuffer(data); - - while (data.Length > 0) - { - var index = data.Span.IndexOfAny(_delimiter.Span); - var segment = index == -1 ? data : data[..index]; - var length = segment.Length + _delimiter.Length; - using var buffer = MemoryPool.Shared.Rent(length); - segment.CopyTo(buffer.Memory); - - if (index != -1) - { - SlicePackage(data, index + _delimiter.Length); - - _delimiter.CopyTo(buffer.Memory[index..]); - if (ReceivedCallBack != null) - { - await ReceivedCallBack(buffer.Memory[..length].ToArray()); - } - - data = data[(index + _delimiter.Length)..]; - } - else - { - SlicePackage(data, 0); - break; - } - } - } -} diff --git a/src/BootstrapBlazor/Services/TcpSocket/DataPackageHandler/FixLengthDataPackageHandler.cs b/src/BootstrapBlazor/Services/TcpSocket/DataPackageHandler/FixLengthDataPackageHandler.cs deleted file mode 100644 index 40289ace4ff..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/DataPackageHandler/FixLengthDataPackageHandler.cs +++ /dev/null @@ -1,54 +0,0 @@ -// 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; - -/// -/// Handles fixed-length data packages by processing incoming data of a specified length. -/// -/// This class is designed to handle data packages with a fixed length, as specified during -/// initialization. It extends and overrides its behavior to process fixed-length -/// data. -/// The data package total data length. -public class FixLengthDataPackageHandler(int length) : DataPackageHandlerBase -{ - private readonly Memory _data = new byte[length]; - - private int _receivedLength; - - /// - /// - /// - /// - /// - /// - public override async ValueTask HandlerAsync(ReadOnlyMemory data, CancellationToken token = default) - { - while (data.Length > 0) - { - // 拷贝数据 - var len = length - _receivedLength; - var segment = data.Length > len ? data[..len] : data; - segment.CopyTo(_data[_receivedLength..]); - - // 更新数据 - data = data[segment.Length..]; - - // 更新已接收长度 - _receivedLength += segment.Length; - - // 如果已接收长度等于总长度则触发回调 - if (_receivedLength == length) - { - // 重置已接收长度 - _receivedLength = 0; - if (ReceivedCallBack != null) - { - await ReceivedCallBack(_data); - } - } - } - } -} diff --git a/src/BootstrapBlazor/Services/TcpSocket/DataPackageHandler/IDataPackageHandler.cs b/src/BootstrapBlazor/Services/TcpSocket/DataPackageHandler/IDataPackageHandler.cs deleted file mode 100644 index 25dbd85f445..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/DataPackageHandler/IDataPackageHandler.cs +++ /dev/null @@ -1,33 +0,0 @@ -// 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 an interface for adapting data packages to and from a TCP socket connection. -/// -/// Implementations of this interface are responsible for converting raw data received from a TCP socket -/// into structured data packages and vice versa. This allows for custom serialization and deserialization logic -/// tailored to specific application protocols. -public interface IDataPackageHandler -{ - /// - /// Gets or sets the callback function to be invoked when data is received asynchronously. - /// - Func, ValueTask>? ReceivedCallBack { get; set; } - - /// - /// Asynchronously receives data and processes it. - /// - /// The method is designed for asynchronous operations and may be used in scenarios where - /// efficient handling of data streams is required. Ensure that the parameter contains valid - /// data for processing, and handle potential cancellation using the . - /// The data to be received, represented as a read-only memory block of bytes. - /// A cancellation token that can be used to cancel the operation. Defaults to if not - /// provided. - /// A containing if the data was successfully received and - /// processed; otherwise, . - ValueTask HandlerAsync(ReadOnlyMemory data, CancellationToken token = default); -} diff --git a/src/BootstrapBlazor/Services/TcpSocket/DefaultSocketClientProvider.cs b/src/BootstrapBlazor/Services/TcpSocket/DefaultSocketClientProvider.cs deleted file mode 100644 index b07cca71607..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/DefaultSocketClientProvider.cs +++ /dev/null @@ -1,93 +0,0 @@ -// 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.Net; -using System.Net.Sockets; -using System.Runtime.Versioning; - -namespace BootstrapBlazor.Components; - -/// -/// TcpSocket 客户端默认实现 -/// -[UnsupportedOSPlatform("browser")] -class DefaultSocketClientProvider : ISocketClientProvider -{ - private TcpClient? _client; - - /// - /// - /// - public bool IsConnected => _client?.Connected ?? false; - - /// - /// - /// - public IPEndPoint LocalEndPoint { get; set; } = new IPEndPoint(IPAddress.Any, 0); - - /// - /// - /// - public async ValueTask ConnectAsync(IPEndPoint endPoint, CancellationToken token = default) - { - _client = new TcpClient(LocalEndPoint); - await _client.ConnectAsync(endPoint, token).ConfigureAwait(false); - if (_client.Connected) - { - if (_client.Client.LocalEndPoint is IPEndPoint localEndPoint) - { - LocalEndPoint = localEndPoint; - } - } - return _client.Connected; - } - - /// - /// - /// - public async ValueTask SendAsync(ReadOnlyMemory data, CancellationToken token = default) - { - var ret = false; - if (_client != null) - { - var stream = _client.GetStream(); - await stream.WriteAsync(data, token).ConfigureAwait(false); - ret = true; - } - return ret; - } - - /// - /// - /// - public async ValueTask ReceiveAsync(Memory buffer, CancellationToken token = default) - { - var len = 0; - if (_client is { Connected: true }) - { - var stream = _client.GetStream(); - len = await stream.ReadAsync(buffer, token).ConfigureAwait(false); - - if (len == 0) - { - _client.Close(); - } - } - return len; - } - - /// - /// - /// - public ValueTask CloseAsync() - { - if (_client != null) - { - _client.Close(); - _client = null; - } - return ValueTask.CompletedTask; - } -} diff --git a/src/BootstrapBlazor/Services/TcpSocket/DefaultTcpSocketClient.cs b/src/BootstrapBlazor/Services/TcpSocket/DefaultTcpSocketClient.cs deleted file mode 100644 index 8c84d7a538f..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/DefaultTcpSocketClient.cs +++ /dev/null @@ -1,441 +0,0 @@ -// 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 Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using System.Buffers; -using System.Net; -using System.Runtime.Versioning; - -namespace BootstrapBlazor.Components; - -[UnsupportedOSPlatform("browser")] -class DefaultTcpSocketClient(SocketClientOptions options) : IServiceProvider, ITcpSocketClient -{ - /// - /// Gets or sets the socket client provider used for managing socket connections. - /// - private ISocketClientProvider? SocketClientProvider { get; set; } - - /// - /// Gets or sets the logger instance used for logging messages and events. - /// - private ILogger? Logger { get; set; } - - /// - /// Gets or sets the service provider used to resolve dependencies. - /// - [NotNull] - public IServiceProvider? ServiceProvider { get; set; } - - /// - /// - /// - public SocketClientOptions Options => options; - - /// - /// - /// - public bool IsConnected => SocketClientProvider?.IsConnected ?? false; - - /// - /// - /// - public IPEndPoint LocalEndPoint => SocketClientProvider?.LocalEndPoint ?? new IPEndPoint(IPAddress.Any, 0); - - /// - /// - /// - public Func, ValueTask>? ReceivedCallBack { get; set; } - - /// - /// - /// - public Func? OnConnecting { get; set; } - - /// - /// - /// - public Func? OnConnected { get; set; } - - private IPEndPoint? _remoteEndPoint; - private IPEndPoint? _localEndPoint; - private CancellationTokenSource? _receiveCancellationTokenSource; - private CancellationTokenSource? _autoConnectTokenSource; - - private readonly SemaphoreSlim _semaphoreSlim = new(1, 1); - - /// - /// - /// - /// - /// - /// - public async ValueTask ConnectAsync(IPEndPoint endPoint, CancellationToken token = default) - { - if (IsConnected) - { - return true; - } - - var connectionToken = GenerateConnectionToken(token); - try - { - await _semaphoreSlim.WaitAsync(connectionToken).ConfigureAwait(false); - } - catch (OperationCanceledException) - { - // 如果信号量等待被取消,则直接返回 IsConnected - // 不管是超时还是被取消,都不需要重连,肯定有其他线程在连接中 - return IsConnected; - } - - if (IsConnected) - { - _semaphoreSlim.Release(); - return true; - } - - var reconnect = true; - var ret = false; - SocketClientProvider = ServiceProvider?.GetRequiredService() - ?? throw new InvalidOperationException("SocketClientProvider is not registered in the service provider."); - - try - { - if (OnConnecting != null) - { - await OnConnecting(); - } - ret = await ConnectCoreAsync(SocketClientProvider, endPoint, connectionToken); - if (OnConnected != null) - { - await OnConnected(); - } - } - catch (OperationCanceledException ex) - { - if (token.IsCancellationRequested) - { - Log(LogLevel.Warning, ex, $"TCP Socket connect operation was canceled from {LocalEndPoint} to {endPoint}"); - reconnect = false; - } - else - { - Log(LogLevel.Warning, ex, $"TCP Socket connect operation timed out from {LocalEndPoint} to {endPoint}"); - } - } - catch (Exception ex) - { - Log(LogLevel.Error, ex, $"TCP Socket connection failed from {LocalEndPoint} to {endPoint}"); - } - - // 释放信号量 - _semaphoreSlim.Release(); - - if (reconnect) - { - _autoConnectTokenSource = new(); - - if (!ret) - { - Reconnect(); - } - } - return ret; - } - - private void Reconnect() - { - if (_autoConnectTokenSource != null && options.IsAutoReconnect && _remoteEndPoint != null) - { - Task.Run(async () => - { - try - { - await Task.Delay(options.ReconnectInterval, _autoConnectTokenSource.Token).ConfigureAwait(false); - await ConnectAsync(_remoteEndPoint, _autoConnectTokenSource.Token).ConfigureAwait(false); - } - catch { } - }, CancellationToken.None).ConfigureAwait(false); - } - } - - private async ValueTask ConnectCoreAsync(ISocketClientProvider provider, IPEndPoint endPoint, CancellationToken token) - { - // 释放资源 - await CloseCoreAsync(); - - // 创建新的 TcpClient 实例 - provider.LocalEndPoint = Options.LocalEndPoint; - - _localEndPoint = Options.LocalEndPoint; - _remoteEndPoint = endPoint; - - var ret = await provider.ConnectAsync(endPoint, token); - - if (ret) - { - _localEndPoint = provider.LocalEndPoint; - - if (options.IsAutoReceive) - { - _ = Task.Run(AutoReceiveAsync, CancellationToken.None).ConfigureAwait(false); - } - } - return ret; - } - - private CancellationToken GenerateConnectionToken(CancellationToken token) - { - var connectionToken = token; - if (Options.ConnectTimeout > 0) - { - // 设置连接超时时间 - var connectTokenSource = new CancellationTokenSource(options.ConnectTimeout); - connectionToken = CancellationTokenSource.CreateLinkedTokenSource(token, connectTokenSource.Token).Token; - } - return connectionToken; - } - - /// - /// - /// - /// - /// - /// - public async ValueTask SendAsync(ReadOnlyMemory data, CancellationToken token = default) - { - if (SocketClientProvider is not { IsConnected: true }) - { - throw new InvalidOperationException($"TCP Socket is not connected {LocalEndPoint}"); - } - - var ret = false; - var reconnect = true; - try - { - var sendToken = token; - if (options.SendTimeout > 0) - { - // 设置发送超时时间 - var sendTokenSource = new CancellationTokenSource(options.SendTimeout); - sendToken = CancellationTokenSource.CreateLinkedTokenSource(token, sendTokenSource.Token).Token; - } - ret = await SocketClientProvider.SendAsync(data, sendToken); - } - catch (OperationCanceledException ex) - { - if (token.IsCancellationRequested) - { - reconnect = false; - Log(LogLevel.Warning, ex, $"TCP Socket send operation was canceled from {_localEndPoint} to {_remoteEndPoint}"); - } - else - { - Log(LogLevel.Warning, ex, $"TCP Socket send operation timed out from {_localEndPoint} to {_remoteEndPoint}"); - } - } - catch (Exception ex) - { - Log(LogLevel.Error, ex, $"TCP Socket send failed from {_localEndPoint} to {_remoteEndPoint}"); - } - - Log(LogLevel.Information, null, $"Sending data from {_localEndPoint} to {_remoteEndPoint}, Data Length: {data.Length} Data Content: {BitConverter.ToString(data.ToArray())} Result: {ret}"); - - if (!ret && reconnect) - { - // 如果发送失败并且需要重连则尝试重连 - Reconnect(); - } - return ret; - } - - /// - /// - /// - /// - /// - public async ValueTask> ReceiveAsync(CancellationToken token = default) - { - if (SocketClientProvider is not { IsConnected: true }) - { - throw new InvalidOperationException($"TCP Socket is not connected {LocalEndPoint}"); - } - - if (options.IsAutoReceive) - { - throw new InvalidOperationException("Cannot call ReceiveAsync when IsAutoReceive is true. Use the auto-receive mechanism instead."); - } - - using var block = MemoryPool.Shared.Rent(options.ReceiveBufferSize); - var buffer = block.Memory; - var len = await ReceiveCoreAsync(SocketClientProvider, buffer, token); - if (len == 0) - { - Reconnect(); - } - return buffer[..len]; - } - - private async ValueTask AutoReceiveAsync() - { - // 自动接收方法 - _receiveCancellationTokenSource ??= new(); - while (_receiveCancellationTokenSource is { IsCancellationRequested: false }) - { - if (SocketClientProvider is not { IsConnected: true }) - { - throw new InvalidOperationException($"TCP Socket is not connected {LocalEndPoint}"); - } - - using var block = MemoryPool.Shared.Rent(options.ReceiveBufferSize); - var buffer = block.Memory; - var len = await ReceiveCoreAsync(SocketClientProvider, buffer, _receiveCancellationTokenSource.Token); - if (len == 0) - { - // 远端关闭或者 DisposeAsync 方法被调用时退出 - break; - } - } - - Reconnect(); - } - - private async ValueTask ReceiveCoreAsync(ISocketClientProvider client, Memory buffer, CancellationToken token) - { - var reconnect = true; - var len = 0; - try - { - var receiveToken = token; - if (options.ReceiveTimeout > 0) - { - // 设置接收超时时间 - var receiveTokenSource = new CancellationTokenSource(options.ReceiveTimeout); - receiveToken = CancellationTokenSource.CreateLinkedTokenSource(receiveToken, receiveTokenSource.Token).Token; - } - - len = await client.ReceiveAsync(buffer, receiveToken); - if (len == 0) - { - // 远端主机关闭链路 - Log(LogLevel.Information, null, $"TCP Socket {_localEndPoint} received 0 data closed by {_remoteEndPoint}"); - buffer = Memory.Empty; - } - else - { - buffer = buffer[..len]; - } - - if (ReceivedCallBack != null) - { - // 如果订阅回调则触发回调 - await ReceivedCallBack(buffer); - } - } - catch (OperationCanceledException ex) - { - if (token.IsCancellationRequested) - { - Log(LogLevel.Warning, ex, $"TCP Socket receive operation canceled from {_localEndPoint} to {_remoteEndPoint}"); - reconnect = false; - } - else - { - Log(LogLevel.Warning, ex, $"TCP Socket receive operation timed out from {_localEndPoint} to {_remoteEndPoint}"); - } - } - catch (Exception ex) - { - Log(LogLevel.Error, ex, $"TCP Socket receive failed from {_localEndPoint} to {_remoteEndPoint}"); - } - - Log(LogLevel.Information, null, $"Receiving data from {_localEndPoint} to {_remoteEndPoint}, Data Length: {len} Data Content: {BitConverter.ToString(buffer.ToArray())}"); - - if (len == 0 && reconnect) - { - // 如果接收数据长度为 0 并且需要重连则尝试重连 - Reconnect(); - } - return len; - } - - /// - /// Logs a message with the specified log level, exception, and additional context. - /// - private void Log(LogLevel logLevel, Exception? ex, string? message) - { - if (options.EnableLog) - { - Logger ??= ServiceProvider?.GetRequiredService>(); - Logger?.Log(logLevel, ex, "{Message}", message); - } - } - - /// - /// - /// - public async ValueTask CloseAsync() - { - // 取消重连任务 - if (_autoConnectTokenSource != null) - { - _autoConnectTokenSource.Cancel(); - _autoConnectTokenSource.Dispose(); - _autoConnectTokenSource = null; - } - - await CloseCoreAsync(); - } - - private async ValueTask CloseCoreAsync() - { - // 取消接收数据的任务 - if (_receiveCancellationTokenSource != null) - { - _receiveCancellationTokenSource.Cancel(); - _receiveCancellationTokenSource.Dispose(); - _receiveCancellationTokenSource = null; - } - - if (SocketClientProvider != null) - { - await SocketClientProvider.CloseAsync(); - } - } - - /// - /// - /// - /// - /// - public object? GetService(Type serviceType) => ServiceProvider.GetService(serviceType); - - /// - /// Releases the resources used by the current instance of the class. - /// - /// This method is called to free both managed and unmanaged resources. If the parameter is , the method releases managed resources in addition to - /// unmanaged resources. Override this method in a derived class to provide custom cleanup logic. - /// to release both managed and unmanaged resources; to release only - /// unmanaged resources. - private async ValueTask DisposeAsync(bool disposing) - { - if (disposing) - { - await CloseAsync(); - } - } - - /// - /// - /// - public async ValueTask DisposeAsync() - { - await DisposeAsync(true); - GC.SuppressFinalize(this); - } -} diff --git a/src/BootstrapBlazor/Services/TcpSocket/DefaultTcpSocketFactory.cs b/src/BootstrapBlazor/Services/TcpSocket/DefaultTcpSocketFactory.cs deleted file mode 100644 index 55f8815c58c..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/DefaultTcpSocketFactory.cs +++ /dev/null @@ -1,61 +0,0 @@ -// 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.Collections.Concurrent; -using System.Runtime.Versioning; - -namespace BootstrapBlazor.Components; - -[UnsupportedOSPlatform("browser")] -sealed class DefaultTcpSocketFactory(IServiceProvider provider) : ITcpSocketFactory -{ - private readonly ConcurrentDictionary _pool = new(); - - public ITcpSocketClient GetOrCreate(string name, Action valueFactory) - { - return _pool.GetOrAdd(name, key => - { - var options = new SocketClientOptions(); - valueFactory(options); - var client = new DefaultTcpSocketClient(options) - { - ServiceProvider = provider, - }; - return client; - }); - } - - public ITcpSocketClient? Remove(string name) - { - ITcpSocketClient? client = null; - if (_pool.TryRemove(name, out var c)) - { - client = c; - } - return client; - } - - private async ValueTask DisposeAsync(bool disposing) - { - if (disposing) - { - // 释放托管资源 - foreach (var socket in _pool.Values) - { - await socket.DisposeAsync(); - } - _pool.Clear(); - } - } - - /// - /// - /// - public async ValueTask DisposeAsync() - { - await DisposeAsync(true); - GC.SuppressFinalize(this); - } -} diff --git a/src/BootstrapBlazor/Services/TcpSocket/Extensions/SocketDataPropertyExtensions.cs b/src/BootstrapBlazor/Services/TcpSocket/Extensions/SocketDataPropertyExtensions.cs deleted file mode 100644 index dbfe1f7f4a3..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/Extensions/SocketDataPropertyExtensions.cs +++ /dev/null @@ -1,106 +0,0 @@ -// 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 SocketDataPropertyConverterAttribute attribute) - { - return attribute.GetConverterByType() ?? attribute.GetDefaultConverter(); - } - - private static ISocketDataPropertyConverter? GetConverterByType(this SocketDataPropertyConverterAttribute 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 SocketDataPropertyConverterAttribute 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 SocketDataPropertyConverterAttribute 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/Extensions/TcpSocketExtensions.cs b/src/BootstrapBlazor/Services/TcpSocket/Extensions/TcpSocketExtensions.cs deleted file mode 100644 index dde3bc7aa8f..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/Extensions/TcpSocketExtensions.cs +++ /dev/null @@ -1,32 +0,0 @@ -// 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 Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using System.Runtime.Versioning; - -namespace BootstrapBlazor.Components; - -/// -/// TcpSocket 扩展方法 -/// -[UnsupportedOSPlatform("browser")] -public static class TcpSocketExtensions -{ - /// - /// 增加 ITcpSocketFactory 服务 - /// - /// - /// - public static IServiceCollection AddBootstrapBlazorTcpSocketFactory(this IServiceCollection services) - { - // 添加 ITcpSocketFactory 服务 - services.AddSingleton(); - - // 增加 ISocketClientProvider 服务 - services.TryAddTransient(); - return services; - } -} diff --git a/src/BootstrapBlazor/Services/TcpSocket/ISocketClientProvider.cs b/src/BootstrapBlazor/Services/TcpSocket/ISocketClientProvider.cs deleted file mode 100644 index 6c3bd9e862b..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/ISocketClientProvider.cs +++ /dev/null @@ -1,73 +0,0 @@ -// 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.Net; - -namespace BootstrapBlazor.Components; - -/// -/// Defines the contract for a socket client that provides asynchronous methods for connecting, sending, receiving, and -/// closing network connections. -/// -/// This interface is designed to facilitate network communication using sockets. It provides methods for -/// establishing connections, transmitting data, and receiving data asynchronously. Implementations of this interface -/// should ensure proper resource management, including closing connections and releasing resources when no longer -/// needed. -public interface ISocketClientProvider -{ - /// - /// Gets a value indicating whether the connection is currently active. - /// - bool IsConnected { get; } - - /// - /// Gets the local network endpoint that the socket is bound to. - /// - IPEndPoint LocalEndPoint { get; set; } - - /// - /// Establishes an asynchronous connection to the specified endpoint. - /// - /// This method attempts to establish a connection to the specified endpoint. If the connection - /// fails, the method returns rather than throwing an exception. Ensure the endpoint is - /// valid and reachable before calling this method. - /// The representing the remote endpoint to connect to. - /// An optional to observe while waiting for the connection to complete. - /// A that represents the asynchronous operation. The result is if the connection was successfully established; otherwise, . - ValueTask ConnectAsync(IPEndPoint endPoint, CancellationToken token = default); - - /// - /// Sends the specified data asynchronously to the connected endpoint. - /// - /// This method performs a non-blocking operation to send data. If the operation is canceled via - /// the , the returned task will not complete successfully. Ensure the connected endpoint - /// is ready to receive data before calling this method. - /// The data to send, represented as a read-only memory block of bytes. - /// An optional cancellation token that can be used to cancel the operation. - /// A representing the asynchronous operation. The result is if the data was sent successfully; otherwise, . - ValueTask SendAsync(ReadOnlyMemory data, CancellationToken token = default); - - /// - /// Asynchronously receives data from a source and writes it into the specified buffer. - /// - /// This method does not guarantee that the buffer will be completely filled. The caller should - /// check the return value to determine the number of bytes received. - /// The memory buffer where the received data will be stored. Must be large enough to hold the incoming data. - /// A cancellation token that can be used to cancel the operation. Defaults to if not - /// provided. - /// A representing the asynchronous operation. The result is the number of bytes - /// successfully received and written into the buffer. Returns 0 if the end of the data stream is reached. - ValueTask ReceiveAsync(Memory buffer, CancellationToken token = default); - - /// - /// Closes the current connection or resource, releasing any associated resources. - /// - /// Once the connection or resource is closed, it cannot be reopened. Ensure that all necessary - /// operations are completed before calling this method. This method is typically used to clean up resources when - /// they are no longer needed. - ValueTask CloseAsync(); -} diff --git a/src/BootstrapBlazor/Services/TcpSocket/ITcpSocketClient.cs b/src/BootstrapBlazor/Services/TcpSocket/ITcpSocketClient.cs deleted file mode 100644 index ae965fb1a63..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/ITcpSocketClient.cs +++ /dev/null @@ -1,93 +0,0 @@ -// 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.Net; - -namespace BootstrapBlazor.Components; - -/// -/// Represents a TCP socket for network communication. -/// -public interface ITcpSocketClient : IAsyncDisposable -{ - /// - /// Gets a value indicating whether the system is currently connected. Default is false. - /// - bool IsConnected { get; } - - /// - /// Gets or sets the configuration options for the socket client. - /// - SocketClientOptions Options { get; } - - /// - /// Gets the local network endpoint that the socket is bound to. - /// - /// This property provides information about the local endpoint of the socket, which is typically - /// used to identify the local address and port being used for communication. If the socket is not bound to a - /// specific local endpoint, this property may return . - IPEndPoint LocalEndPoint { get; } - - /// - /// Gets or sets the callback function to handle received data. - /// - /// The callback function should be designed to handle the received data efficiently and - /// asynchronously. Ensure that the implementation does not block or perform long-running operations, as this may - /// impact performance. - Func, ValueTask>? ReceivedCallBack { get; set; } - - /// - /// Gets or sets the callback function that is invoked when a connection attempt is initiated. - /// - Func? OnConnecting { get; set; } - - /// - /// Gets or sets the delegate to be invoked when a connection is successfully established. - /// - Func? OnConnected { get; set; } - - /// - /// Establishes an asynchronous connection to the specified endpoint. - /// - /// This method attempts to establish a connection to the specified endpoint. If the connection - /// cannot be established, the method returns rather than throwing an exception. - /// The representing the remote endpoint to connect to. Cannot be null. - /// A that can be used to cancel the connection attempt. Defaults to if not provided. - /// A task that represents the asynchronous operation. The task result is if the connection - /// is successfully established; otherwise, . - ValueTask ConnectAsync(IPEndPoint endPoint, CancellationToken token = default); - - /// - /// Sends the specified data asynchronously to the target endpoint. - /// - /// This method performs a non-blocking operation to send data. If the operation is canceled via - /// the , the task will complete with a canceled state. Ensure the connection is properly - /// initialized before calling this method. - /// The byte array containing the data to be sent. Cannot be null or empty. - /// An optional to observe while waiting for the operation to complete. - /// A task that represents the asynchronous operation. The task result is if the data was - /// sent successfully; otherwise, . - ValueTask SendAsync(ReadOnlyMemory data, CancellationToken token = default); - - /// - /// Asynchronously receives a block of data from the underlying source. - /// - /// This method is non-blocking and completes when data is available or the operation is - /// canceled. If the operation is canceled, the returned task will be in a faulted state with a . - /// A cancellation token that can be used to cancel the operation. The default value is . - /// A containing a of bytes representing the received data. - /// The returned memory may be empty if no data is available. - ValueTask> ReceiveAsync(CancellationToken token = default); - - /// - /// Closes the current connection or resource, releasing any associated resources. - /// - /// Once the connection or resource is closed, it cannot be reopened. Ensure that all necessary - /// operations are completed before calling this method. This method is typically used to clean up resources when - /// they are no longer needed. - ValueTask CloseAsync(); -} diff --git a/src/BootstrapBlazor/Services/TcpSocket/ITcpSocketFactory.cs b/src/BootstrapBlazor/Services/TcpSocket/ITcpSocketFactory.cs deleted file mode 100644 index 4384477477c..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/ITcpSocketFactory.cs +++ /dev/null @@ -1,30 +0,0 @@ -// 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; - -/// -/// ITcpSocketFactory Interface -/// -public interface ITcpSocketFactory : IAsyncDisposable -{ - /// - /// Retrieves an existing TCP socket client by name or creates a new one using the specified configuration. - /// - /// The unique name of the TCP socket client to retrieve or create. Cannot be null or empty. - /// A delegate used to configure the for the new TCP socket client if it does not - /// already exist. This delegate is invoked only when a new client is created. - /// An instance of corresponding to the specified name. If the client already exists, - /// the existing instance is returned; otherwise, a new instance is created and returned. - ITcpSocketClient GetOrCreate(string name, Action valueFactory); - - /// - /// Removes the TCP socket client associated with the specified name. - /// - /// The name of the TCP socket client to remove. Cannot be or empty. - /// The removed instance if a client with the specified name exists; otherwise, . - ITcpSocketClient? Remove(string name); -} diff --git a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/ISocketDataPropertyConverter.cs b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/ISocketDataPropertyConverter.cs deleted file mode 100644 index b49b0a2a383..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/ISocketDataPropertyConverter.cs +++ /dev/null @@ -1,19 +0,0 @@ -// 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 deleted file mode 100644 index de8d2162e84..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataBoolConverter.cs +++ /dev/null @@ -1,26 +0,0 @@ -// 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 deleted file mode 100644 index cea9b606969..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataByteArrayConverter.cs +++ /dev/null @@ -1,21 +0,0 @@ -// 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 deleted file mode 100644 index 2b4559d95a0..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataDoubleBigEndianConverter.cs +++ /dev/null @@ -1,33 +0,0 @@ -// 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; - if (data.Length <= 8) - { - 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 deleted file mode 100644 index 06c6b27ee9d..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataDoubleLittleEndianConverter.cs +++ /dev/null @@ -1,33 +0,0 @@ -// 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; - if (data.Length <= 8) - { - 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 deleted file mode 100644 index 80aec33cc4c..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataEnumConverter.cs +++ /dev/null @@ -1,33 +0,0 @@ -// 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 deleted file mode 100644 index 94fcfeb8bab..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt16BigEndianConverter.cs +++ /dev/null @@ -1,33 +0,0 @@ -// 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; - if (data.Length <= 2) - { - 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 deleted file mode 100644 index 41862b54203..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt16LittleEndianConverter.cs +++ /dev/null @@ -1,33 +0,0 @@ -// 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; - if (data.Length <= 2) - { - 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 deleted file mode 100644 index 2f76f168de4..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt32BigEndianConverter.cs +++ /dev/null @@ -1,34 +0,0 @@ -// 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; - if (data.Length <= 4) - { - 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 deleted file mode 100644 index d833da17183..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt32LittleEndianConverter.cs +++ /dev/null @@ -1,34 +0,0 @@ -// 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; - if (data.Length <= 4) - { - 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 deleted file mode 100644 index 5dc2d22f350..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt64BigEndianConverter.cs +++ /dev/null @@ -1,34 +0,0 @@ -// 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; - if (data.Length <= 8) - { - 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 deleted file mode 100644 index dabbda71b7e..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataInt64LittleEndianConverter.cs +++ /dev/null @@ -1,34 +0,0 @@ -// 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; - if (data.Length <= 8) - { - 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/SocketDataSingleBigEndianConverter.cs b/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataSingleBigEndianConverter.cs deleted file mode 100644 index 14753c8a50a..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataSingleBigEndianConverter.cs +++ /dev/null @@ -1,33 +0,0 @@ -// 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; - if (data.Length <= 4) - { - 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 deleted file mode 100644 index 0c3132aa406..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataSingleLittleEndianConverter.cs +++ /dev/null @@ -1,33 +0,0 @@ -// 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; - if (data.Length <= 4) - { - 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 deleted file mode 100644 index 9b87b5577e0..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataStringConverter.cs +++ /dev/null @@ -1,24 +0,0 @@ -// 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 deleted file mode 100644 index 96aba7ceaf8..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt16BigEndianConverter.cs +++ /dev/null @@ -1,33 +0,0 @@ -// 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; - if (data.Length <= 2) - { - 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 deleted file mode 100644 index 4abd0b76cf1..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt16LittleEndianConverter.cs +++ /dev/null @@ -1,33 +0,0 @@ -// 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; - if (data.Length <= 2) - { - 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 deleted file mode 100644 index eb26d1b5051..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt32BigEndianConverter.cs +++ /dev/null @@ -1,33 +0,0 @@ -// 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; - if (data.Length <= 4) - { - 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 deleted file mode 100644 index 727f339ed6f..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt32LittleEndianConverter.cs +++ /dev/null @@ -1,33 +0,0 @@ -// 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; - if (data.Length <= 4) - { - 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 deleted file mode 100644 index 5e3d6ef9d94..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt64BigEndianConverter.cs +++ /dev/null @@ -1,33 +0,0 @@ -// 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; - if (data.Length <= 8) - { - 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 deleted file mode 100644 index ee976251e97..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/PropertyConverter/SocketDataUInt64LittleEndianConverter.cs +++ /dev/null @@ -1,33 +0,0 @@ -// 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; - if (data.Length <= 8) - { - 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/src/BootstrapBlazor/Services/TcpSocket/SocketClientOptions.cs b/src/BootstrapBlazor/Services/TcpSocket/SocketClientOptions.cs deleted file mode 100644 index d6d525c4fbf..00000000000 --- a/src/BootstrapBlazor/Services/TcpSocket/SocketClientOptions.cs +++ /dev/null @@ -1,68 +0,0 @@ -// 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.Net; - -namespace BootstrapBlazor.Components; - -/// -/// Represents configuration options for a socket client, including buffer sizes, timeouts, and endpoints. -/// -/// Use this class to configure various settings for a socket client, such as connection timeouts, -/// buffer sizes, and local or remote endpoints. These options allow fine-tuning of socket behavior to suit specific -/// networking scenarios. -public class SocketClientOptions -{ - /// - /// Gets or sets the size, in bytes, of the receive buffer used by the connection. - /// - public int ReceiveBufferSize { get; set; } = 1024 * 64; - - /// - /// Gets or sets a value indicating whether automatic receiving data is enabled. Default is true. - /// - public bool IsAutoReceive { get; set; } = true; - - /// - /// Gets or sets the timeout duration, in milliseconds, for establishing a connection. - /// - public int ConnectTimeout { get; set; } - - /// - /// Gets or sets the duration, in milliseconds, to wait for a send operation to complete before timing out. - /// - /// If the send operation does not complete within the specified timeout period, an exception may - /// be thrown. - public int SendTimeout { get; set; } - - /// - /// Gets or sets the amount of time, in milliseconds, that the receiver will wait for a response before timing out. - /// - /// Use this property to configure the maximum wait time for receiving a response. Setting an - /// appropriate timeout can help prevent indefinite blocking in scenarios where responses may be delayed or - /// unavailable. - public int ReceiveTimeout { get; set; } - - /// - /// Gets or sets the local endpoint for the socket client. Default value is - /// - /// This property specifies the local network endpoint that the socket client will bind to when establishing a connection. - public IPEndPoint LocalEndPoint { get; set; } = new(IPAddress.Any, 0); - - /// - /// Gets or sets a value indicating whether logging is enabled. Default value is false. - /// - public bool EnableLog { get; set; } - - /// - /// Gets or sets a value indicating whether the system should automatically attempt to reconnect after a connection is lost. Default value is false. - /// - public bool IsAutoReconnect { get; set; } - - /// - /// Gets or sets the interval, in milliseconds, between reconnection attempts. Default value is 5000. - /// - public int ReconnectInterval { get; set; } = 5000; -} diff --git a/src/BootstrapBlazor/Utils/Utility.cs b/src/BootstrapBlazor/Utils/Utility.cs index c2be34a3c4d..3cfdea287a8 100644 --- a/src/BootstrapBlazor/Utils/Utility.cs +++ b/src/BootstrapBlazor/Utils/Utility.cs @@ -8,10 +8,7 @@ using System.ComponentModel; using System.Data; using System.Linq.Expressions; -using System.Net; -using System.Net.Sockets; using System.Reflection; -using System.Runtime.Versioning; namespace BootstrapBlazor.Components; @@ -901,63 +898,4 @@ static Expression> CreateLambda(Type /// /// public static IStringLocalizer? CreateLocalizer(Type type) => CacheManager.CreateLocalizerByType(type); - - /// - /// Converts a string representation of an IP address or hostname into an object. - /// - /// This method handles common special cases for IP address strings, such as "localhost" and - /// "any". For other inputs, it attempts to parse the string as an IP address using . If parsing fails, the method resolves the input as a - /// hostname. - /// A string containing the IP address or hostname to convert. Special values include: - /// "localhost" returns the loopback address (). "any" returns the wildcard address - /// (). For other values, the method attempts to parse the - /// string as an IP address or resolve it as a hostname. - /// An object representing the parsed or resolved IP address. If the input cannot be parsed - /// or resolved, the method returns a default IP address. - [UnsupportedOSPlatform("browser")] - public static IPAddress ConvertToIPAddress(string ipString) - { - if (string.IsNullOrEmpty(ipString)) - { - throw new ArgumentNullException(nameof(ipString), "IP address cannot be null or empty."); - } - - if (ipString.Equals("localhost", StringComparison.OrdinalIgnoreCase)) - { - return IPAddress.Loopback; - } - if (ipString.Equals("any", StringComparison.OrdinalIgnoreCase)) - { - return IPAddress.Any; - } - - return IPAddress.TryParse(ipString, out var ip) ? ip : IPAddressByHostName; - } - - [ExcludeFromCodeCoverage] - - [UnsupportedOSPlatform("browser")] - private static IPAddress IPAddressByHostName => Dns.GetHostAddresses(Dns.GetHostName(), AddressFamily.InterNetwork).FirstOrDefault() ?? IPAddress.Any; - - /// - /// Converts a string representation of an IP address and a port number into an instance. - /// - /// This method is not supported on browser platforms. - /// The string representation of the IP address. Must be a valid IPv4 or IPv6 address. - /// The port number associated with the endpoint. Must be between 0 and 65535. - /// An representing the specified IP address and port. - /// Thrown if is less than 0 or greater than 65535. - [UnsupportedOSPlatform("browser")] - public static IPEndPoint ConvertToIpEndPoint(string ipString, int port) - { - if (port < 0 || port > 65535) - { - throw new ArgumentOutOfRangeException(nameof(port), "Port must be between 0 and 65535."); - } - - var address = ConvertToIPAddress(ipString); - return new IPEndPoint(address, port); - } } From 7d97b0ffc32814c0feaee4d91c41adcbd795398d Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Wed, 23 Jul 2025 16:05:25 +0800 Subject: [PATCH 2/2] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DefaultSocketClientProviderTest.cs | 110 -- .../SocketDataConverterCollectionsTest.cs | 105 -- .../UnitTest/Services/TcpSocketFactoryTest.cs | 1284 ----------------- .../TcpSocketPropertyConverterTest.cs | 73 - test/UnitTest/Utils/UtilityTest.cs | 21 - 5 files changed, 1593 deletions(-) delete mode 100644 test/UnitTest/Services/DefaultSocketClientProviderTest.cs delete mode 100644 test/UnitTest/Services/SocketDataConverterCollectionsTest.cs delete mode 100644 test/UnitTest/Services/TcpSocketFactoryTest.cs delete mode 100644 test/UnitTest/Services/TcpSocketPropertyConverterTest.cs diff --git a/test/UnitTest/Services/DefaultSocketClientProviderTest.cs b/test/UnitTest/Services/DefaultSocketClientProviderTest.cs deleted file mode 100644 index 51918355063..00000000000 --- a/test/UnitTest/Services/DefaultSocketClientProviderTest.cs +++ /dev/null @@ -1,110 +0,0 @@ -// 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 Microsoft.Extensions.Logging; -using System.Net; -using System.Net.Sockets; - -namespace UnitTest.Services; - -public class DefaultSocketClientProviderTest -{ - [Fact] - public async Task DefaultSocketClient_Ok() - { - var sc = new ServiceCollection(); - sc.AddBootstrapBlazorTcpSocketFactory(); - var provider = sc.BuildServiceProvider(); - var clientProvider = provider.GetRequiredService(); - - // 未建立连接时 IsConnected 应为 false - Assert.False(clientProvider.IsConnected); - - // 未建立连接直接调用 ReceiveAsync 方法 - var buffer = new byte[1024]; - var len = await clientProvider.ReceiveAsync(buffer); - Assert.Equal(0, len); - } - - [Fact] - public async Task ReceiveAsync_Ok() - { - var port = 8100; - // 测试接收数据时服务器断开未连接的情况 - StartTcpServer(port); - - var sc = new ServiceCollection(); - sc.AddBootstrapBlazorTcpSocketFactory(); - var provider = sc.BuildServiceProvider(); - var factory = provider.GetRequiredService(); - var client = factory.GetOrCreate("provider", op => - { - op.LocalEndPoint = Utility.ConvertToIpEndPoint("localhost", 0); - op.IsAutoReceive = false; - op.EnableLog = false; - }); - - await client.ConnectAsync("127.0.0.1", port); - Assert.True(client.IsConnected); - - var buffer = await client.ReceiveAsync(); - Assert.Equal(2, buffer.Length); - - await Task.Delay(50); - buffer = await client.ReceiveAsync(); - Assert.False(client.IsConnected); - } - - [Fact] - public void SocketClientOptions_Ok() - { - var options = new SocketClientOptions - { - ReceiveBufferSize = 1024 * 64, - IsAutoReceive = true, - ConnectTimeout = 1000, - SendTimeout = 500, - ReceiveTimeout = 500, - LocalEndPoint = new IPEndPoint(IPAddress.Loopback, 0) - }; - Assert.Equal(1024 * 64, options.ReceiveBufferSize); - Assert.True(options.IsAutoReceive); - Assert.Equal(1000, options.ConnectTimeout); - Assert.Equal(500, options.SendTimeout); - Assert.Equal(500, options.ReceiveTimeout); - Assert.Equal(new IPEndPoint(IPAddress.Loopback, 0), options.LocalEndPoint); - } - - private static TcpListener StartTcpServer(int port) - { - var server = new TcpListener(IPAddress.Loopback, port); - server.Start(); - Task.Run(() => AcceptClientsAsync(server)); - return server; - } - - private static async Task AcceptClientsAsync(TcpListener server) - { - while (true) - { - var client = await server.AcceptTcpClientAsync(); - _ = Task.Run(async () => - { - using var stream = client.GetStream(); - while (true) - { - var buffer = new byte[1024]; - - // 模拟拆包发送第二段数据 - await stream.WriteAsync(new byte[] { 0x3, 0x4 }, CancellationToken.None); - - // 等待 20ms - await Task.Delay(20); - client.Close(); - } - }); - } - } -} diff --git a/test/UnitTest/Services/SocketDataConverterCollectionsTest.cs b/test/UnitTest/Services/SocketDataConverterCollectionsTest.cs deleted file mode 100644 index db2fe233a09..00000000000 --- a/test/UnitTest/Services/SocketDataConverterCollectionsTest.cs +++ /dev/null @@ -1,105 +0,0 @@ -// 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 Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; - -namespace UnitTest.Services; - -public class SocketDataConverterCollectionsTest : BootstrapBlazorTestBase -{ - protected override void ConfigureConfiguration(IServiceCollection services) - { - base.ConfigureConfiguration(services); - - services.ConfigureSocketDataConverters(options => - { - options.AddTypeConverter(); - options.AddPropertyConverter(entity => entity.Header, new SocketDataPropertyConverterAttribute() - { - Offset = 0, - Length = 5 - }); - options.AddPropertyConverter(entity => entity.Body, new SocketDataPropertyConverterAttribute() - { - Offset = 5, - Length = 2 - }); - - // 为提高代码覆盖率 重复添加转换器以后面的为准 - options.AddTypeConverter(); - options.AddPropertyConverter(entity => entity.Header, new SocketDataPropertyConverterAttribute() - { - Offset = 0, - Length = 5 - }); - }); - } - - [Fact] - public void TryGetConverter_Ok() - { - var service = Context.Services.GetRequiredService>(); - Assert.NotNull(service.Value); - - var ret = service.Value.TryGetTypeConverter(out var converter); - Assert.True(ret); - Assert.NotNull(converter); - - var fakeConverter = service.Value.TryGetTypeConverter(out var fooConverter); - Assert.False(fakeConverter); - Assert.Null(fooConverter); - - ret = service.Value.TryGetPropertyConverter(entity => entity.Header, out var propertyConverterAttribute); - Assert.True(ret); - Assert.NotNull(propertyConverterAttribute); - Assert.True(propertyConverterAttribute is { Offset: 0, Length: 5 }); - - ret = service.Value.TryGetPropertyConverter(entity => entity.Name, out var fooPropertyConverterAttribute); - Assert.False(ret); - Assert.Null(fooPropertyConverterAttribute); - - ret = service.Value.TryGetPropertyConverter(entity => entity.ToString(), out _); - Assert.False(ret); - } - - class MockEntity - { - public byte[]? Header { get; set; } - - public byte[]? Body { get; set; } - } - - class MockLoggerProvider : ILoggerProvider - { - public ILogger CreateLogger(string categoryName) - { - return new MockLogger(); - } - - public void Dispose() - { - - } - } - - class MockLogger : ILogger - { - public IDisposable? BeginScope(TState state) where TState : notnull - { - return null; - } - - public bool IsEnabled(LogLevel logLevel) - { - return true; - } - - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) - { - - } - } -} diff --git a/test/UnitTest/Services/TcpSocketFactoryTest.cs b/test/UnitTest/Services/TcpSocketFactoryTest.cs deleted file mode 100644 index 443a0820b3c..00000000000 --- a/test/UnitTest/Services/TcpSocketFactoryTest.cs +++ /dev/null @@ -1,1284 +0,0 @@ -// 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 Microsoft.Extensions.Logging; -using System.Buffers; -using System.Net; -using System.Net.Sockets; -using System.Reflection; -using System.Text; - -namespace UnitTest.Services; - -public class TcpSocketFactoryTest -{ - [Fact] - public async Task GetOrCreate_Ok() - { - // 测试 GetOrCreate 方法创建的 Client 销毁后继续 GetOrCreate 得到的对象是否可用 - var sc = new ServiceCollection(); - sc.AddLogging(builder => - { - builder.AddProvider(new MockLoggerProvider()); - }); - sc.AddBootstrapBlazorTcpSocketFactory(); - var provider = sc.BuildServiceProvider(); - var factory = provider.GetRequiredService(); - var client1 = factory.GetOrCreate("demo", op => op.LocalEndPoint = Utility.ConvertToIpEndPoint("localhost", 0)); - await client1.CloseAsync(); - - var client2 = factory.GetOrCreate("demo", op => op.LocalEndPoint = Utility.ConvertToIpEndPoint("localhost", 0)); - Assert.Equal(client1, client2); - - var ip = Dns.GetHostAddresses(Dns.GetHostName(), AddressFamily.InterNetwork).FirstOrDefault() ?? IPAddress.Loopback; - var client3 = factory.GetOrCreate("demo1", op => op.LocalEndPoint = Utility.ConvertToIpEndPoint(ip.ToString(), 0)); - - // 测试不合格 IP 地址 - var client4 = factory.GetOrCreate("demo2", op => op.LocalEndPoint = Utility.ConvertToIpEndPoint("256.0.0.1", 0)); - - var client5 = factory.Remove("demo2"); - Assert.Equal(client4, client5); - Assert.NotNull(client5); - - await client5.DisposeAsync(); - await factory.DisposeAsync(); - } - - [Fact] - public async Task ConnectAsync_Timeout() - { - var client = CreateClient(builder => - { - // 增加发送报错 MockSocket - builder.AddTransient(); - }); - client.Options.ConnectTimeout = 10; - - var connect = await client.ConnectAsync("localhost", 9999); - Assert.False(connect); - } - - [Fact] - public async Task ConnectAsync_Cancel() - { - var client = CreateClient(builder => - { - builder.AddTransient(); - }, - options => - { - options.ConnectTimeout = 500; - }); - - // 测试 ConnectAsync 方法连接取消逻辑 - var cst = new CancellationTokenSource(); - cst.Cancel(); - var connect = await client.ConnectAsync("localhost", 9999, cst.Token); - - // 由于信号量被取消,所以连接会失败 - Assert.False(connect); - - // 测试真正的连接被取消逻辑 - cst = new CancellationTokenSource(200); - connect = await client.ConnectAsync("localhost", 9999, cst.Token); - Assert.False(connect); - } - - [Fact] - public async Task ConnectAsync_Failed() - { - var client = CreateClient(); - - // 测试 ConnectAsync 方法连接失败 - var connect = await client.ConnectAsync("localhost", 9999); - Assert.False(connect); - } - - [Fact] - public async Task ConnectAsync_Error() - { - var client = CreateClient(); - - // 反射设置 SocketClientProvider 为空 - var propertyInfo = client.GetType().GetProperty("ServiceProvider", BindingFlags.Public | BindingFlags.Instance); - Assert.NotNull(propertyInfo); - propertyInfo.SetValue(client, null); - - // 测试 ConnectAsync 方法连接失败 - var ex = await Assert.ThrowsAsync(async () => await client.ConnectAsync("localhost", 9999)); - Assert.NotNull(ex); - - // 反射测试 Log 方法 - var methodInfo = client.GetType().GetMethod("Log", BindingFlags.NonPublic | BindingFlags.Instance); - Assert.NotNull(methodInfo); - methodInfo.Invoke(client, [LogLevel.Error, null!, "Test error log"]); - } - - [Fact] - public async Task ConnectAsync_Lock() - { - // 测试并发锁问题 - var provider = new MockAutoReconnectLockSocketProvider(); - var client = CreateClient(builder => - { - builder.AddTransient(p => provider); - }); - - // 开 5 个线程同时连接 - _ = Task.Run(async () => - { - // 延时 150 保证有一个连接失败 - await Task.Delay(150); - provider.SetConnected(true); - }); - var results = await Task.WhenAll(Enumerable.Range(1, 5).Select(i => client.ConnectAsync("localhost", 0).AsTask())); - // 期望结果是 1个 false 4个 true - Assert.Equal(1, results.Count(r => !r)); - Assert.Equal(4, results.Count(r => r)); - } - - [Fact] - public async Task Send_Timeout() - { - var port = 8887; - var server = StartTcpServer(port, MockSplitPackageAsync); - - var client = CreateClient(builder => - { - // 增加发送报错 MockSocket - builder.AddTransient(); - }); - client.Options.SendTimeout = 10; - - await client.ConnectAsync("localhost", port); - - var data = new ReadOnlyMemory([1, 2, 3, 4, 5]); - var result = await client.SendAsync(data); - Assert.False(result); - } - - [Fact] - public async Task SendAsync_Error() - { - var client = CreateClient(builder => - { - // 增加发送报错 MockSocket - builder.AddTransient(); - }); - - // 测试未建立连接前调用 SendAsync 方法报异常逻辑 - var data = new ReadOnlyMemory([1, 2, 3, 4, 5]); - var ex = await Assert.ThrowsAsync(async () => await client.SendAsync(data)); - Assert.NotNull(ex); - - // 测试发送失败 - var port = 8892; - var server = StartTcpServer(port, MockSplitPackageAsync); - - await client.ConnectAsync("localhost", port); - Assert.True(client.IsConnected); - - // 内部生成异常日志 - await client.SendAsync(data); - } - - [Fact] - public async Task SendAsync_Cancel() - { - var port = 8881; - var server = StartTcpServer(port, MockSplitPackageAsync); - - var client = CreateClient(); - Assert.False(client.IsConnected); - - // 连接 TCP Server - await client.ConnectAsync("localhost", port); - Assert.True(client.IsConnected); - - // 测试 SendAsync 方法发送取消逻辑 - var cst = new CancellationTokenSource(); - cst.Cancel(); - - var result = await client.SendAsync("test", null, cst.Token); - Assert.False(result); - - result = await client.SendAsync("test", Encoding.UTF8, cst.Token); - Assert.False(result); - - // 关闭连接 - StopTcpServer(server); - } - - [Fact] - public async Task ReceiveAsync_Timeout() - { - var port = 8888; - var server = StartTcpServer(port, MockSplitPackageAsync); - - var client = CreateClient(); - client.Options.ReceiveTimeout = 100; - - await client.ConnectAsync("localhost", port); - - var data = new ReadOnlyMemory([1, 2, 3, 4, 5]); - await client.SendAsync(data); - await Task.Delay(220); // 等待接收超时 - } - - [Fact] - public async Task ReceiveAsync_Cancel() - { - var port = 8889; - var server = StartTcpServer(port, MockSplitPackageAsync); - - var client = CreateClient(); - await client.ConnectAsync("localhost", port); - - var data = new ReadOnlyMemory([1, 2, 3, 4, 5]); - await client.SendAsync(data); - - // 通过反射取消令牌 - var type = client.GetType(); - Assert.NotNull(type); - - var fieldInfo = type.GetField("_receiveCancellationTokenSource", BindingFlags.NonPublic | BindingFlags.Instance); - Assert.NotNull(fieldInfo); - var tokenSource = fieldInfo.GetValue(client) as CancellationTokenSource; - Assert.NotNull(tokenSource); - tokenSource.Cancel(); - await Task.Delay(50); - } - - [Fact] - public async Task ReceiveAsync_InvalidOperationException() - { - // 未连接时调用 ReceiveAsync 方法会抛出 InvalidOperationException 异常 - var client = CreateClient(); - var ex = await Assert.ThrowsAsync(async () => await client.ReceiveAsync()); - Assert.NotNull(ex); - - // 已连接但是启用了自动接收功能时调用 ReceiveAsync 方法会抛出 InvalidOperationException 异常 - var port = 8893; - var server = StartTcpServer(port, MockSplitPackageAsync); - - client.Options.IsAutoReceive = true; - var connected = await client.ConnectAsync("localhost", port); - Assert.True(connected); - - ex = await Assert.ThrowsAsync(async () => await client.ReceiveAsync()); - Assert.NotNull(ex); - } - - [Fact] - public async Task ReceiveAsync_Ok() - { - var onConnecting = false; - var onConnected = false; - var port = 8891; - var server = StartTcpServer(port, MockSplitPackageAsync); - - var client = CreateClient(); - client.Options.IsAutoReceive = false; - client.OnConnecting = () => - { - onConnecting = true; - return Task.CompletedTask; - }; - client.OnConnected = () => - { - onConnected = true; - return Task.CompletedTask; - }; - var connected = await client.ConnectAsync("localhost", port); - Assert.True(connected); - Assert.True(onConnecting); - Assert.True(onConnected); - - var data = new ReadOnlyMemory([1, 2, 3, 4, 5]); - var send = await client.SendAsync(data); - Assert.True(send); - - // 未设置数据处理器未开启自动接收时,调用 ReceiveAsync 方法获取数据 - // 需要自己处理粘包分包和业务问题 - var payload = await client.ReceiveAsync(); - Assert.Equal([1, 2, 3, 4, 5], payload.ToArray()); - - // 由于服务器端模拟了拆包发送第二段数据,所以这里可以再次调用 ReceiveAsync 方法获取第二段数据 - payload = await client.ReceiveAsync(); - Assert.Equal([3, 4], payload.ToArray()); - } - - [Fact] - public async Task ReceiveAsync_Error() - { - var client = CreateClient(); - - // 测试未建立连接前调用 ReceiveAsync 方法报异常逻辑 - var type = client.GetType(); - Assert.NotNull(type); - - var methodInfo = type.GetMethod("AutoReceiveAsync", BindingFlags.NonPublic | BindingFlags.Instance); - Assert.NotNull(methodInfo); - - var task = (ValueTask)methodInfo.Invoke(client, null)!; - var ex = await Assert.ThrowsAsync(async () => await task); - Assert.NotNull(ex); - - var port = 8882; - var server = StartTcpServer(port, MockSplitPackageAsync); - - Assert.Equal(1024 * 64, client.Options.ReceiveBufferSize); - - client.Options.ReceiveBufferSize = 1024 * 20; - Assert.Equal(1024 * 20, client.Options.ReceiveBufferSize); - - ReadOnlyMemory buffer = ReadOnlyMemory.Empty; - var tcs = new TaskCompletionSource(); - - // 增加接收回调方法 - client.ReceivedCallBack = b => - { - buffer = b; - tcs.SetResult(); - return ValueTask.CompletedTask; - }; - - await client.ConnectAsync("localhost", port); - - // 发送数据导致接收数据异常 - var data = new ReadOnlyMemory([1, 2, 3, 4, 5]); - await client.SendAsync(data); - - await tcs.Task; - Assert.Equal([1, 2, 3, 4, 5], buffer.ToArray()); - - // 关闭连接 - StopTcpServer(server); - } - - [Fact] - public async Task AutoReconnect_Ok() - { - var client = CreateClient(optionConfigure: options => - { - options.IsAutoReconnect = true; - options.ReconnectInterval = 200; - options.IsAutoReceive = true; - }); - - // 使用场景自动接收数据,短线后自动重连 - var port = 8894; - var connect = await client.ConnectAsync("localhost", port); - Assert.False(connect); - - // 开启服务端后,可以自动重连上 - var server = StartTcpServer(port, LoopSendPackageAsync); - await Task.Delay(250); - Assert.True(client.IsConnected); - - await client.DisposeAsync(); - } - - [Fact] - public async Task AutoReconnect_False() - { - var provider = new MockAutoReconnectSocketProvider(); - var client = CreateClient(builder => - { - // 增加发送报错 MockSocket - builder.AddTransient(p => provider); - }, - optionConfigure: options => - { - options.IsAutoReconnect = true; - options.ReconnectInterval = 200; - options.IsAutoReceive = true; - }); - - // 使用场景自动接收数据,短线后自动重连 - var connect = await client.ConnectAsync("localhost", 0); - Assert.False(connect); - - provider.SetConnected(true); - await Task.Delay(250); - Assert.True(client.IsConnected); - } - - [Fact] - public async Task AutoReconnect_Send_Ok() - { - // 发送数据时连接断开了,测试重连功能 - var provider = new MockAutoReconnectSocketProvider(); - var client = CreateClient(builder => - { - // 增加发送报错 MockSocket - builder.AddTransient(p => provider); - }, optionConfigure: options => - { - options.IsAutoReconnect = true; - options.ReconnectInterval = 200; - options.IsAutoReceive = true; - }); - - provider.SetConnected(true); - var connect = await client.ConnectAsync("localhost", 0); - Assert.True(connect); - - // 发送时断开连接 - provider.SetSend(false); - var send = await client.SendAsync("test"); - Assert.False(send); - - await Task.Delay(250); - Assert.True(client.IsConnected); - } - - [Fact] - public async Task AutoReconnect_Receive_Ok() - { - // 接收数据时连接断开了,测试重连功能 - var provider = new MockAutoReconnectSocketProvider(); - var client = CreateClient(builder => - { - // 增加发送报错 MockSocket - builder.AddTransient(p => provider); - }, optionConfigure: options => - { - options.IsAutoReconnect = true; - options.ReconnectInterval = 200; - options.IsAutoReceive = false; - }); - - provider.SetConnected(true); - var connect = await client.ConnectAsync("localhost", 0); - Assert.True(connect); - - // 发送时断开连接 - provider.SetReceive(false); - var buffer = await client.ReceiveAsync(); - Assert.Equal(Memory.Empty, buffer); - - await Task.Delay(250); - provider.SetReceive(true); - buffer = await client.ReceiveAsync(); - Assert.Equal(5, buffer.Length); - } - - [Fact] - public async Task AutoReconnect_Cancel() - { - // 测试重连时取消逻辑 - var provider = new MockAutoReconnectSocketProvider(); - var client = CreateClient(builder => - { - // 增加发送报错 MockSocket - builder.AddTransient(p => provider); - }, optionConfigure: options => - { - options.IsAutoReconnect = true; - options.ReconnectInterval = 2000; - options.IsAutoReceive = false; - }); - - await client.ConnectAsync("localhost", 0); - await Task.Delay(100); - await client.DisposeAsync(); - } - - [Fact] - public async Task FixLengthDataPackageHandler_Ok() - { - var port = 8884; - var server = StartTcpServer(port, MockSplitPackageAsync); - var client = CreateClient(); - var tcs = new TaskCompletionSource(); - var receivedBuffer = new byte[1024]; - - // 设置数据适配器 - var adapter = new DataPackageAdapter - { - DataPackageHandler = new FixLengthDataPackageHandler(7) - }; - client.SetDataPackageAdapter(adapter, buffer => - { - // buffer 即是接收到的数据 - buffer.CopyTo(receivedBuffer); - receivedBuffer = receivedBuffer[..buffer.Length]; - tcs.SetResult(); - return ValueTask.CompletedTask; - }); - - // 测试 ConnectAsync 方法 - var connect = await client.ConnectAsync("localhost", port); - Assert.True(connect); - Assert.True(client.IsConnected); - - // 测试 SendAsync 方法 - var data = new ReadOnlyMemory([1, 2, 3, 4, 5]); - var result = await client.SendAsync(data); - Assert.True(result); - - await tcs.Task; - Assert.Equal([1, 2, 3, 4, 5, 3, 4], receivedBuffer.ToArray()); - - // 关闭连接 - await client.CloseAsync(); - StopTcpServer(server); - } - - [Fact] - public async Task FixLengthDataPackageHandler_Sticky() - { - var port = 8885; - var server = StartTcpServer(port, MockStickyPackageAsync); - var client = CreateClient(); - var tcs = new TaskCompletionSource(); - var receivedBuffer = new byte[128]; - - // 连接 TCP Server - var connect = await client.ConnectAsync("localhost", port); - - // 设置数据适配器 - var adapter = new DataPackageAdapter - { - DataPackageHandler = new FixLengthDataPackageHandler(7) - }; - - client.SetDataPackageAdapter(adapter, buffer => - { - // buffer 即是接收到的数据 - buffer.CopyTo(receivedBuffer); - receivedBuffer = receivedBuffer[..buffer.Length]; - tcs.SetResult(); - return ValueTask.CompletedTask; - }); - - // 发送数据 - var data = new ReadOnlyMemory([1, 2, 3, 4, 5]); - await client.SendAsync(data); - - // 等待接收数据处理完成 - await tcs.Task; - - // 验证接收到的数据 - Assert.Equal([1, 2, 3, 4, 5, 3, 4], receivedBuffer.ToArray()); - - // 重置接收缓冲区 - receivedBuffer = new byte[1024]; - tcs = new TaskCompletionSource(); - - // 等待第二次数据 - await tcs.Task; - - // 验证第二次收到的数据 - Assert.Equal([2, 2, 3, 4, 5, 6, 7], receivedBuffer.ToArray()); - tcs = new TaskCompletionSource(); - await tcs.Task; - - // 验证第三次收到的数据 - Assert.Equal([3, 2, 3, 4, 5, 6, 7], receivedBuffer.ToArray()); - - // 关闭连接 - await client.CloseAsync(); - StopTcpServer(server); - } - - [Fact] - public async Task DelimiterDataPackageHandler_Ok() - { - var port = 8883; - var server = StartTcpServer(port, MockDelimiterPackageAsync); - var client = CreateClient(); - var tcs = new TaskCompletionSource(); - var receivedBuffer = new byte[128]; - - // 设置数据适配器 - var adapter = new DataPackageAdapter - { - DataPackageHandler = new DelimiterDataPackageHandler([13, 10]), - }; - client.SetDataPackageAdapter(adapter, buffer => - { - // buffer 即是接收到的数据 - buffer.CopyTo(receivedBuffer); - receivedBuffer = receivedBuffer[..buffer.Length]; - tcs.SetResult(); - return ValueTask.CompletedTask; - }); - - // 连接 TCP Server - var connect = await client.ConnectAsync("localhost", port); - - // 发送数据 - var data = new ReadOnlyMemory([1, 2, 3, 4, 5]); - await client.SendAsync(data); - - // 等待接收数据处理完成 - await tcs.Task; - - // 验证接收到的数据 - Assert.Equal([1, 2, 3, 4, 5, 13, 10], receivedBuffer.ToArray()); - - // 等待第二次数据 - receivedBuffer = new byte[1024]; - tcs = new TaskCompletionSource(); - await tcs.Task; - - // 验证接收到的数据 - Assert.Equal([5, 6, 13, 10], receivedBuffer.ToArray()); - - // 关闭连接 - await client.CloseAsync(); - StopTcpServer(server); - - var handler = new DelimiterDataPackageHandler("\r\n"); - var ex = Assert.Throws(() => new DelimiterDataPackageHandler(string.Empty)); - Assert.NotNull(ex); - - ex = Assert.Throws(() => new DelimiterDataPackageHandler(null!)); - Assert.NotNull(ex); - } - - [Fact] - public async Task TryConvertTo_Ok() - { - var port = 8886; - var server = StartTcpServer(port, MockEntityPackageAsync); - var client = CreateClient(); - var tcs = new TaskCompletionSource(); - MockEntity? entity = null; - - // 设置数据适配器 - var adapter = new DataPackageAdapter - { - DataPackageHandler = new FixLengthDataPackageHandler(29), - }; - client.SetDataPackageAdapter(adapter, new SocketDataConverter(), t => - { - entity = t; - tcs.SetResult(); - return Task.CompletedTask; - }); - - // 连接 TCP Server - var connect = await client.ConnectAsync("localhost", port); - - // 发送数据 - var data = new ReadOnlyMemory([1, 2, 3, 4, 5]); - await client.SendAsync(data); - await tcs.Task; - - Assert.NotNull(entity); - Assert.Equal([1, 2, 3, 4, 5], entity.Header); - Assert.Equal([3, 4], entity.Body); - - // string - Assert.Equal("1", entity.Value1); - - // string - Assert.Equal("1", entity.Value14); - - // 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; - - Assert.NotNull(entity); - Assert.Equal([1, 2, 3, 4, 5], entity.Header); - - // 测试数据适配器直接调用 TryConvertTo 方法转换数据 - var adapter2 = new DataPackageAdapter(); - var result = adapter2.TryConvertTo(data, new SocketDataConverter(), 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(noConvertEntity); - - var converter = new MockSocketDataConverter(); - result = converter.TryConvertTo(new byte[] { 0x1, 0x2 }, out t); - Assert.False(result); - - server.Stop(); - } - - [Fact] - public async Task TryGetTypeConverter_Ok() - { - // 测试服务配置转换器 - var port = 8895; - var server = StartTcpServer(port, MockSplitPackageAsync); - - var client = CreateClient(builder => - { - builder.ConfigureSocketDataConverters(options => - { - options.AddTypeConverter(); - options.AddPropertyConverter(entity => entity.Header, new SocketDataPropertyConverterAttribute() - { - Offset = 0, - Length = 5 - }); - options.AddPropertyConverter(entity => entity.Body, new SocketDataPropertyConverterAttribute() - { - Offset = 5, - Length = 2 - }); - }); - }); - var tcs = new TaskCompletionSource(); - var receivedBuffer = new byte[128]; - - // 连接 TCP Server - var connect = await client.ConnectAsync("localhost", port); - - // 设置数据适配器 - var adapter = new DataPackageAdapter - { - DataPackageHandler = new FixLengthDataPackageHandler(7) - }; - - OptionConvertEntity? entity = null; - client.SetDataPackageAdapter(adapter, data => - { - // buffer 即是接收到的数据 - entity = data; - tcs.SetResult(); - return Task.CompletedTask; - }); - - // 发送数据 - var data = new ReadOnlyMemory([1, 2, 3, 4, 5]); - await client.SendAsync(data); - - // 等待接收数据处理完成 - await tcs.Task; - Assert.NotNull(entity); - Assert.Equal([1, 2, 3, 4, 5], entity.Header); - Assert.Equal([3, 4], entity.Body); - - server.Stop(); - } - - private static TcpListener StartTcpServer(int port, Func handler) - { - var server = new TcpListener(IPAddress.Loopback, port); - server.Start(); - Task.Run(() => AcceptClientsAsync(server, handler)); - return server; - } - - private static async Task AcceptClientsAsync(TcpListener server, Func handler) - { - while (true) - { - var client = await server.AcceptTcpClientAsync(); - _ = Task.Run(() => handler(client)); - } - } - - private static async Task MockDelimiterPackageAsync(TcpClient client) - { - using var stream = client.GetStream(); - while (true) - { - var buffer = new byte[10240]; - var len = await stream.ReadAsync(buffer); - if (len == 0) - { - break; - } - - // 回写数据到客户端 - var block = new ReadOnlyMemory(buffer, 0, len); - await stream.WriteAsync(block, CancellationToken.None); - - await Task.Delay(20); - - // 模拟拆包发送第二段数据 - await stream.WriteAsync(new byte[] { 13, 10, 0x5, 0x6, 13, 10 }, CancellationToken.None); - } - } - - private static async Task MockSplitPackageAsync(TcpClient client) - { - using var stream = client.GetStream(); - while (true) - { - var buffer = new byte[1024]; - var len = await stream.ReadAsync(buffer); - if (len == 0) - { - break; - } - - // 回写数据到客户端 - var block = new ReadOnlyMemory(buffer, 0, len); - await stream.WriteAsync(block, CancellationToken.None); - - // 模拟延时 - await Task.Delay(50); - - // 模拟拆包发送第二段数据 - await stream.WriteAsync(new byte[] { 0x3, 0x4 }, CancellationToken.None); - } - } - - 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(); - while (true) - { - var buffer = new byte[10240]; - var len = await stream.ReadAsync(buffer); - if (len == 0) - { - break; - } - - // 回写数据到客户端 - var block = new ReadOnlyMemory(buffer, 0, len); - await stream.WriteAsync(block, CancellationToken.None); - - // 模拟延时 - await Task.Delay(10); - - // 模拟拆包发送第二段数据 - await stream.WriteAsync(new byte[] { 0x3, 0x4, 0x2, 0x2 }, CancellationToken.None); - - // 模拟延时 - await Task.Delay(10); - - // 模拟粘包发送后续数据 - await stream.WriteAsync(new byte[] { 0x3, 0x4, 0x5, 0x6, 0x7, 0x3, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x1 }, CancellationToken.None); - } - } - - private static async Task LoopSendPackageAsync(TcpClient client) - { - using var stream = client.GetStream(); - while (true) - { - // 模拟发送数据 - var data = new byte[] { 1, 2, 3, 4, 5 }; - await stream.WriteAsync(data, CancellationToken.None); - // 模拟延时 - await Task.Delay(500); - } - } - - private static void StopTcpServer(TcpListener server) - { - server?.Stop(); - } - - private static ITcpSocketClient CreateClient(Action? builder = null, Action? optionConfigure = null) - { - var sc = new ServiceCollection(); - sc.AddLogging(builder => - { - builder.AddProvider(new MockLoggerProvider()); - }); - sc.AddBootstrapBlazorTcpSocketFactory(); - builder?.Invoke(sc); - - var provider = sc.BuildServiceProvider(); - var factory = provider.GetRequiredService(); - var client = factory.GetOrCreate("test", op => - { - op.LocalEndPoint = Utility.ConvertToIpEndPoint("localhost", 0); - op.EnableLog = true; - optionConfigure?.Invoke(op); - }); - return client; - } - - class MockLoggerProvider : ILoggerProvider - { - public ILogger CreateLogger(string categoryName) - { - return new MockLogger(); - } - - public void Dispose() - { - - } - } - - class MockLogger : ILogger - { - public IDisposable? BeginScope(TState state) where TState : notnull - { - return null; - } - - public bool IsEnabled(LogLevel logLevel) - { - return true; - } - - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) - { - - } - } - - class MockSendErrorSocketProvider : ISocketClientProvider - { - public bool IsConnected { get; private set; } - - public IPEndPoint LocalEndPoint { get; set; } = new IPEndPoint(IPAddress.Any, 0); - - public ValueTask CloseAsync() - { - return ValueTask.CompletedTask; - } - - public ValueTask ConnectAsync(IPEndPoint endPoint, CancellationToken token = default) - { - IsConnected = true; - return ValueTask.FromResult(true); - } - - public ValueTask ReceiveAsync(Memory buffer, CancellationToken token = default) - { - return ValueTask.FromResult(0); - } - - public ValueTask SendAsync(ReadOnlyMemory data, CancellationToken token = default) - { - throw new Exception("Mock send error"); - } - } - - class MockConnectTimeoutSocketProvider : ISocketClientProvider - { - public bool IsConnected { get; private set; } - - public IPEndPoint LocalEndPoint { get; set; } = new IPEndPoint(IPAddress.Any, 0); - - public ValueTask CloseAsync() - { - return ValueTask.CompletedTask; - } - - public async ValueTask ConnectAsync(IPEndPoint endPoint, CancellationToken token = default) - { - await Task.Delay(1000, token); - IsConnected = false; - return false; - } - - public ValueTask ReceiveAsync(Memory buffer, CancellationToken token = default) - { - return ValueTask.FromResult(0); - } - - public ValueTask SendAsync(ReadOnlyMemory data, CancellationToken token = default) - { - return ValueTask.FromResult(true); - } - } - - class MockConnectCancelSocketProvider : ISocketClientProvider - { - public bool IsConnected { get; private set; } - - public IPEndPoint LocalEndPoint { get; set; } = new IPEndPoint(IPAddress.Any, 0); - - public ValueTask CloseAsync() - { - return ValueTask.CompletedTask; - } - - public async ValueTask ConnectAsync(IPEndPoint endPoint, CancellationToken token = default) - { - await Task.Delay(250, token); - return false; - } - - public ValueTask ReceiveAsync(Memory buffer, CancellationToken token = default) - { - return ValueTask.FromResult(0); - } - - public ValueTask SendAsync(ReadOnlyMemory data, CancellationToken token = default) - { - return ValueTask.FromResult(true); - } - } - - class MockSendTimeoutSocketProvider : ISocketClientProvider - { - public bool IsConnected { get; private set; } - - public IPEndPoint LocalEndPoint { get; set; } = new IPEndPoint(IPAddress.Any, 0); - - public ValueTask CloseAsync() - { - return ValueTask.CompletedTask; - } - - public ValueTask ConnectAsync(IPEndPoint endPoint, CancellationToken token = default) - { - IsConnected = true; - return ValueTask.FromResult(true); - } - - public ValueTask ReceiveAsync(Memory buffer, CancellationToken token = default) - { - return ValueTask.FromResult(0); - } - - public async ValueTask SendAsync(ReadOnlyMemory data, CancellationToken token = default) - { - // 模拟超时发送 - await Task.Delay(100, token); - return false; - } - } - - class MockAutoReconnectLockSocketProvider : ISocketClientProvider - { - public bool IsConnected { get; private set; } - - public IPEndPoint LocalEndPoint { get; set; } = new IPEndPoint(IPAddress.Loopback, 0); - - public async ValueTask ConnectAsync(IPEndPoint endPoint, CancellationToken token = default) - { - await Task.Delay(100, token); - return IsConnected; - } - - public ValueTask SendAsync(ReadOnlyMemory data, CancellationToken token = default) - { - return ValueTask.FromResult(true); - } - - public ValueTask ReceiveAsync(Memory buffer, CancellationToken token = default) - { - byte[] data = [1, 2, 3, 4, 5]; - data.CopyTo(buffer); - return ValueTask.FromResult(5); - } - - public ValueTask CloseAsync() - { - return ValueTask.CompletedTask; - } - - public void SetConnected(bool state) - { - IsConnected = state; - } - } - - class MockAutoReconnectSocketProvider : ISocketClientProvider - { - public bool IsConnected { get; private set; } - - public IPEndPoint LocalEndPoint { get; set; } = new IPEndPoint(IPAddress.Loopback, 0); - - public ValueTask ConnectAsync(IPEndPoint endPoint, CancellationToken token = default) - { - return ValueTask.FromResult(IsConnected); - } - - private bool _sendState = true; - public ValueTask SendAsync(ReadOnlyMemory data, CancellationToken token = default) - { - return ValueTask.FromResult(_sendState); - } - - private bool _receiveState = true; - public ValueTask ReceiveAsync(Memory buffer, CancellationToken token = default) - { - if (_receiveState) - { - byte[] data = [1, 2, 3, 4, 5]; - data.CopyTo(buffer); - return ValueTask.FromResult(5); - } - else - { - return ValueTask.FromResult(0); - } - } - - public ValueTask CloseAsync() - { - return ValueTask.CompletedTask; - } - - public void SetConnected(bool state) - { - IsConnected = state; - } - - public void SetSend(bool state) - { - _sendState = state; - } - - public void SetReceive(bool state) - { - _receiveState = state; - } - } - - [SocketDataTypeConverter(Type = typeof(SocketDataConverter))] - class MockEntity - { - [SocketDataPropertyConverter(Type = typeof(byte[]), Offset = 0, Length = 5)] - public byte[]? Header { get; set; } - - [SocketDataPropertyConverter(Type = typeof(byte[]), Offset = 5, Length = 2)] - public byte[]? Body { get; set; } - - [SocketDataPropertyConverter(Type = typeof(string), Offset = 7, Length = 1, EncodingName = "utf-8")] - public string? Value1 { get; set; } - - [SocketDataPropertyConverter(Type = typeof(int), Offset = 8, Length = 1)] - public int Value2 { get; set; } - - [SocketDataPropertyConverter(Type = typeof(long), Offset = 9, Length = 1)] - public long Value3 { get; set; } - - [SocketDataPropertyConverter(Type = typeof(double), Offset = 10, Length = 8)] - public double Value4 { get; set; } - - [SocketDataPropertyConverter(Type = typeof(float), Offset = 18, Length = 4)] - public float Value5 { get; set; } - - [SocketDataPropertyConverter(Type = typeof(short), Offset = 22, Length = 1)] - public short Value6 { get; set; } - - [SocketDataPropertyConverter(Type = typeof(ushort), Offset = 23, Length = 1)] - public ushort Value7 { get; set; } - - [SocketDataPropertyConverter(Type = typeof(uint), Offset = 24, Length = 1)] - public uint Value8 { get; set; } - - [SocketDataPropertyConverter(Type = typeof(ulong), Offset = 25, Length = 1)] - public ulong Value9 { get; set; } - - [SocketDataPropertyConverter(Type = typeof(bool), Offset = 26, Length = 1)] - public bool Value10 { get; set; } - - [SocketDataPropertyConverter(Type = typeof(EnumEducation), Offset = 27, Length = 1)] - public EnumEducation Value11 { get; set; } - - [SocketDataPropertyConverter(Type = typeof(Foo), Offset = 28, Length = 1, ConverterType = typeof(FooConverter), ConverterParameters = ["test"])] - public Foo? Value12 { get; set; } - - [SocketDataPropertyConverter(Type = typeof(string), Offset = 7, Length = 1)] - public string? Value14 { get; set; } - - public string? Value13 { get; set; } - } - - class MockSocketDataConverter : SocketDataConverter - { - protected override bool Parse(ReadOnlyMemory data, MockEntity entity) - { - return false; - } - } - - class FooConverter(string name) : ISocketDataPropertyConverter - { - public object? Convert(ReadOnlyMemory data) - { - return new Foo() { Id = data.Span[0], Name = name }; - } - } - - class NoConvertEntity - { - public byte[]? Header { get; set; } - - public byte[]? Body { get; set; } - } - - class OptionConvertEntity - { - public byte[]? Header { get; set; } - - public byte[]? Body { get; set; } - } -} diff --git a/test/UnitTest/Services/TcpSocketPropertyConverterTest.cs b/test/UnitTest/Services/TcpSocketPropertyConverterTest.cs deleted file mode 100644 index 60a63ab4533..00000000000 --- a/test/UnitTest/Services/TcpSocketPropertyConverterTest.cs +++ /dev/null @@ -1,73 +0,0 @@ -// 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); - } -} diff --git a/test/UnitTest/Utils/UtilityTest.cs b/test/UnitTest/Utils/UtilityTest.cs index b90980f7d0c..bfbf3d58ad6 100644 --- a/test/UnitTest/Utils/UtilityTest.cs +++ b/test/UnitTest/Utils/UtilityTest.cs @@ -743,27 +743,6 @@ public void FormatIp_Test() Assert.Equal("192.168.1.###", result); } - [Fact] - public void ConvertToIPAddress_Ok() - { - var ex = Assert.Throws(() => Utility.ConvertToIPAddress("")); - Assert.NotNull(ex); - - var address = Utility.ConvertToIPAddress("any"); - Assert.Equal(IPAddress.Any, address); - } - - [Fact] - public void ConvertToIpEndPoint_Ok() - { - var ex = Assert.Throws(() => Utility.ConvertToIpEndPoint("localhost", 88990)); - Assert.NotNull(ex); - - ex = null; - ex = Assert.Throws(() => Utility.ConvertToIpEndPoint("localhost", -1000)); - Assert.NotNull(ex); - } - [AutoGenerateClass(Align = Alignment.Center)] private class Dog {