发布:3.1.7

This commit is contained in:
若汝棋茗
2025-06-08 21:54:55 +08:00
parent c6064c614f
commit a7c490d21c
8 changed files with 269 additions and 145 deletions

View File

@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<BaseVersion>3.1.6</BaseVersion>
<BaseVersion>3.1.7</BaseVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<TouchSocketVersion>$(BaseVersion)</TouchSocketVersion>

View File

@@ -10,6 +10,7 @@
// 感谢您的下载和使用
//------------------------------------------------------------------------------
using System;
using System.Runtime.CompilerServices;
namespace TouchSocket.Core;
@@ -37,4 +38,13 @@ public static class GlobalEnvironment
public static bool IsDynamicCodeSupported => true;
#endif
/// <summary>
/// 获取应用程序的基础目录。
/// </summary>
public static string BaseDirectory =>
#if NET45
AppDomain.CurrentDomain.BaseDirectory;
#else
AppContext.BaseDirectory;
#endif
}

View File

@@ -42,7 +42,7 @@ public sealed class FileLogger : LoggerBase, IDisposable
// 表达式的结果是根据当前日期格式化后的字符串,确保每天的日志被打包在不同的文件夹中
this.m_createLogFolder = (logLevel) =>
{
return Path.Combine("logs", DateTime.Now.ToString("[yyyy-MM-dd]"));
return Path.Combine(GlobalEnvironment.BaseDirectory, "logs", DateTime.Now.ToString("[yyyy-MM-dd]"));
};
}

View File

@@ -17,20 +17,36 @@ using System.Text;
using System.Threading.Tasks;
namespace TouchSocket.Core;
/// <summary>
/// 表示动态方法的信息。
/// </summary>
public interface IDynamicMethodInfo
{
/// <summary>
/// 真实返回值类型。
/// <para>当方法为void或task时为null</para>
/// <para>当方法为task泛型时为泛型元素类型</para>
/// <para>当方法为 void 或 Task 时,为 null</para>
/// <para>当方法为 Task 泛型时,为泛型元素类型</para>
/// </summary>
Type RealReturnType { get;}
Type RealReturnType { get; }
/// <summary>
/// 返回值的Task类型。
/// 返回值的 Task 类型。
/// </summary>
MethodReturnKind ReturnKind { get;}
MethodReturnKind ReturnKind { get; }
object Invoke(object instance, object[] parameters);
/// <summary>
/// 异步获取方法的结果。
/// </summary>
/// <param name="result">方法的返回值。</param>
/// <returns>异步任务,包含方法的结果。</returns>
Task<object> GetResultAsync(object result);
}
/// <summary>
/// 调用方法。
/// </summary>
/// <param name="instance">方法所属的实例对象。</param>
/// <param name="parameters">方法的参数。</param>
/// <returns>方法的返回值。</returns>
object Invoke(object instance, object[] parameters);
}

View File

@@ -25,7 +25,6 @@ public class FileResourceInfo : PackageBase
{
private FileSection[] m_fileSections;
/// <summary>
/// 初始化FileResourceInfo对象的新实例。
/// </summary>
@@ -41,7 +40,7 @@ public class FileResourceInfo : PackageBase
}
// 使用FileInfo对象创建RemoteFileInfo对象并用其初始化FileResourceInfo对象
this.Create(fileInfo.Map<RemoteFileInfo>(), fileSectionSize);
this.PrivateCreate(fileInfo.Map<RemoteFileInfo>(), fileSectionSize);
}
/// <summary>
@@ -62,13 +61,14 @@ public class FileResourceInfo : PackageBase
public FileResourceInfo(RemoteFileInfo fileInfo, int fileSectionSize)
{
// 调用Create方法来初始化远程资源这是因为初始化过程可能涉及到复杂的逻辑通过调用已有方法可以简化构造函数的代码
this.Create(fileInfo, fileSectionSize);
this.PrivateCreate(fileInfo, fileSectionSize);
}
/// <summary>
/// 从内存初始化资源
/// </summary>
/// <param name="byteBlock">包含资源信息的字节块</param>
[Obsolete($"此方法已被弃用,请使用{nameof(FileResourceInfo.Create)}")]
public FileResourceInfo(in IByteBlock byteBlock)
{
// 读取文件区块大小
@@ -92,6 +92,10 @@ public class FileResourceInfo : PackageBase
this.m_fileSections = fileSections;
}
private FileResourceInfo()
{
}
/// <summary>
/// 资源文件信息
/// </summary>
@@ -112,6 +116,58 @@ public class FileResourceInfo : PackageBase
/// </summary>
public int ResourceHandle { get; private set; }
/// <summary>
/// 从字节块创建一个新的 <see cref="FileResourceInfo"/> 实例。
/// </summary>
/// <param name="byteBlock">字节块,用于读取文件资源信息。</param>
/// <returns>返回一个新的 <see cref="FileResourceInfo"/> 实例。</returns>
public static FileResourceInfo Create(ByteBlock byteBlock)
{
return Create(ref byteBlock);
}
/// <summary>
/// 从流中创建一个新的 <see cref="FileResourceInfo"/> 实例。
/// </summary>
/// <param name="stream">包含文件资源信息的流。</param>
/// <returns>返回一个新的 <see cref="FileResourceInfo"/> 实例。</returns>
public static FileResourceInfo Create(Stream stream)
{
return Create(new ByteBlock(stream.ReadAllToByteArray()));
}
/// <summary>
/// 从字节块创建一个新的 <see cref="FileResourceInfo"/> 实例。
/// </summary>
/// <typeparam name="TByteBlock">字节块的类型,必须实现 <see cref="IByteBlock"/> 接口。</typeparam>
/// <param name="byteBlock">引用的字节块,用于读取文件资源信息。</param>
/// <returns>返回一个新的 <see cref="FileResourceInfo"/> 实例。</returns>
public static FileResourceInfo Create<TByteBlock>(ref TByteBlock byteBlock) where TByteBlock : IByteBlock
{
var fileResourceInfo = new FileResourceInfo();
// 读取文件区块大小
fileResourceInfo.FileSectionSize = byteBlock.ReadInt32();
// 读取资源句柄
fileResourceInfo.ResourceHandle = byteBlock.ReadInt32();
// 读取文件信息
fileResourceInfo.FileInfo = byteBlock.ReadPackage<RemoteFileInfo>();
// 读取文件区块数量
var len = byteBlock.ReadInt32();
// 根据读取的文件区块数量,创建相应的 FileSection 数组
var fileSections = new FileSection[len];
// 遍历每个文件区块,并从字节块中读取具体信息
for (var i = 0; i < len; i++)
{
fileSections[i] = byteBlock.ReadPackage<FileSection>();
}
// 将读取的文件区块信息数组赋值给成员变量
fileResourceInfo.m_fileSections = fileSections;
return fileResourceInfo;
}
/// <summary>
/// 获取尝试续传时的索引。
/// </summary>
@@ -151,6 +207,7 @@ public class FileResourceInfo : PackageBase
// 使用LINQ查询语法筛选出所有Status为指定fileSectionStatus的FileSection对象
return this.FileSections.Where(a => a.Status == fileSectionStatus);
}
/// <inheritdoc/>
public override void Package<TByteBlock>(ref TByteBlock byteBlock)
{
@@ -173,35 +230,6 @@ public class FileResourceInfo : PackageBase
this.ResourceHandle = handle;
}
/// <inheritdoc/>
public override void Unpackage<TByteBlock>(ref TByteBlock byteBlock)
{
this.ResourceHandle = byteBlock.ReadInt32();
this.FileInfo = byteBlock.ReadPackage<RemoteFileInfo>();
}
private void Create(RemoteFileInfo fileInfo, long fileSectionSize)
{
this.ResourceHandle = this.GetHashCode();
this.FileSectionSize = (int)fileSectionSize;
var sectionCount = (int)((fileInfo.Length / fileSectionSize) + 1);
var sections = new FileSection[sectionCount];
for (var i = 0; i < sectionCount; i++)
{
var fileSection = new FileSection()
{
Offset = i * fileSectionSize,
Length = (int)Math.Min(fileInfo.Length - i * fileSectionSize, fileSectionSize),
ResourceHandle = this.ResourceHandle,
Status = FileSectionStatus.Default,
Index = i
};
sections[i] = fileSection;
}
this.FileInfo = fileInfo;
this.m_fileSections = sections;
}
/// <summary>
/// 将<see cref="FileResourceInfo"/>对象保存到内存。
/// </summary>
@@ -222,4 +250,56 @@ public class FileResourceInfo : PackageBase
byteBlock.WritePackage(item);
}
}
/// <summary>
/// 将<see cref="FileResourceInfo"/>对象保存到内存。
/// </summary>
/// <param name="byteBlock">用于存储文件资源信息的字节块参数。</param>
public void Save(ByteBlock byteBlock)
{
this.Save(ref byteBlock);
}
/// <summary>
/// 将 <see cref="FileResourceInfo"/> 对象保存到指定的流中。
/// </summary>
/// <param name="stream">目标流,用于存储文件资源信息。</param>
public void Save(Stream stream)
{
using (var byteBlock = new ByteBlock(1024 * 64))
{
this.Save(byteBlock);
// 将字节块的内容写入到指定的流中
stream.Write(byteBlock.Span);
}
}
/// <inheritdoc/>
public override void Unpackage<TByteBlock>(ref TByteBlock byteBlock)
{
this.ResourceHandle = byteBlock.ReadInt32();
this.FileInfo = byteBlock.ReadPackage<RemoteFileInfo>();
}
private void PrivateCreate(RemoteFileInfo fileInfo, long fileSectionSize)
{
this.ResourceHandle = this.GetHashCode();
this.FileSectionSize = (int)fileSectionSize;
var sectionCount = (int)((fileInfo.Length / fileSectionSize) + 1);
var sections = new FileSection[sectionCount];
for (var i = 0; i < sectionCount; i++)
{
var fileSection = new FileSection()
{
Offset = i * fileSectionSize,
Length = (int)Math.Min(fileInfo.Length - i * fileSectionSize, fileSectionSize),
ResourceHandle = this.ResourceHandle,
Status = FileSectionStatus.Default,
Index = i
};
sections[i] = fileSection;
}
this.FileInfo = fileInfo;
this.m_fileSections = sections;
}
}

View File

@@ -20,7 +20,6 @@ namespace TouchSocket.Dmtp.FileTransfer;
/// </summary>
public class FileSectionResult : ResultBase, IDisposable
{
/// <summary>
/// 构造函数初始化FileSectionResult对象用于处理文件段结果。
/// </summary>
@@ -61,7 +60,8 @@ public class FileSectionResult : ResultBase, IDisposable
/// </summary>
public void Dispose()
{
// 释放当前对象持有的资源
this.Value.Dispose();
var value = this.Value;
this.Value = null;
value.SafeDispose();
}
}

View File

@@ -15,10 +15,6 @@ using System.Diagnostics.CodeAnalysis;
using TouchSocket.Core;
using static System.Collections.Specialized.BitVector32;
#if NET6_0_OR_GREATER
using System.Diagnostics.CodeAnalysis;
#endif
namespace TouchSocket.Rpc;
/// <summary>

View File

@@ -13,6 +13,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using TouchSocket.Core;
@@ -22,89 +23,22 @@ namespace TouchSocket.Sockets;
/// <summary>
/// 客户端工厂的基类,用于创建特定类型的客户端对象。
/// </summary>
/// <typeparam name="TClient">客户端类型必须实现IClient接口。</typeparam>
/// <typeparam name="TClient">客户端类型,必须实现<see cref="IClient"/>接口。</typeparam>
public abstract class ClientFactory<TClient> : DependencyObject where TClient : IClient
{
private readonly ConcurrentList<TClient> m_createdClients = new ConcurrentList<TClient>();
private readonly CancellationTokenSource m_cts = new CancellationTokenSource();
private readonly ConcurrentQueue<TClient> m_freeClients = new ConcurrentQueue<TClient>();
private readonly SingleTimer m_singleTimer;
/// <summary>
/// 客户端工厂类的构造函数。
/// </summary>
public ClientFactory()
{
// 初始化一个单例计时器每1000毫秒执行一次指定的回调方法。
this.m_singleTimer = new SingleTimer(1000, () =>
{
// 创建一个临时客户端列表,用于存储需要移除的客户端。
var list = new List<TClient>();
// 移除所有不再活跃的客户端。
this.m_createdClients.RemoveAll(a =>
{
// 如果客户端不再活跃,则移除并处理该客户端。
if (!this.IsAlive(a))
{
this.DisposeClient(a);
return true;
}
return false;
});
// 注释掉的代码块,用于在客户端数量不足最小值时新增客户端。
//if (this.CreatedClients.Count < this.MinCount)
//{
// using (this.GetClient())
// {
// }
//}
});
// 启动定时任务但不阻塞构造函数
_ = this.RunPeriodicAsync();
}
#region
/// <summary>
/// 获取可用的客户端数量。
/// <para>
/// 该值指示了当前空闲的客户端数量和未创建的客户端数量。
/// </para>
/// </summary>
/// <returns></returns>
public int AvailableCount => Math.Max(0, this.MaxCount - this.CreatedClients.Count) + this.FreeClients.Count;
/// <summary>
/// 获取已经创建的客户端数量。
/// </summary>
public int CreatedCount => this.CreatedClients.Count;
/// <summary>
/// 获取空闲的客户端数量。
/// </summary>
public int FreeCount => this.FreeClients.Count;
/// <summary>
/// 最大客户端数量。默认10。
/// </summary>
public int MaxCount { get; set; } = 10;
/// <summary>
/// 池中维护的最小客户端数量。默认0。
/// </summary>
public int MinCount { get; set; }
/// <summary>
/// 已创建的客户端安全列表,一般不要直接操作。
/// </summary>
protected IReadOnlyList<TClient> CreatedClients => this.m_createdClients;
/// <summary>
/// 空闲客户端的安全队列,一般不要直接操作。
/// </summary>
protected ConcurrentQueue<TClient> FreeClients => this.m_freeClients;
#endregion
/// <summary>
/// 清理池中的所有客户端。
/// </summary>
@@ -140,31 +74,6 @@ public abstract class ClientFactory<TClient> : DependencyObject where TClient :
this.m_createdClients.Remove(client);
}
#region GetClient
/// <summary>
/// 获取用于传输的客户端结果。可以支持<see cref="IDisposable"/>。
/// </summary>
/// <param name="waitTime">等待时间,超过此时间则取消获取客户端的操作。</param>
/// <returns>返回一个<see cref="ClientFactoryResult{TClient}"/>对象,包含租用的客户端和归还客户端的方法。</returns>
public virtual async ValueTask<ClientFactoryResult<TClient>> GetClient(TimeSpan waitTime)
{
// 租用客户端,并配置不等待主线程
return new ClientFactoryResult<TClient>(await this.RentClient(waitTime).ConfigureAwait(EasyTask.ContinueOnCapturedContext), this.ReturnClient);
}
/// <summary>
/// 获取一个指定客户端默认情况下等待1秒。
/// </summary>
/// <returns>返回一个<see cref="ClientFactoryResult{TClient}"/>对象,包含租用的客户端和归还客户端的方法。</returns>
public ValueTask<ClientFactoryResult<TClient>> GetClient()
{
// 使用默认等待时间1秒来获取客户端
return this.GetClient(TimeSpan.FromSeconds(1));
}
#endregion GetClient
/// <summary>
/// 判断客户端是不是存活状态。
/// </summary>
@@ -183,7 +92,7 @@ public abstract class ClientFactory<TClient> : DependencyObject where TClient :
{
if (disposing)
{
this.m_singleTimer.SafeDispose();
this.m_cts.Cancel();
this.Clear();
}
base.Dispose(disposing);
@@ -248,6 +157,119 @@ public abstract class ClientFactory<TClient> : DependencyObject where TClient :
}
}
private async Task ExecuteAsyncTask()
{
try
{
// 移除所有不再活跃的客户端。
this.m_createdClients.RemoveAll(a =>
{
// 如果客户端不再活跃,则移除并处理该客户端。
if (!this.IsAlive(a))
{
this.DisposeClient(a);
return true;
}
return false;
});
var clients = new List<TClient>();
while (this.CreatedCount < this.MinCount)
{
var client = await this.RentClient(TimeSpan.FromSeconds(1)).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
clients.Add(client);
}
foreach (var item in clients)
{
this.ReturnClient(item);
}
}
catch
{
}
}
private async Task RunPeriodicAsync()
{
while (!this.m_cts.IsCancellationRequested)
{
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(EasyTask.ContinueOnCapturedContext); // 每秒执行
await this.ExecuteAsyncTask().ConfigureAwait(EasyTask.ContinueOnCapturedContext);
}
}
#region
/// <summary>
/// 获取可用的客户端数量。
/// <para>
/// 该值指示了当前空闲的客户端数量和未创建的客户端数量。
/// </para>
/// </summary>
/// <returns></returns>
public int AvailableCount => Math.Max(0, this.MaxCount - this.CreatedClients.Count) + this.FreeClients.Count;
/// <summary>
/// 获取已经创建的客户端数量。
/// </summary>
public int CreatedCount => this.CreatedClients.Count;
/// <summary>
/// 获取空闲的客户端数量。
/// </summary>
public int FreeCount => this.FreeClients.Count;
/// <summary>
/// 最大客户端数量。默认10。
/// </summary>
public int MaxCount { get; set; } = 10;
/// <summary>
/// 池中维护的最小客户端数量。默认0。
/// </summary>
public int MinCount { get; set; }
/// <summary>
/// 已创建的客户端安全列表,一般不要直接操作。
/// </summary>
protected IReadOnlyList<TClient> CreatedClients => this.m_createdClients;
/// <summary>
/// 空闲客户端的安全队列,一般不要直接操作。
/// </summary>
protected ConcurrentQueue<TClient> FreeClients => this.m_freeClients;
#endregion
#region GetClient
/// <summary>
/// 获取用于传输的客户端结果。可以支持<see cref="IDisposable"/>。
/// </summary>
/// <param name="waitTime">等待时间,超过此时间则取消获取客户端的操作。</param>
/// <returns>返回一个<see cref="ClientFactoryResult{TClient}"/>对象,包含租用的客户端和归还客户端的方法。</returns>
public virtual async ValueTask<ClientFactoryResult<TClient>> GetClient(TimeSpan waitTime)
{
// 租用客户端,并配置不等待主线程
return new ClientFactoryResult<TClient>(await this.RentClient(waitTime).ConfigureAwait(EasyTask.ContinueOnCapturedContext), this.ReturnClient);
}
/// <summary>
/// 获取一个指定客户端默认情况下等待1秒。
/// </summary>
/// <returns>返回一个<see cref="ClientFactoryResult{TClient}"/>对象,包含租用的客户端和归还客户端的方法。</returns>
public ValueTask<ClientFactoryResult<TClient>> GetClient()
{
// 使用默认等待时间1秒来获取客户端
return this.GetClient(TimeSpan.FromSeconds(1));
}
#endregion GetClient
private bool Wait()
{
return !this.FreeClients.IsEmpty;