mirror of
https://github.com/RRQM/TouchSocket.git
synced 2025-12-18 01:16:44 +08:00
发布:3.1.0
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
<Project>
|
||||
|
||||
<Import Project="$../../../TouchSocketVersion.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TouchSocketVersion>3.0.26</TouchSocketVersion>
|
||||
<Version>$(TouchSocketVersion)</Version>
|
||||
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<LangVersion>latest</LangVersion>
|
||||
@@ -20,13 +23,7 @@
|
||||
<!--<Nullable>enable</Nullable>-->
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'">
|
||||
<Version>$(TouchSocketVersion)</Version>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<Version>$(TouchSocketVersion)-Debug</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="PolySharp" Version="1.15.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
@@ -97,16 +94,6 @@
|
||||
<Copy SourceFiles="$(PackageOutputPath)\$(PackageId).$(PackageVersion).nupkg" DestinationFolder="D:\Nuget\local" />
|
||||
</Target>
|
||||
|
||||
<!--<ItemGroup Condition="'$(IsSourceGenerator)'!='True'">
|
||||
<PackageReference Include="Backport.System.Threading.Lock" Version="3.1.4">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<Using Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net9.0'))" Alias="Lock" Include="System.Threading.Lock" />
|
||||
<Using Condition="!$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net9.0'))" Alias="Lock" Include="Backport.System.Threading.Lock" />
|
||||
<Using Alias="LockFactory" Include="Backport.System.Threading.LockFactory" />
|
||||
</ItemGroup>-->
|
||||
|
||||
<PropertyGroup Condition="'$(IsSourceGenerator)'!='True'">
|
||||
<PackageReadmeFile>Readme.md</PackageReadmeFile>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TouchSocket.Core;
|
||||
using TouchSocket.Resources;
|
||||
@@ -192,7 +193,7 @@ public class WebSocketDmtpService : ConnectableService<WebSocketDmtpSessionClien
|
||||
/// </summary>
|
||||
/// <returns>异步任务。</returns>
|
||||
/// <exception cref="NotSupportedException">抛出不支持异常。</exception>
|
||||
public override Task StopAsync()
|
||||
public override Task<Result> StopAsync(CancellationToken token=default)
|
||||
{
|
||||
throw new NotSupportedException("此服务的生命周期跟随主Host");
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public class WebSocketDmtpSessionClient : ResolverConfigObject, IWebSocketDmtpSe
|
||||
private HttpContext m_httpContext;
|
||||
private string m_id;
|
||||
private readonly Lock m_locker = new Lock();
|
||||
|
||||
private CancellationTokenSource m_tokenSourceForReceive;
|
||||
#endregion 字段
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -81,10 +81,10 @@ public class WebSocketDmtpSessionClient : ResolverConfigObject, IWebSocketDmtpSe
|
||||
public bool Online => this.DmtpActor.Online;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public DateTime LastReceivedTime => this.m_receiveCounter.LastIncrement;
|
||||
public DateTimeOffset LastReceivedTime => this.m_receiveCounter.LastIncrement;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public DateTime LastSentTime => this.m_sentCounter.LastIncrement;
|
||||
public DateTimeOffset LastSentTime => this.m_sentCounter.LastIncrement;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override IPluginManager PluginManager => this.m_pluginManager;
|
||||
@@ -105,26 +105,31 @@ public class WebSocketDmtpSessionClient : ResolverConfigObject, IWebSocketDmtpSe
|
||||
public string VerifyToken => this.Config.GetValue(DmtpConfigExtension.DmtpOptionProperty).VerifyToken;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task CloseAsync(string msg)
|
||||
public async Task<Result> CloseAsync(string msg, CancellationToken token = default)
|
||||
{
|
||||
if (this.m_dmtpActor != null)
|
||||
try
|
||||
{
|
||||
await this.m_dmtpActor.CloseAsync(msg).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
if (this.m_dmtpActor != null)
|
||||
{
|
||||
await this.m_dmtpActor.CloseAsync(msg, token).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
if (this.m_client != null)
|
||||
{
|
||||
await this.m_client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, msg, token).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
this.Abort(true, msg);
|
||||
return Result.Success;
|
||||
}
|
||||
if (this.m_client != null)
|
||||
catch (Exception ex)
|
||||
{
|
||||
await this.m_client.CloseAsync(WebSocketCloseStatus.NormalClosure, msg, CancellationToken.None).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
return Result.FromException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task ResetIdAsync(string newId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(newId))
|
||||
{
|
||||
throw new ArgumentException($"“{nameof(newId)}”不能为 null 或空。", nameof(newId));
|
||||
}
|
||||
|
||||
ThrowHelper.ThrowArgumentNullExceptionIfStringIsNullOrEmpty(newId, nameof(newId));
|
||||
if (this.m_id == newId)
|
||||
{
|
||||
return;
|
||||
@@ -182,23 +187,36 @@ public class WebSocketDmtpSessionClient : ResolverConfigObject, IWebSocketDmtpSe
|
||||
this.m_dmtpActor = actor;
|
||||
}
|
||||
|
||||
internal async Task Start(WebSocket webSocket, HttpContext context)
|
||||
internal async Task Start(WebSocket client, HttpContext context)
|
||||
{
|
||||
this.m_client = webSocket;
|
||||
var tokenSourceForReceive = new CancellationTokenSource();
|
||||
this.m_tokenSourceForReceive = tokenSourceForReceive;
|
||||
var token = tokenSourceForReceive.Token;
|
||||
|
||||
this.m_client = client;
|
||||
this.m_httpContext = context;
|
||||
string msg = string.Empty;
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
using (var byteBlock = new ByteBlock(this.m_receiveBufferSize))
|
||||
{
|
||||
var result = await this.m_client.ReceiveAsync(byteBlock.TotalMemory, default).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
if (client.State!= WebSocketState.Open)
|
||||
{
|
||||
msg = TouchSocketResource.ClientNotConnected;
|
||||
break;
|
||||
}
|
||||
var result = await client.ReceiveAsync(byteBlock.TotalMemory, token).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
|
||||
if (result.MessageType == WebSocketMessageType.Close)
|
||||
{
|
||||
try
|
||||
{
|
||||
await this.m_client.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
if (!token.IsCancellationRequested)
|
||||
{
|
||||
await client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, string.Empty, token).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -215,12 +233,14 @@ public class WebSocketDmtpSessionClient : ResolverConfigObject, IWebSocketDmtpSe
|
||||
await this.m_dmtpAdapter.ReceivedInputAsync(byteBlock).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
}
|
||||
|
||||
this.Abort(false, TouchSocketResource.RemoteDisconnects);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.Abort(false, ex.Message);
|
||||
msg = ex.Message;
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.Abort(false, msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,6 +293,17 @@ public class WebSocketDmtpSessionClient : ResolverConfigObject, IWebSocketDmtpSe
|
||||
}
|
||||
}
|
||||
|
||||
private void CancelReceive()
|
||||
{
|
||||
var tokenSourceForReceive = this.m_tokenSourceForReceive;
|
||||
if (tokenSourceForReceive != null)
|
||||
{
|
||||
tokenSourceForReceive.Cancel();
|
||||
tokenSourceForReceive.Dispose();
|
||||
}
|
||||
this.m_tokenSourceForReceive = null;
|
||||
}
|
||||
|
||||
private void Abort(bool manual, string msg)
|
||||
{
|
||||
lock (this.m_locker)
|
||||
@@ -285,7 +316,7 @@ public class WebSocketDmtpSessionClient : ResolverConfigObject, IWebSocketDmtpSe
|
||||
base.Dispose(true);
|
||||
this.m_client.SafeDispose();
|
||||
this.DmtpActor.SafeDispose();
|
||||
|
||||
this.CancelReceive();
|
||||
if (this.m_service.TryRemove(this.m_id, out _))
|
||||
{
|
||||
//if (this.PluginManager.Enable)
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace TouchSocket.Dmtp.AspNetCore;
|
||||
/// <summary>
|
||||
/// 基于WebSocket协议的Dmtp服务器辅助客户端。
|
||||
/// </summary>
|
||||
public interface IWebSocketDmtpSessionClient : IClient, IIdClient, IDmtpActorObject, IClosableClient, IResolverConfigObject, IOnlineClient
|
||||
public interface IWebSocketDmtpSessionClient : IDependencyClient, IIdClient, IDmtpActorObject, IClosableClient, IResolverConfigObject, IOnlineClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Http上下文
|
||||
|
||||
@@ -35,7 +35,7 @@ public sealed class CacheEntry<TKey, TValue> : ICacheEntry<TKey, TValue>
|
||||
public CacheEntry(TKey key, TValue value)
|
||||
{
|
||||
// 初始化更新时间为当前UTC时间。
|
||||
this.UpdateTime = DateTime.UtcNow;
|
||||
this.UpdateTime = DateTimeOffset.UtcNow;
|
||||
// 设置默认缓存持续时间为1分钟。
|
||||
this.Duration = TimeSpan.FromSeconds(60);
|
||||
// 设置缓存条目的键。
|
||||
@@ -57,7 +57,7 @@ public sealed class CacheEntry<TKey, TValue> : ICacheEntry<TKey, TValue>
|
||||
/// <summary>
|
||||
/// 更新时间
|
||||
/// </summary>
|
||||
public DateTime UpdateTime { get; set; }
|
||||
public DateTimeOffset UpdateTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 值
|
||||
|
||||
@@ -85,7 +85,7 @@ public static class CacheManagementExtensions
|
||||
if (update)
|
||||
{
|
||||
// 如果设置了更新时间戳,则更新缓存项的时间戳为当前时间。
|
||||
cacheEntry.UpdateTime = DateTime.UtcNow;
|
||||
cacheEntry.UpdateTime = DateTimeOffset.UtcNow;
|
||||
}
|
||||
value = cacheEntry.Value;
|
||||
return true;
|
||||
@@ -93,7 +93,7 @@ public static class CacheManagementExtensions
|
||||
else
|
||||
{
|
||||
// 如果当前时间与上次更新时间的差值大于等于缓存项的持续时间,表示缓存项已过期。
|
||||
if (DateTime.UtcNow - cacheEntry.UpdateTime > cacheEntry.Duration)
|
||||
if (DateTimeOffset.UtcNow - cacheEntry.UpdateTime > cacheEntry.Duration)
|
||||
{
|
||||
// 从缓存中移除过期的缓存项。
|
||||
cacheClient.RemoveCache(key);
|
||||
@@ -105,7 +105,7 @@ public static class CacheManagementExtensions
|
||||
if (update)
|
||||
{
|
||||
// 如果设置了更新时间戳,则更新缓存项的时间戳为当前时间。
|
||||
cacheEntry.UpdateTime = DateTime.UtcNow;
|
||||
cacheEntry.UpdateTime = DateTimeOffset.UtcNow;
|
||||
}
|
||||
value = cacheEntry.Value;
|
||||
return true;
|
||||
|
||||
@@ -27,7 +27,7 @@ public interface ICacheEntry
|
||||
/// <summary>
|
||||
/// 更新时间
|
||||
/// </summary>
|
||||
DateTime UpdateTime { get; set; }
|
||||
DateTimeOffset UpdateTime { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -37,7 +37,7 @@ public class MemoryCache<TKey, TValue> : IEnumerable<ICacheEntry<TKey, TValue>>,
|
||||
var list = new List<TKey>();
|
||||
foreach (var item in this.m_pairs)
|
||||
{
|
||||
if (DateTime.UtcNow - item.Value.UpdateTime > item.Value.Duration)
|
||||
if (DateTimeOffset.UtcNow - item.Value.UpdateTime > item.Value.Duration)
|
||||
{
|
||||
list.Add(item.Key);
|
||||
}
|
||||
@@ -90,7 +90,7 @@ public class MemoryCache<TKey, TValue> : IEnumerable<ICacheEntry<TKey, TValue>>,
|
||||
}
|
||||
else
|
||||
{
|
||||
if (DateTime.UtcNow - cache.UpdateTime > cache.Duration)
|
||||
if (DateTimeOffset.UtcNow - cache.UpdateTime > cache.Duration)
|
||||
{
|
||||
this.OnRemove(key, out _);
|
||||
return false;
|
||||
@@ -121,7 +121,7 @@ public class MemoryCache<TKey, TValue> : IEnumerable<ICacheEntry<TKey, TValue>>,
|
||||
}
|
||||
else
|
||||
{
|
||||
if (DateTime.UtcNow - cache.UpdateTime > cache.Duration)
|
||||
if (DateTimeOffset.UtcNow - cache.UpdateTime > cache.Duration)
|
||||
{
|
||||
this.OnRemove(key, out _);
|
||||
return default;
|
||||
|
||||
23
src/TouchSocket.Core/Class1.cs
Normal file
23
src/TouchSocket.Core/Class1.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:https://touchsocket.net/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
[assembly: UnconditionalSuppressMessage("ReflectionAnalysis", "IL2104",
|
||||
Justification = "The list only contains types stored through the annotated setter111.")]
|
||||
|
||||
[assembly: UnconditionalSuppressMessage("ReflectionAnalysis", "IL3153",
|
||||
Justification = "The list only contains types stored through the annotated setter222.")]
|
||||
#endif
|
||||
@@ -106,44 +106,4 @@ public sealed class DynamicallyAccessedMembersAttribute : Attribute
|
||||
/// </summary>
|
||||
public DynamicallyAccessedMemberTypes MemberTypes { get; }
|
||||
}
|
||||
|
||||
|
||||
///// <summary>
|
||||
///// 表示当指定成员不为 null 时,方法返回特定值的属性。
|
||||
///// </summary>
|
||||
//public sealed class MemberNotNullWhenAttribute : Attribute
|
||||
//{
|
||||
|
||||
// /// <summary>
|
||||
// /// 初始化 <see cref="MemberNotNullWhenAttribute"/> 类的新实例,适用于单个成员。
|
||||
// /// </summary>
|
||||
// /// <param name="returnValue">预期的方法返回值。</param>
|
||||
// /// <param name="member">表示成员的字符串。</param>
|
||||
// public MemberNotNullWhenAttribute(bool returnValue, string member) : this(returnValue, new string[] { member })
|
||||
// {
|
||||
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// 初始化 <see cref="MemberNotNullWhenAttribute"/> 类的新实例,适用于多个成员。
|
||||
// /// </summary>
|
||||
// /// <param name="returnValue">预期的方法返回值。</param>
|
||||
// /// <param name="members">表示成员的字符串数组。</param>
|
||||
// public MemberNotNullWhenAttribute(bool returnValue, params string[] members)
|
||||
// {
|
||||
// this.ReturnValue = returnValue;
|
||||
// this.Members = members;
|
||||
// }
|
||||
|
||||
|
||||
// /// <summary>
|
||||
// /// 获取成员的数组,这些成员在方法返回特定值时不应为 null。
|
||||
// /// </summary>
|
||||
// public string[] Members { get; }
|
||||
|
||||
// /// <summary>
|
||||
// /// 获取当指定成员不为 null 时,方法预期返回的值。
|
||||
// /// </summary>
|
||||
// public bool ReturnValue { get; }
|
||||
//}
|
||||
#endif
|
||||
@@ -1,191 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:https://touchsocket.net/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks.Sources;
|
||||
|
||||
namespace TouchSocket.Core;
|
||||
|
||||
/// <summary>
|
||||
/// 异步有界队列类,基于值任务源。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">队列中元素的类型。</typeparam>
|
||||
public sealed class AsyncBoundedQueue<T> : ValueTaskSource<T>
|
||||
{
|
||||
private readonly Lock m_locker = new Lock();
|
||||
|
||||
// 使用并发队列来存储元素,支持线程安全的操作。
|
||||
private readonly ConcurrentQueue<T> m_queue = new ConcurrentQueue<T>();
|
||||
|
||||
// 使用SemaphoreSlim来限制同时写入队列的操作数量,从而实现有界队列的功能。
|
||||
private readonly SemaphoreSlim m_writeLock;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数,初始化有界队列。
|
||||
/// </summary>
|
||||
/// <param name="capacity">队列的最大容量,必须为正数。</param>
|
||||
public AsyncBoundedQueue(int capacity)
|
||||
{
|
||||
if (capacity < 1)
|
||||
{
|
||||
ThrowHelper.ThrowArgumentOutOfRangeException_LessThan(nameof(capacity), capacity, 1);
|
||||
}
|
||||
|
||||
this.m_writeLock = new SemaphoreSlim(capacity, capacity);
|
||||
this.Capacity = capacity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取队列的最大容量。
|
||||
/// </summary>
|
||||
public int Capacity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取队列中的元素数量。
|
||||
/// </summary>
|
||||
public int Count => this.m_queue.Count;
|
||||
|
||||
/// <summary>
|
||||
/// 获取队列中剩余的可用空间数量。
|
||||
/// </summary>
|
||||
public int FreeCount => this.Capacity - this.Count;
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,该值指示队列是否为空。
|
||||
/// </summary>
|
||||
public bool IsEmpty => this.m_queue.IsEmpty;
|
||||
|
||||
/// <summary>
|
||||
/// 异步取出队列中的一个元素。
|
||||
/// <para>
|
||||
/// 注意:此方法为线程安全的方法,可以在多个线程中同时调用,但是await后的结果只能被一个线程获取。所以在多个线程中调用此方法时,可能会出现await为null的情况。
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">取消操作的令牌。</param>
|
||||
/// <returns>一个ValueTask对象,可以异步等待。</returns>
|
||||
public ValueTask<T> DequeueAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
lock (this.m_locker)
|
||||
{
|
||||
if (this.TryDequeue(out var result))
|
||||
{
|
||||
return new ValueTask<T>(result);
|
||||
}
|
||||
return base.ValueWaitAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步向队列中添加一个元素。
|
||||
/// </summary>
|
||||
/// <param name="item">要添加到队列中的元素。</param>
|
||||
/// <param name="cancellationToken">取消操作的令牌。</param>
|
||||
public async Task EnqueueAsync(T item, CancellationToken cancellationToken = default)
|
||||
{
|
||||
this.ThrowIfDisposed();
|
||||
//this.m_writeLock.CurrentCount
|
||||
await this.m_writeLock.WaitAsync(cancellationToken).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
lock (this.m_locker)
|
||||
{
|
||||
this.m_queue.Enqueue(item);
|
||||
base.Complete(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步向队列中添加一个元素。
|
||||
/// </summary>
|
||||
/// <param name="item">要添加到队列中的元素。</param>
|
||||
/// <param name="timeout">等待添加操作完成的超时时间(以毫秒为单位)。</param>
|
||||
/// <param name="cancellationToken">取消操作的令牌。</param>
|
||||
public async Task EnqueueAsync(T item, int timeout, CancellationToken cancellationToken)
|
||||
{
|
||||
this.ThrowIfDisposed();
|
||||
await this.m_writeLock.WaitTimeAsync(timeout, cancellationToken).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
|
||||
lock (this.m_locker)
|
||||
{
|
||||
this.m_queue.Enqueue(item);
|
||||
base.Complete(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试从队列中取出一个元素。
|
||||
/// </summary>
|
||||
/// <param name="result">当此方法返回时,如果操作成功,则包含从队列中移除的对象;否则为默认值。</param>
|
||||
/// <returns>如果成功从队列中移除并返回了一个对象,则为 true;否则为 false。</returns>
|
||||
public bool TryDequeue(out T result)
|
||||
{
|
||||
if (this.m_queue.TryDequeue(out result))
|
||||
{
|
||||
this.m_writeLock.Release();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (disposing)
|
||||
{
|
||||
this.m_writeLock.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从队列中取出一个元素并返回。
|
||||
/// </summary>
|
||||
/// <returns>队列中的一个元素。</returns>
|
||||
protected override T GetResult()
|
||||
{
|
||||
if (this.TryDequeue(out var result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
//移除下面这行代码
|
||||
//主要是因为公布了TryDequeue方法,有可能异步完成了,但是取数据时已被其他线程通过TryDequeue取走。
|
||||
//所以这里不再抛出异常,而是返回默认值。
|
||||
//ThrowHelper.ThrowInvalidOperationException("队列意外为空。");
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override ValueTaskSourceStatus GetStatus(short token)
|
||||
{
|
||||
lock (this.m_locker)
|
||||
{
|
||||
if (this.m_queue.IsEmpty)
|
||||
{
|
||||
return base.GetStatus(token);
|
||||
}
|
||||
return ValueTaskSourceStatus.Succeeded;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行调度操作,直接执行给定的操作。
|
||||
/// </summary>
|
||||
/// <param name="action">要执行的操作。</param>
|
||||
/// <param name="state">操作的状态对象。</param>
|
||||
protected override void Scheduler(Action<object> action, object state)
|
||||
{
|
||||
Task.Factory.StartNew(action, state);
|
||||
}
|
||||
}
|
||||
443
src/TouchSocket.Core/Collections/AsyncQueue.cs
Normal file
443
src/TouchSocket.Core/Collections/AsyncQueue.cs
Normal file
@@ -0,0 +1,443 @@
|
||||
using Microsoft;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TouchSocket.Resources;
|
||||
|
||||
namespace TouchSocket.Core;
|
||||
|
||||
/// <summary>
|
||||
/// 一个线程安全的、支持异步出队的队列。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">队列中存储的值的类型。</typeparam>
|
||||
[DebuggerDisplay("Count = {Count}, Completed = {m_completeSignaled}")]
|
||||
public class AsyncQueue<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// 由 <see cref="Completion"/> 返回的任务的来源。延迟构造。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 使用 volatile 以允许在 <see cref="Completion"/> 中的检查-锁定-检查模式可靠,
|
||||
/// 如果在锁内,一个线程初始化值并分配字段,而弱内存模型允许在初始化之前进行分配。
|
||||
/// 锁外的另一个线程可能会观察到非空字段并在实际初始化之前开始访问 Task 属性。
|
||||
/// volatile 防止围绕此字段的分配(或读取)的命令的 CPU 重排序。
|
||||
/// </remarks>
|
||||
private volatile TaskCompletionSource<object> m_completedSource;
|
||||
|
||||
/// <summary>
|
||||
/// 内部元素队列。延迟构造。
|
||||
/// </summary>
|
||||
private Queue<T> m_queueElements;
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="DequeueAsync(CancellationToken)"/> 等待者的内部队列。延迟构造。
|
||||
/// </summary>
|
||||
private Queue<TaskCompletionSource<T>> m_dequeuingWaiters;
|
||||
|
||||
/// <summary>
|
||||
/// 指示是否已调用 <see cref="Complete"/> 的值。
|
||||
/// </summary>
|
||||
private bool m_completeSignaled;
|
||||
|
||||
/// <summary>
|
||||
/// 指示是否已调用 <see cref="OnCompleted"/> 的标志。
|
||||
/// </summary>
|
||||
private bool m_onCompletedInvoked;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化 <see cref="AsyncQueue{T}"/> 类的新实例。
|
||||
/// </summary>
|
||||
public AsyncQueue()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,指示队列当前是否为空。
|
||||
/// </summary>
|
||||
public bool IsEmpty => this.Count == 0;
|
||||
|
||||
/// <summary>
|
||||
/// 获取队列中当前元素的数量。
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (this.SyncRoot)
|
||||
{
|
||||
return this.m_queueElements?.Count ?? 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个值,指示队列是否为空且已调用 <see cref="Complete" />。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 这在功能上与 <see cref="Completion"/>.IsCompleted 冗余,但此属性
|
||||
/// 不会导致 <see cref="Completion"/> 可能的任务的延迟实例化,
|
||||
/// 如果没有其他原因需要任务存在。
|
||||
/// </remarks>
|
||||
public bool IsCompleted
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (this.SyncRoot)
|
||||
{
|
||||
return this.m_completeSignaled && this.IsEmpty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个任务,当调用 <see cref="Complete"/> 且队列为空时,该任务会转换为完成状态。
|
||||
/// </summary>
|
||||
public Task Completion
|
||||
{
|
||||
get
|
||||
{
|
||||
if (this.m_completedSource is null)
|
||||
{
|
||||
lock (this.SyncRoot)
|
||||
{
|
||||
if (this.m_completedSource is null)
|
||||
{
|
||||
if (this.IsCompleted)
|
||||
{
|
||||
return EasyTask.CompletedTask;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.m_completedSource = new TaskCompletionSource<object>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.m_completedSource.Task;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取此队列使用的同步对象。
|
||||
/// </summary>
|
||||
protected Lock SyncRoot => new Lock();
|
||||
|
||||
/// <summary>
|
||||
/// 获取队列的初始容量。
|
||||
/// </summary>
|
||||
protected virtual int InitialCapacity => 4;
|
||||
|
||||
/// <summary>
|
||||
/// 表示不再有元素会被入队。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 此方法会立即返回。
|
||||
/// 在调用此方法之前入队的元素仍然可以被出队。
|
||||
/// 只有在调用此方法并且队列为空后,<see cref="IsCompleted" /> 才会返回 true。
|
||||
/// </remarks>
|
||||
public void Complete()
|
||||
{
|
||||
lock (this.SyncRoot)
|
||||
{
|
||||
this.m_completeSignaled = true;
|
||||
}
|
||||
|
||||
this.CompleteIfNecessary();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将一个元素添加到队列的尾部。
|
||||
/// </summary>
|
||||
/// <param name="value">要添加的值。</param>
|
||||
/// <exception cref="InvalidOperationException">如果已调用 <see cref="Complete" />,则抛出此异常。使用 <see cref="TryEnqueue" /> 可避免此异常。</exception>
|
||||
public void Enqueue(T value)
|
||||
{
|
||||
if (!this.TryEnqueue(value))
|
||||
{
|
||||
ThrowHelper.ThrowInvalidOperationException(TouchSocketCoreResource.InvalidAfterCompleted);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 如果队列尚未完成,则将一个元素添加到队列的尾部。
|
||||
/// </summary>
|
||||
/// <param name="value">要添加的值。</param>
|
||||
/// <returns>如果值已添加到队列,则返回 <see langword="true" />;如果队列已完成,则返回 <see langword="false" />。</returns>
|
||||
public bool TryEnqueue(T value)
|
||||
{
|
||||
var alreadyDispatched = false;
|
||||
lock (this.SyncRoot)
|
||||
{
|
||||
if (this.m_completeSignaled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 是否有出队者在等待此项?
|
||||
while (this.m_dequeuingWaiters?.Count > 0)
|
||||
{
|
||||
var waitingDequeuer = this.m_dequeuingWaiters.Dequeue();
|
||||
if (waitingDequeuer.TrySetResult(value))
|
||||
{
|
||||
alreadyDispatched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.FreeCanceledDequeuers();
|
||||
|
||||
if (!alreadyDispatched)
|
||||
{
|
||||
this.m_queueElements ??= new Queue<T>(this.InitialCapacity);
|
||||
|
||||
this.m_queueElements.Enqueue(value);
|
||||
}
|
||||
}
|
||||
|
||||
this.OnEnqueued(value, alreadyDispatched);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取队列头部的值而不将其从队列中移除(如果队列非空)。
|
||||
/// </summary>
|
||||
/// <param name="value">接收队列头部的值;如果队列为空,则为元素类型的默认值。</param>
|
||||
/// <returns>如果队列非空,则返回 <see langword="true" />;否则返回 <see langword="false" />。</returns>
|
||||
public bool TryPeek(out T value)
|
||||
{
|
||||
lock (this.SyncRoot)
|
||||
{
|
||||
if (this.m_queueElements is object && this.m_queueElements.Count > 0)
|
||||
{
|
||||
value = this.m_queueElements.Peek();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = default(T)!;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取队列头部的值而不将其从队列中移除。
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">如果队列为空,则抛出此异常。</exception>
|
||||
public T Peek()
|
||||
{
|
||||
if (!this.TryPeek(out T value))
|
||||
{
|
||||
ThrowHelper.ThrowInvalidOperationException(TouchSocketCoreResource.QueueEmpty);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个任务,其结果是队列头部的元素。
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">
|
||||
/// 一个令牌,其取消表示对该项失去兴趣。
|
||||
/// 取消此令牌并不保证任务会在分配队列头部的结果元素之前被取消。
|
||||
/// 调用者有责任在取消后确保任务被取消,或者它有一个结果,调用者需要负责处理。
|
||||
/// </param>
|
||||
/// <returns>一个任务,其结果是队列头部的元素。</returns>
|
||||
/// <exception cref="OperationCanceledException">
|
||||
/// 当此实例的队列为空且已调用 <see cref="Complete()"/> 时抛出。
|
||||
/// 当 <paramref name="cancellationToken"/> 在可以出队工作项之前被取消时也会抛出。
|
||||
/// </exception>
|
||||
public Task<T> DequeueAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return EasyTask.FromCanceled<T>(cancellationToken);
|
||||
}
|
||||
|
||||
T result;
|
||||
lock (this.SyncRoot)
|
||||
{
|
||||
if (this.IsCompleted)
|
||||
{
|
||||
return EasyTask.FromCanceled<T>(new CancellationToken(true));
|
||||
}
|
||||
|
||||
if (this.m_queueElements?.Count > 0)
|
||||
{
|
||||
result = this.m_queueElements.Dequeue();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.m_dequeuingWaiters is null)
|
||||
{
|
||||
this.m_dequeuingWaiters = new Queue<TaskCompletionSource<T>>(capacity: 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.FreeCanceledDequeuers();
|
||||
}
|
||||
|
||||
// 替换以下代码行:
|
||||
// var waiterTcs = new TaskCompletionSourceWithoutInlining<T>(allowInliningContinuations: false);
|
||||
|
||||
// 修复后的代码如下:
|
||||
var waiterTcs = new TaskCompletionSource<T>();
|
||||
waiterTcs.AttachCancellation(cancellationToken);
|
||||
this.m_dequeuingWaiters.Enqueue(waiterTcs);
|
||||
return waiterTcs.Task;
|
||||
}
|
||||
}
|
||||
|
||||
this.CompleteIfNecessary();
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 如果队列头部有可用的元素,则立即将其出队,否则返回而不出队。
|
||||
/// </summary>
|
||||
/// <param name="value">接收队列头部的元素;如果队列为空,则为 <c>default(T)</c>。</param>
|
||||
/// <returns>如果有元素被出队,则返回 <see langword="true" />;如果队列为空,则返回 <see langword="false" />。</returns>
|
||||
public bool TryDequeue(out T value)
|
||||
{
|
||||
var result = this.TryDequeueInternal(null, out value);
|
||||
this.CompleteIfNecessary();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将此队列的副本作为数组返回。
|
||||
/// </summary>
|
||||
public T[] ToArray()
|
||||
{
|
||||
lock (this.SyncRoot)
|
||||
{
|
||||
return this.m_queueElements?.ToArray() ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 如果队列头部有满足指定检查的可用元素,则立即将其出队;
|
||||
/// 否则返回而不出队。
|
||||
/// </summary>
|
||||
/// <param name="valueCheck">必须成功以出队的头部元素的测试。</param>
|
||||
/// <param name="value">接收队列头部的元素;如果队列为空,则为 <c>default(T)</c>。</param>
|
||||
/// <returns>如果有元素被出队,则返回 <see langword="true" />;如果队列为空,则返回 <see langword="false" />。</returns>
|
||||
protected bool TryDequeue(Predicate<T> valueCheck, out T value)
|
||||
{
|
||||
bool result = this.TryDequeueInternal(valueCheck, out value);
|
||||
this.CompleteIfNecessary();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当一个值被入队时调用。
|
||||
/// </summary>
|
||||
/// <param name="value">入队的值。</param>
|
||||
/// <param name="alreadyDispatched">
|
||||
/// 如果该项将跳过队列,因为已有出队者在等待项,则为 <see langword="true" />;
|
||||
/// 如果该项实际被添加到队列,则为 <see langword="false" />。
|
||||
/// </param>
|
||||
protected virtual void OnEnqueued(T value, bool alreadyDispatched)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当一个值被出队时调用。
|
||||
/// </summary>
|
||||
/// <param name="value">出队的值。</param>
|
||||
protected virtual void OnDequeued(T value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当队列完成时调用。
|
||||
/// </summary>
|
||||
protected virtual void OnCompleted()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 如果队列头部有可用的元素,则立即将其出队;
|
||||
/// 否则返回而不出队。
|
||||
/// </summary>
|
||||
/// <param name="valueCheck">必须成功以出队的头部元素的测试。</param>
|
||||
/// <param name="value">接收队列头部的元素;如果队列为空,则为 <c>default(T)</c>。</param>
|
||||
/// <returns><c><see langword="true"/></c> 如果有元素被出队;如果队列为空,则返回 <see langword="false" />。</returns>
|
||||
private bool TryDequeueInternal(Predicate<T> valueCheck, out T value)
|
||||
{
|
||||
bool dequeued;
|
||||
lock (this.SyncRoot)
|
||||
{
|
||||
if (this.m_queueElements is object && this.m_queueElements.Count > 0 && (valueCheck is null || valueCheck(this.m_queueElements.Peek())))
|
||||
{
|
||||
value = this.m_queueElements.Dequeue();
|
||||
dequeued = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = default(T)!;
|
||||
dequeued = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (dequeued)
|
||||
{
|
||||
this.OnDequeued(value);
|
||||
}
|
||||
|
||||
return dequeued;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 如果已发出信号且队列为空,则将此队列转换为完成状态。
|
||||
/// </summary>
|
||||
private void CompleteIfNecessary()
|
||||
{
|
||||
//ThrowHelper.AssertFalse(Monitor.IsEntered(this.SyncRoot), nameof(Monitor.IsEntered)); // 重要,因为我们将转换任务为完成。
|
||||
|
||||
bool transitionTaskSource, invokeOnCompleted = false;
|
||||
lock (this.SyncRoot)
|
||||
{
|
||||
transitionTaskSource = this.m_completeSignaled && (this.m_queueElements is null || this.m_queueElements.Count == 0);
|
||||
if (transitionTaskSource)
|
||||
{
|
||||
invokeOnCompleted = !this.m_onCompletedInvoked;
|
||||
this.m_onCompletedInvoked = true;
|
||||
while (this.m_dequeuingWaiters?.Count > 0)
|
||||
{
|
||||
this.m_dequeuingWaiters.Dequeue().TrySetCanceled();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (transitionTaskSource)
|
||||
{
|
||||
this.m_completedSource?.TrySetResult(null);
|
||||
if (invokeOnCompleted)
|
||||
{
|
||||
this.OnCompleted();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从等待队列的头部清除尽可能多的已取消的出队者。
|
||||
/// </summary>
|
||||
private void FreeCanceledDequeuers()
|
||||
{
|
||||
lock (this.SyncRoot)
|
||||
{
|
||||
while (this.m_dequeuingWaiters?.Count > 0 && this.m_dequeuingWaiters.Peek().Task.IsCompleted)
|
||||
{
|
||||
this.m_dequeuingWaiters.Dequeue();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,7 +63,7 @@ public class SnowflakeIdGenerator
|
||||
// 设置工作机器ID
|
||||
s_workerId = workerId;
|
||||
// 设置时间戳基准值,这是Snowflake算法中用到的一个重要参数
|
||||
this.m_twepoch = DateTime.UtcNow.Ticks - 10000;
|
||||
this.m_twepoch = DateTimeOffset.UtcNow.Ticks - 10000;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Buffers;
|
||||
|
||||
namespace TouchSocket.Core;
|
||||
|
||||
@@ -87,7 +88,7 @@ public static partial class GZip
|
||||
{
|
||||
using (var gZipStream = new GZipStream(new MemoryStream(data, offset, length), CompressionMode.Decompress))
|
||||
{
|
||||
var bytes = BytePool.Default.Rent(1024 * 64);
|
||||
var bytes = ArrayPool<byte>.Shared.Rent(1024 * 64);
|
||||
try
|
||||
{
|
||||
int r;
|
||||
@@ -99,7 +100,7 @@ public static partial class GZip
|
||||
}
|
||||
finally
|
||||
{
|
||||
BytePool.Default.Return(bytes);
|
||||
ArrayPool<byte>.Shared.Return(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ public abstract class CacheDataHandlingAdapter : SingleStreamDataHandlingAdapter
|
||||
this.m_cacheByteBlock.Write(new ReadOnlySpan<byte>(buffer, offset, length));
|
||||
if (this.UpdateCacheTimeWhenRev)
|
||||
{
|
||||
this.LastCacheTime = DateTime.UtcNow;
|
||||
this.LastCacheTime = DateTimeOffset.UtcNow;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ public abstract class CacheDataHandlingAdapter : SingleStreamDataHandlingAdapter
|
||||
buffer = null;
|
||||
return false;
|
||||
}
|
||||
if (DateTime.UtcNow - this.LastCacheTime > this.CacheTimeout)
|
||||
if (DateTimeOffset.UtcNow - this.LastCacheTime > this.CacheTimeout)
|
||||
{
|
||||
this.m_cacheByteBlock.SafeDispose();
|
||||
this.m_cacheByteBlock = null;
|
||||
@@ -86,7 +86,7 @@ public abstract class CacheDataHandlingAdapter : SingleStreamDataHandlingAdapter
|
||||
byteBlock = null;
|
||||
return false;
|
||||
}
|
||||
if (DateTime.UtcNow - this.LastCacheTime > this.CacheTimeout)
|
||||
if (DateTimeOffset.UtcNow - this.LastCacheTime > this.CacheTimeout)
|
||||
{
|
||||
this.m_cacheByteBlock.SafeDispose();
|
||||
this.m_cacheByteBlock = null;
|
||||
|
||||
@@ -63,7 +63,7 @@ public abstract class CustomDataHandlingAdapter<TRequest> : SingleStreamDataHand
|
||||
public bool TryParseRequest<TByteBlock>(ref TByteBlock byteBlock, out TRequest request) where TByteBlock : IByteBlock
|
||||
{
|
||||
// 检查缓存是否超时,如果超时则清除缓存。
|
||||
if (this.CacheTimeoutEnable && DateTime.UtcNow - this.LastCacheTime > this.CacheTimeout)
|
||||
if (this.CacheTimeoutEnable && DateTimeOffset.UtcNow - this.LastCacheTime > this.CacheTimeout)
|
||||
{
|
||||
this.Reset();
|
||||
}
|
||||
@@ -173,7 +173,7 @@ public abstract class CustomDataHandlingAdapter<TRequest> : SingleStreamDataHand
|
||||
/// <param name="byteBlock"></param>
|
||||
protected override async Task PreviewReceivedAsync(ByteBlock byteBlock)
|
||||
{
|
||||
if (this.CacheTimeoutEnable && DateTime.UtcNow - this.LastCacheTime > this.CacheTimeout)
|
||||
if (this.CacheTimeoutEnable && DateTimeOffset.UtcNow - this.LastCacheTime > this.CacheTimeout)
|
||||
{
|
||||
this.Reset();
|
||||
}
|
||||
@@ -252,7 +252,7 @@ public abstract class CustomDataHandlingAdapter<TRequest> : SingleStreamDataHand
|
||||
// 更新缓存时间。
|
||||
if (this.UpdateCacheTimeWhenRev)
|
||||
{
|
||||
this.LastCacheTime = DateTime.UtcNow;
|
||||
this.LastCacheTime = DateTimeOffset.UtcNow;
|
||||
}
|
||||
request = default;
|
||||
return filterResult;
|
||||
@@ -262,7 +262,7 @@ public abstract class CustomDataHandlingAdapter<TRequest> : SingleStreamDataHand
|
||||
// 对于继续或默认的过滤结果,更新缓存时间。
|
||||
if (this.UpdateCacheTimeWhenRev)
|
||||
{
|
||||
this.LastCacheTime = DateTime.UtcNow;
|
||||
this.LastCacheTime = DateTimeOffset.UtcNow;
|
||||
}
|
||||
request = default;
|
||||
return filterResult;
|
||||
@@ -314,14 +314,14 @@ public abstract class CustomDataHandlingAdapter<TRequest> : SingleStreamDataHand
|
||||
}
|
||||
if (this.UpdateCacheTimeWhenRev)
|
||||
{
|
||||
this.LastCacheTime = DateTime.UtcNow;
|
||||
this.LastCacheTime = DateTimeOffset.UtcNow;
|
||||
}
|
||||
return;
|
||||
|
||||
case FilterResult.GoOn:
|
||||
if (this.UpdateCacheTimeWhenRev)
|
||||
{
|
||||
this.LastCacheTime = DateTime.UtcNow;
|
||||
this.LastCacheTime = DateTimeOffset.UtcNow;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ public class FixedHeaderPackageAdapter : SingleStreamDataHandlingAdapter
|
||||
var buffer = array.Array;
|
||||
var r = byteBlock.Length;
|
||||
|
||||
if (this.CacheTimeoutEnable && DateTime.UtcNow - this.LastCacheTime > this.CacheTimeout)
|
||||
if (this.CacheTimeoutEnable && DateTimeOffset.UtcNow - this.LastCacheTime > this.CacheTimeout)
|
||||
{
|
||||
this.Reset();
|
||||
}
|
||||
@@ -90,7 +90,7 @@ public class FixedHeaderPackageAdapter : SingleStreamDataHandlingAdapter
|
||||
this.m_surPlusLength -= r;
|
||||
if (this.UpdateCacheTimeWhenRev)
|
||||
{
|
||||
this.LastCacheTime = DateTime.UtcNow;
|
||||
this.LastCacheTime = DateTimeOffset.UtcNow;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -274,7 +274,7 @@ public class FixedHeaderPackageAdapter : SingleStreamDataHandlingAdapter
|
||||
Array.Copy(dataBuffer, index, this.m_agreementTempBytes, 0, this.m_agreementTempBytes.Length);
|
||||
if (this.UpdateCacheTimeWhenRev)
|
||||
{
|
||||
this.LastCacheTime = DateTime.UtcNow;
|
||||
this.LastCacheTime = DateTimeOffset.UtcNow;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -312,7 +312,7 @@ public class FixedHeaderPackageAdapter : SingleStreamDataHandlingAdapter
|
||||
this.m_tempByteBlock.Write(new ReadOnlySpan<byte>(dataBuffer, index + (byte)this.FixedHeaderType, recedSurPlusLength));
|
||||
if (this.UpdateCacheTimeWhenRev)
|
||||
{
|
||||
this.LastCacheTime = DateTime.UtcNow;
|
||||
this.LastCacheTime = DateTimeOffset.UtcNow;
|
||||
}
|
||||
}
|
||||
index += (length + (byte)this.FixedHeaderType);
|
||||
|
||||
@@ -58,7 +58,7 @@ public class FixedSizePackageAdapter : SingleStreamDataHandlingAdapter
|
||||
/// <param name="byteBlock"></param>
|
||||
protected override async Task PreviewReceivedAsync(ByteBlock byteBlock)
|
||||
{
|
||||
if (this.CacheTimeoutEnable && DateTime.UtcNow - this.LastCacheTime > this.CacheTimeout)
|
||||
if (this.CacheTimeoutEnable && DateTimeOffset.UtcNow - this.LastCacheTime > this.CacheTimeout)
|
||||
{
|
||||
this.Reset();
|
||||
}
|
||||
@@ -91,7 +91,7 @@ public class FixedSizePackageAdapter : SingleStreamDataHandlingAdapter
|
||||
this.m_surPlusLength -= r;
|
||||
if (this.UpdateCacheTimeWhenRev)
|
||||
{
|
||||
this.LastCacheTime = DateTime.UtcNow;
|
||||
this.LastCacheTime = DateTimeOffset.UtcNow;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -191,7 +191,7 @@ public class FixedSizePackageAdapter : SingleStreamDataHandlingAdapter
|
||||
this.m_tempByteBlock.Write(new ReadOnlySpan<byte>(dataBuffer, index, r - index));
|
||||
if (this.UpdateCacheTimeWhenRev)
|
||||
{
|
||||
this.LastCacheTime = DateTime.UtcNow;
|
||||
this.LastCacheTime = DateTimeOffset.UtcNow;
|
||||
}
|
||||
}
|
||||
index += this.FixedSize;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -22,28 +23,40 @@ namespace TouchSocket.Core;
|
||||
/// </summary>
|
||||
public class PeriodPackageAdapter : SingleStreamDataHandlingAdapter
|
||||
{
|
||||
private readonly ConcurrentQueue<byte[]> m_bytes = new ConcurrentQueue<byte[]>();
|
||||
private long m_count;
|
||||
private readonly ConcurrentQueue<ValueByteBlock> m_bytes = new ConcurrentQueue<ValueByteBlock>();
|
||||
private long m_fireCount;
|
||||
private int m_dataCount;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override Task PreviewReceivedAsync(ByteBlock byteBlock)
|
||||
{
|
||||
this.m_bytes.Enqueue(byteBlock.ToArray());
|
||||
Interlocked.Increment(ref this.m_count);
|
||||
Task.Run(this.DelayGo);
|
||||
var dataLength = byteBlock.Length;
|
||||
var valueByteBlock = new ValueByteBlock(dataLength);
|
||||
valueByteBlock.Write(byteBlock.Span);
|
||||
this.m_bytes.Enqueue(valueByteBlock);
|
||||
Interlocked.Increment(ref this.m_fireCount);
|
||||
Interlocked.Add(ref this.m_dataCount, dataLength);
|
||||
|
||||
this.ThrowIfMoreThanMaxPackageSize(this.m_dataCount);
|
||||
|
||||
_ =EasyTask.SafeRun(this.DelayGo);
|
||||
return EasyTask.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task DelayGo()
|
||||
{
|
||||
await Task.Delay(this.CacheTimeout).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
if (Interlocked.Decrement(ref this.m_count) == 0)
|
||||
if (Interlocked.Decrement(ref this.m_fireCount) == 0)
|
||||
{
|
||||
using (var byteBlock = new ByteBlock())
|
||||
using (var byteBlock = new ByteBlock(this.m_dataCount))
|
||||
{
|
||||
while (this.m_bytes.TryDequeue(out var bytes))
|
||||
while (this.m_bytes.TryDequeue(out var valueByteBlock))
|
||||
{
|
||||
byteBlock.Write(bytes);
|
||||
using (valueByteBlock)
|
||||
{
|
||||
byteBlock.Write(valueByteBlock.Span);
|
||||
Interlocked.Add(ref this.m_dataCount, -valueByteBlock.Length);
|
||||
}
|
||||
}
|
||||
|
||||
byteBlock.SeekToStart();
|
||||
|
||||
@@ -77,7 +77,7 @@ public class TerminatorPackageAdapter : SingleStreamDataHandlingAdapter
|
||||
/// <param name="byteBlock"></param>
|
||||
protected override async Task PreviewReceivedAsync(ByteBlock byteBlock)
|
||||
{
|
||||
if (this.CacheTimeoutEnable && DateTime.UtcNow - this.LastCacheTime > this.CacheTimeout)
|
||||
if (this.CacheTimeoutEnable && DateTimeOffset.UtcNow - this.LastCacheTime > this.CacheTimeout)
|
||||
{
|
||||
this.Reset();
|
||||
}
|
||||
@@ -104,7 +104,7 @@ public class TerminatorPackageAdapter : SingleStreamDataHandlingAdapter
|
||||
this.m_tempByteBlock.Write(new ReadOnlySpan<byte>(buffer, 0, cacheLength));
|
||||
if (this.UpdateCacheTimeWhenRev)
|
||||
{
|
||||
this.LastCacheTime = DateTime.UtcNow;
|
||||
this.LastCacheTime = DateTimeOffset.UtcNow;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -129,7 +129,7 @@ public class TerminatorPackageAdapter : SingleStreamDataHandlingAdapter
|
||||
this.m_tempByteBlock.Write(new ReadOnlySpan<byte>(buffer, startIndex, cacheLength - startIndex));
|
||||
if (this.UpdateCacheTimeWhenRev)
|
||||
{
|
||||
this.LastCacheTime = DateTime.UtcNow;
|
||||
this.LastCacheTime = DateTimeOffset.UtcNow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public abstract class SingleStreamDataHandlingAdapter : DataHandlingAdapter
|
||||
/// <summary>
|
||||
/// 最后缓存的时间
|
||||
/// </summary>
|
||||
protected DateTime LastCacheTime { get; set; }
|
||||
protected DateTimeOffset LastCacheTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 收到数据的切入点,该方法由框架自动调用。
|
||||
@@ -186,6 +186,6 @@ public abstract class SingleStreamDataHandlingAdapter : DataHandlingAdapter
|
||||
/// </summary>
|
||||
protected override void Reset()
|
||||
{
|
||||
this.LastCacheTime = DateTime.UtcNow;
|
||||
this.LastCacheTime = DateTimeOffset.UtcNow;
|
||||
}
|
||||
}
|
||||
@@ -159,7 +159,7 @@ public class SingleStreamDataAdapterTester : DisposableObject
|
||||
{
|
||||
if (block == null)
|
||||
{
|
||||
block = BytePool.Default.GetByteBlock(this.m_bufferLength);
|
||||
block = new ByteBlock(this.m_bufferLength);
|
||||
byteBlocks.Add(block);
|
||||
}
|
||||
var surLen = this.m_bufferLength - block.Position;
|
||||
@@ -204,7 +204,7 @@ public class SingleStreamDataAdapterTester : DisposableObject
|
||||
|
||||
private ByteBlock Write(QueueDataBytes transferByte, ref int offset)
|
||||
{
|
||||
var block = BytePool.Default.GetByteBlock(this.m_bufferLength);
|
||||
var block = new ByteBlock(this.m_bufferLength);
|
||||
var len = Math.Min(transferByte.Length - offset, this.m_bufferLength);
|
||||
block.Write(new ReadOnlySpan<byte>(transferByte.Buffer, offset, len));
|
||||
offset += len;
|
||||
|
||||
@@ -148,7 +148,7 @@ public class SingleStreamDataAdapterTester<TAdapter, TRequest> : DisposableObjec
|
||||
{
|
||||
if (block == null)
|
||||
{
|
||||
block = BytePool.Default.GetByteBlock(this.m_bufferLength);
|
||||
block = new ByteBlock(this.m_bufferLength);
|
||||
byteBlocks.Add(block);
|
||||
}
|
||||
var surLen = this.m_bufferLength - block.Position;
|
||||
@@ -193,7 +193,7 @@ public class SingleStreamDataAdapterTester<TAdapter, TRequest> : DisposableObjec
|
||||
|
||||
private ByteBlock Write(QueueDataBytes transferByte, ref int offset)
|
||||
{
|
||||
var block = BytePool.Default.GetByteBlock(this.m_bufferLength);
|
||||
var block = new ByteBlock(this.m_bufferLength);
|
||||
var len = Math.Min(transferByte.Length - offset, this.m_bufferLength);
|
||||
block.Write(new ReadOnlySpan<byte>(transferByte.Buffer, offset, len));
|
||||
offset += len;
|
||||
|
||||
@@ -220,4 +220,27 @@ public static class CollectionsExtension
|
||||
return values;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region System.Collections.Generic Queue
|
||||
|
||||
#if !(NET6_0_OR_GREATER||NETSTANDARD2_1_OR_GREATER)
|
||||
/// <summary>
|
||||
/// 尝试从队列中移除并返回位于开头的对象。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">队列中元素的类型。</typeparam>
|
||||
/// <param name="queue">要操作的队列。</param>
|
||||
/// <param name="result">如果移除成功,则包含移除的对象;否则为默认值。</param>
|
||||
/// <returns>如果成功移除并返回了对象,则为 true;否则为 false。</returns>
|
||||
public static bool TryDequeue<T>(this Queue<T> queue, out T result)
|
||||
{
|
||||
if (queue.Count > 0)
|
||||
{
|
||||
result = queue.Dequeue();
|
||||
return true;
|
||||
}
|
||||
result = default(T);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
}
|
||||
@@ -12,8 +12,10 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace TouchSocket.Core;
|
||||
|
||||
@@ -87,6 +89,16 @@ public static class ReflectionExtension
|
||||
(TouchSocketCoreUtility.NullableType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取参数的描述信息。
|
||||
/// </summary>
|
||||
/// <param name="parameterInfo">参数信息。</param>
|
||||
/// <returns>返回描述信息,如果没有描述信息则返回默认值。</returns>
|
||||
public static string GetDescription(this ParameterInfo parameterInfo)
|
||||
{
|
||||
return parameterInfo.GetCustomAttribute<DescriptionAttribute>()?.Description ?? default;
|
||||
}
|
||||
|
||||
#endregion ParameterInfo
|
||||
|
||||
#region MemberInfo
|
||||
@@ -103,6 +115,16 @@ public static class ReflectionExtension
|
||||
//return ((dynamic)memberInfo.GetCustomAttribute(Type.GetType("System.Runtime.CompilerServices.TupleElementNamesAttribute")))?.TransformNames;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取参数的描述信息。
|
||||
/// </summary>
|
||||
/// <param name="memberInfo">参数信息。</param>
|
||||
/// <returns>返回描述信息,如果没有描述信息则返回默认值。</returns>
|
||||
public static string GetDescription(this MemberInfo memberInfo)
|
||||
{
|
||||
return memberInfo.GetCustomAttribute<DescriptionAttribute>()?.Description ?? default;
|
||||
}
|
||||
|
||||
#endregion MemberInfo
|
||||
|
||||
#region FieldInfo
|
||||
|
||||
@@ -14,6 +14,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
@@ -31,11 +32,6 @@ public static class StringExtension
|
||||
/// </summary>
|
||||
public const string DefaultSpaceString = " ";
|
||||
|
||||
/// <summary>
|
||||
/// 默认的空格字符串的UTF-8表示。
|
||||
/// </summary>
|
||||
public static ReadOnlySpan<byte> DefaultSpaceUtf8Span => " "u8;
|
||||
|
||||
/// <summary>
|
||||
/// 默认的rn字符串的UTF-8表示。
|
||||
/// </summary>
|
||||
@@ -46,6 +42,11 @@ public static class StringExtension
|
||||
/// </summary>
|
||||
public static ReadOnlySpan<byte> Default_RNRN_Utf8Span => "\r\n\r\n"u8;
|
||||
|
||||
/// <summary>
|
||||
/// 默认的空格字符串的UTF-8表示。
|
||||
/// </summary>
|
||||
public static ReadOnlySpan<byte> DefaultSpaceUtf8Span => " "u8;
|
||||
|
||||
/// <summary>
|
||||
/// 从Base64转到数组。
|
||||
/// </summary>
|
||||
@@ -176,6 +177,7 @@ public static class StringExtension
|
||||
/// <param name="value"></param>
|
||||
/// <param name="destinationType">目标类型必须为基础类型</param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="NotSupportedException">类型转换失败</exception>
|
||||
public static object ParseToType(this string value, Type destinationType)
|
||||
{
|
||||
if (TryParseToType(value, destinationType, out var returnValue))
|
||||
@@ -186,6 +188,23 @@ public static class StringExtension
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字符串解析为指定的类型。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">目标类型。</typeparam>
|
||||
/// <param name="value">要解析的字符串。</param>
|
||||
/// <returns>解析后的目标类型对象。</returns>
|
||||
/// <exception cref="NotSupportedException">类型转换失败</exception>
|
||||
public static T ParseToType<T>(this string value)
|
||||
{
|
||||
if (TryParseToType<T>(value, out var returnValue))
|
||||
{
|
||||
return returnValue;
|
||||
}
|
||||
ThrowHelper.ThrowNotSupportedException(TouchSocketCoreResource.StringParseToTypeFail.Format(value, typeof(T)));
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除字符串末尾指定数量的字符。
|
||||
/// </summary>
|
||||
@@ -411,4 +430,172 @@ public static class StringExtension
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///<summary>
|
||||
/// 尝试将字符串解析为指定的类型。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">目标类型。</typeparam>
|
||||
/// <param name="value">要解析的字符串。</param>
|
||||
/// <param name="returnValue">解析后的值,输出参数。</param>
|
||||
/// <returns>如果解析成功返回 true,否则返回 false。</returns>
|
||||
public static bool TryParseToType<T>(string value, out T returnValue)
|
||||
{
|
||||
// 处理空或全空格字符串
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
returnValue = default;
|
||||
return true;
|
||||
}
|
||||
|
||||
var type = typeof(T);
|
||||
|
||||
// 处理枚举类型
|
||||
if (type.IsEnum)
|
||||
{
|
||||
#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
|
||||
bool success = Enum.TryParse(type, value, out object enumResult);
|
||||
if (success)
|
||||
{
|
||||
returnValue = (T)enumResult;
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
try
|
||||
{
|
||||
var enumResult = Enum.Parse(type, value);
|
||||
returnValue = (T)enumResult;
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
#endif
|
||||
returnValue = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 根据 TypeCode 处理基础类型
|
||||
switch (Type.GetTypeCode(type))
|
||||
{
|
||||
case TypeCode.Boolean:
|
||||
if (bool.TryParse(value, out bool boolResult))
|
||||
{
|
||||
returnValue = Unsafe.As<bool, T>(ref boolResult);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeCode.Char:
|
||||
if (char.TryParse(value, out char charResult))
|
||||
{
|
||||
returnValue = Unsafe.As<char, T>(ref charResult);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeCode.SByte:
|
||||
if (sbyte.TryParse(value, out sbyte sbyteResult))
|
||||
{
|
||||
returnValue = Unsafe.As<sbyte, T>(ref sbyteResult);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeCode.Byte:
|
||||
if (byte.TryParse(value, out byte byteResult))
|
||||
{
|
||||
returnValue = Unsafe.As<byte, T>(ref byteResult);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeCode.Int16:
|
||||
if (short.TryParse(value, out short shortResult))
|
||||
{
|
||||
returnValue = Unsafe.As<short, T>(ref shortResult);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeCode.UInt16:
|
||||
if (ushort.TryParse(value, out ushort ushortResult))
|
||||
{
|
||||
returnValue = Unsafe.As<ushort, T>(ref ushortResult);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeCode.Int32:
|
||||
if (int.TryParse(value, out int intResult))
|
||||
{
|
||||
returnValue = Unsafe.As<int, T>(ref intResult);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeCode.UInt32:
|
||||
if (uint.TryParse(value, out uint uintResult))
|
||||
{
|
||||
returnValue = Unsafe.As<uint, T>(ref uintResult);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeCode.Int64:
|
||||
if (long.TryParse(value, out long longResult))
|
||||
{
|
||||
returnValue = Unsafe.As<long, T>(ref longResult);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeCode.UInt64:
|
||||
if (ulong.TryParse(value, out var ulongResult))
|
||||
{
|
||||
returnValue = Unsafe.As<ulong, T>(ref ulongResult);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeCode.Single:
|
||||
if (float.TryParse(value, out var floatResult))
|
||||
{
|
||||
returnValue = Unsafe.As<float, T>(ref floatResult);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeCode.Double:
|
||||
if (double.TryParse(value, out var doubleResult))
|
||||
{
|
||||
returnValue = Unsafe.As<double, T>(ref doubleResult);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeCode.Decimal:
|
||||
if (decimal.TryParse(value, out var decimalResult))
|
||||
{
|
||||
returnValue = Unsafe.As<decimal, T>(ref decimalResult);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeCode.DateTime:
|
||||
if (DateTime.TryParse(value, out var dateResult))
|
||||
{
|
||||
returnValue = Unsafe.As<DateTime, T>(ref dateResult);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeCode.String:
|
||||
returnValue = Unsafe.As<string, T>(ref value);
|
||||
return true;
|
||||
}
|
||||
|
||||
returnValue = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Buffers;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TouchSocket.Core;
|
||||
@@ -791,6 +792,17 @@ public static class SystemExtension
|
||||
return dt.ToString("r", CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将DateTimeOffset对象转换为GMT格式的字符串。
|
||||
/// </summary>
|
||||
/// <param name="dt">要转换的DateTime对象。</param>
|
||||
/// <returns>转换后的GMT格式字符串。</returns>
|
||||
public static string ToGMTString(this DateTimeOffset dt)
|
||||
{
|
||||
// 使用"r"格式字符串和InvariantCulture确保GMT格式的正确性
|
||||
return dt.ToString("r", CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将DateTime对象转换为自1970年1月1日以来的毫秒数的32位无符号整数表示。
|
||||
/// </summary>
|
||||
@@ -825,12 +837,16 @@ public static class SystemExtension
|
||||
/// <param name="memory">要读取数据到的内存区域。</param>
|
||||
/// <param name="cancellationToken">用于取消操作的令牌。</param>
|
||||
/// <returns>读取到的数据长度。</returns>
|
||||
public static Task<int> ReadAsync(this Stream stream, Memory<byte> memory, CancellationToken cancellationToken)
|
||||
public static async Task<int> ReadAsync(this Stream stream, Memory<byte> memory, CancellationToken cancellationToken)
|
||||
{
|
||||
if (memory.IsEmpty)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
// 获取内存区域对应的数组
|
||||
var bytes = memory.GetArray();
|
||||
// 调用异步方法读取数据到指定的数组区域
|
||||
return stream.ReadAsync(bytes.Array, bytes.Offset, bytes.Count);
|
||||
return await stream.ReadAsync(bytes.Array, bytes.Offset, bytes.Count).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -841,10 +857,14 @@ public static class SystemExtension
|
||||
/// <returns>读取到的数据长度。</returns>
|
||||
public static int Read(this Stream stream, Span<byte> span)
|
||||
{
|
||||
if (span.IsEmpty)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
// 获取字节跨度的长度
|
||||
var len = span.Length;
|
||||
// 从字节池中租用一个缓冲区
|
||||
var buffer = BytePool.Default.Rent(len);
|
||||
var buffer = ArrayPool<byte>.Shared.Rent(len);
|
||||
try
|
||||
{
|
||||
// 从流中读取数据到缓冲区
|
||||
@@ -857,7 +877,7 @@ public static class SystemExtension
|
||||
finally
|
||||
{
|
||||
// 将缓冲区归还到字节池
|
||||
BytePool.Default.Return(buffer);
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -870,7 +890,7 @@ public static class SystemExtension
|
||||
/// <remarks>
|
||||
/// 此方法利用内存块的 GetArray 方法获取数组段信息,然后使用现有的 WriteAsync 方法异步地将内容写入流中,提高了写入操作的效率和灵活性。
|
||||
/// </remarks>
|
||||
public static async ValueTask WriteAsync(this Stream stream, ReadOnlyMemory<byte> memory, CancellationToken token)
|
||||
public static async Task WriteAsync(this Stream stream, ReadOnlyMemory<byte> memory, CancellationToken token)
|
||||
{
|
||||
var segment = memory.GetArray();
|
||||
await stream.WriteAsync(segment.Array, segment.Offset, segment.Count, token);
|
||||
@@ -886,10 +906,14 @@ public static class SystemExtension
|
||||
/// </remarks>
|
||||
public static void Write(this Stream stream, ReadOnlySpan<byte> span)
|
||||
{
|
||||
if (span.IsEmpty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// 获取字节跨度的长度
|
||||
var len = span.Length;
|
||||
// 从字节池中租用一个缓冲区
|
||||
var buffer = BytePool.Default.Rent(len);
|
||||
var buffer = ArrayPool<byte>.Shared.Rent(len);
|
||||
try
|
||||
{
|
||||
// 将字节跨度的内容复制到租用的缓冲区中
|
||||
@@ -901,7 +925,7 @@ public static class SystemExtension
|
||||
finally
|
||||
{
|
||||
// 将使用完的缓冲区归还到字节池,以便其他操作重用
|
||||
BytePool.Default.Return(buffer);
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
@@ -220,5 +221,90 @@ public static class SystemThreadingExtension
|
||||
task.ContinueWith(t => GC.KeepAlive(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
|
||||
}
|
||||
|
||||
#endregion Task
|
||||
/// <summary>
|
||||
/// 如果给定的 <see cref="CancellationToken"/> 被取消,则取消 <see cref="TaskCompletionSource{TResult}.Task"/>。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">成功完成的 <see cref="Task{TResult}"/> 返回的值的类型。</typeparam>
|
||||
/// <param name="taskCompletionSource">要取消的 <see cref="TaskCompletionSource{TResult}"/>。</param>
|
||||
/// <param name="cancellationToken">用于取消的 <see cref="CancellationToken"/>。</param>
|
||||
public static void AttachCancellation<T>(this TaskCompletionSource<T> taskCompletionSource, CancellationToken cancellationToken)
|
||||
{
|
||||
if (taskCompletionSource == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(taskCompletionSource));
|
||||
}
|
||||
|
||||
if (cancellationToken.CanBeCanceled && !taskCompletionSource.Task.IsCompleted)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
taskCompletionSource.TrySetCanceled();
|
||||
}
|
||||
else
|
||||
{
|
||||
var tuple = new CancelableTaskCompletionSource<T>(taskCompletionSource, cancellationToken);
|
||||
tuple.CancellationTokenRegistration = cancellationToken.Register(
|
||||
s =>
|
||||
{
|
||||
var t = (CancelableTaskCompletionSource<T>)s!;
|
||||
if (t.TaskCompletionSource.TrySetCanceled())
|
||||
{
|
||||
}
|
||||
},
|
||||
tuple,
|
||||
useSynchronizationContext: false);
|
||||
|
||||
taskCompletionSource.Task.ContinueWith(
|
||||
(_, s) =>
|
||||
{
|
||||
var t = (CancelableTaskCompletionSource<T>)s!;
|
||||
if (t.ContinuationScheduled || !t.OnOwnerThread)
|
||||
{
|
||||
t.CancellationTokenRegistration.Dispose();
|
||||
}
|
||||
else if (!t.CancellationToken.IsCancellationRequested)
|
||||
{
|
||||
ThreadPool.QueueUserWorkItem(
|
||||
s2 =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var t2 = (CancelableTaskCompletionSource<T>)s2!;
|
||||
t2.CancellationTokenRegistration.Dispose();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
},
|
||||
s);
|
||||
}
|
||||
},
|
||||
tuple,
|
||||
CancellationToken.None,
|
||||
TaskContinuationOptions.ExecuteSynchronously,
|
||||
TaskScheduler.Default);
|
||||
tuple.ContinuationScheduled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 表示一个可取消的 TaskCompletionSource。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">成功完成的 <see cref="Task{TResult}"/> 返回的值的类型。</typeparam>
|
||||
private class CancelableTaskCompletionSource<T>
|
||||
{
|
||||
public TaskCompletionSource<T> TaskCompletionSource { get; }
|
||||
public CancellationToken CancellationToken { get; }
|
||||
public CancellationTokenRegistration CancellationTokenRegistration { get; set; }
|
||||
public bool ContinuationScheduled { get; set; }
|
||||
public bool OnOwnerThread => Thread.CurrentThread.IsThreadPoolThread;
|
||||
|
||||
public CancelableTaskCompletionSource(TaskCompletionSource<T> taskCompletionSource, CancellationToken cancellationToken)
|
||||
{
|
||||
TaskCompletionSource = taskCompletionSource;
|
||||
CancellationToken = cancellationToken;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@@ -28,7 +28,7 @@ public class Counter
|
||||
/// <summary>
|
||||
/// 最后一次递增时间
|
||||
/// </summary>
|
||||
protected DateTime m_lastIncrement;
|
||||
protected DateTimeOffset m_lastIncrement;
|
||||
|
||||
/// <summary>
|
||||
/// 周期内的累计计数值。
|
||||
@@ -38,7 +38,7 @@ public class Counter
|
||||
/// <summary>
|
||||
/// 最后一次递增时间
|
||||
/// </summary>
|
||||
public DateTime LastIncrement => this.m_lastIncrement;
|
||||
public DateTimeOffset LastIncrement => this.m_lastIncrement;
|
||||
|
||||
/// <summary>
|
||||
/// 当达到一个周期时触发。
|
||||
@@ -61,7 +61,7 @@ public class Counter
|
||||
bool isPeriod;
|
||||
|
||||
// 获取当前时间
|
||||
var dateTime = DateTime.UtcNow;
|
||||
var dateTime = DateTimeOffset.UtcNow;
|
||||
|
||||
// 检查自上次递增以来是否超过了设定的周期时间
|
||||
if (dateTime - this.LastIncrement > this.Period)
|
||||
|
||||
@@ -41,7 +41,7 @@ public class FlowGate : Counter
|
||||
{
|
||||
if (this.m_count > this.Maximum)
|
||||
{
|
||||
var time = (DateTime.UtcNow - this.LastIncrement);
|
||||
var time = (DateTimeOffset.UtcNow - this.LastIncrement);
|
||||
var waitTime = this.Period - time <= TimeSpan.Zero ? TimeSpan.Zero : (this.GetBaseTime() - time);
|
||||
waitTime = waitTime < TimeSpan.Zero ? TimeSpan.Zero : waitTime;
|
||||
Thread.Sleep(waitTime);
|
||||
@@ -67,7 +67,7 @@ public class FlowGate : Counter
|
||||
if (this.m_count > this.Maximum)
|
||||
{
|
||||
// 计算自上次增加以来的时间差
|
||||
var time = (DateTime.UtcNow - this.LastIncrement);
|
||||
var time = (DateTimeOffset.UtcNow - this.LastIncrement);
|
||||
// 计算还需要等待的时间,确保等待时间不为负
|
||||
var waitTime = this.Period - time <= TimeSpan.Zero ? TimeSpan.Zero : (this.GetBaseTime() - time);
|
||||
// 将等待时间小于0的情况调整为0
|
||||
|
||||
@@ -35,6 +35,9 @@ public abstract class FlowOperator
|
||||
|
||||
private long m_speedTemp;
|
||||
|
||||
/// <summary>
|
||||
/// 流量控制器。
|
||||
/// </summary>
|
||||
public FlowOperator()
|
||||
{
|
||||
this.MaxSpeed = long.MaxValue;
|
||||
|
||||
@@ -28,7 +28,7 @@ public struct ValueCounter
|
||||
/// <summary>
|
||||
/// 最后一次递增时间
|
||||
/// </summary>
|
||||
private DateTime m_lastIncrement;
|
||||
private DateTimeOffset m_lastIncrement;
|
||||
|
||||
/// <summary>
|
||||
/// 周期内的累计计数值。
|
||||
@@ -38,7 +38,7 @@ public struct ValueCounter
|
||||
/// <summary>
|
||||
/// 最后一次递增时间
|
||||
/// </summary>
|
||||
public readonly DateTime LastIncrement => this.m_lastIncrement;
|
||||
public readonly DateTimeOffset LastIncrement => this.m_lastIncrement;
|
||||
|
||||
/// <summary>
|
||||
/// 当达到一个周期时触发。
|
||||
@@ -61,7 +61,7 @@ public struct ValueCounter
|
||||
bool isPeriod;
|
||||
|
||||
// 获取当前时间
|
||||
var dateTime = DateTime.UtcNow;
|
||||
var dateTime = DateTimeOffset.UtcNow;
|
||||
|
||||
// 判断自上次递增以来是否超过了设定的周期时间
|
||||
if (dateTime - this.LastIncrement > this.Period)
|
||||
|
||||
@@ -13,113 +13,64 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks.Sources;
|
||||
|
||||
namespace TouchSocket.Core;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 表示一个块段,用于异步操作中作为值任务的源,提供 <see cref="IBlockResult{T}"/> 类型的结果。
|
||||
/// 表示一个块段,用于异步操作中作为值任务的源,提供 <see cref="IBlockResult"/> 类型的结果。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">块段中元素的类型。</typeparam>
|
||||
public abstract class BlockSegment<T> : ValueTaskSource<IBlockResult<T>>
|
||||
/// <typeparam name="TBlockResult">块段中元素的类型,必须实现 <see cref="IBlockResult"/> 接口。</typeparam>
|
||||
public abstract class BlockSegment<TBlockResult> : DisposableObject, IValueTaskSource<TBlockResult>
|
||||
where TBlockResult : IBlockResult
|
||||
{
|
||||
#region 字段
|
||||
|
||||
private readonly AsyncAutoResetEvent m_resetEventForCompleteRead = new AsyncAutoResetEvent(false);
|
||||
private readonly BlockResult m_result;
|
||||
private readonly SemaphoreSlim m_resetEventForCompleteRead = new(0, 1);
|
||||
private CancellationTokenRegistration m_tokenRegistration;
|
||||
private ManualResetValueTaskSourceCore<TBlockResult> m_valueTaskSourceCore;
|
||||
private TBlockResult m_blockResult;
|
||||
|
||||
#endregion 字段
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前块段的结果。
|
||||
/// 初始化 <see cref="BlockSegment{TBlockResult}"/> 类的新实例。
|
||||
/// </summary>
|
||||
public IBlockResult<T> Result => this.m_result;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化BlockSegment类的新实例。
|
||||
/// </summary>
|
||||
public BlockSegment()
|
||||
/// <param name="runContinuationsAsynchronously">指示是否异步运行延续。</param>
|
||||
public BlockSegment(bool runContinuationsAsynchronously = false)
|
||||
{
|
||||
// 初始化块结果,与CompleteRead方法关联
|
||||
this.m_result = new BlockResult(this.CompleteRead);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重置块段的状态,为下一次使用做准备。
|
||||
/// </summary>
|
||||
protected override void Reset()
|
||||
{
|
||||
// 重置等待读取完成的事件
|
||||
this.m_resetEventForCompleteRead.Reset();
|
||||
// 将块结果标记为未完成
|
||||
this.m_result.IsCompleted = false;
|
||||
// 清除结果中的内存数据
|
||||
this.m_result.Memory = default;
|
||||
// 清除结果中的消息
|
||||
this.m_result.Message = default;
|
||||
base.Reset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 调度执行指定操作。
|
||||
/// </summary>
|
||||
/// <param name="action">要执行的操作。</param>
|
||||
/// <param name="state">操作的状态信息。</param>
|
||||
protected override void Scheduler(Action<object> action, object state)
|
||||
{
|
||||
// 定义一个局部函数来运行传递的操作
|
||||
void Run(object o)
|
||||
m_valueTaskSourceCore = new ManualResetValueTaskSourceCore<TBlockResult>()
|
||||
{
|
||||
action.Invoke(o);
|
||||
}
|
||||
// 不安全地将工作项加入线程池队列
|
||||
ThreadPool.UnsafeQueueUserWorkItem(Run, state);
|
||||
RunContinuationsAsynchronously = runContinuationsAsynchronously
|
||||
};
|
||||
|
||||
this.m_blockResult = this.CreateResult(this.CompleteRead);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前块段的结果。
|
||||
/// 取消当前操作。
|
||||
/// </summary>
|
||||
/// <returns>块段的结果。</returns>
|
||||
protected override IBlockResult<T> GetResult()
|
||||
protected void Cancel()
|
||||
{
|
||||
// 返回内部结果对象
|
||||
return this.m_result;
|
||||
this.m_valueTaskSourceCore.SetException(new OperationCanceledException());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步地输入数据到块段中。
|
||||
/// 完成读取操作。
|
||||
/// </summary>
|
||||
/// <param name="memory">要输入的数据内存。</param>
|
||||
protected async Task InputAsync(ReadOnlyMemory<T> memory)
|
||||
protected virtual void CompleteRead()
|
||||
{
|
||||
// 设置结果中的内存数据
|
||||
this.m_result.Memory = memory;
|
||||
// 标记为未完成
|
||||
this.Complete(false);
|
||||
// 等待读取完成
|
||||
await this.m_resetEventForCompleteRead.WaitOneAsync().ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
this.m_valueTaskSourceCore.Reset();
|
||||
this.m_resetEventForCompleteRead.Release();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标记块段为完成,并可选地提供完成消息。
|
||||
/// 创建块段结果。
|
||||
/// </summary>
|
||||
/// <param name="msg">完成消息。</param>
|
||||
protected async Task Complete(string msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 将结果标记为已完成
|
||||
this.m_result.IsCompleted = true;
|
||||
// 设置完成消息
|
||||
this.m_result.Message = msg;
|
||||
// 触发输入操作,以应用完成状态
|
||||
await this.InputAsync(default).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 异常情况下,不做处理
|
||||
}
|
||||
}
|
||||
/// <param name="actionForDispose">用于释放的操作。</param>
|
||||
/// <returns>块段结果。</returns>
|
||||
protected abstract TBlockResult CreateResult(Action actionForDispose);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
@@ -130,41 +81,277 @@ public abstract class BlockSegment<T> : ValueTaskSource<IBlockResult<T>>
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
this.m_resetEventForCompleteRead.Set();
|
||||
this.m_resetEventForCompleteRead.SafeDispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private void CompleteRead()
|
||||
/// <summary>
|
||||
/// 异步读取块段。
|
||||
/// </summary>
|
||||
/// <param name="token">取消令牌。</param>
|
||||
/// <returns>值任务,表示异步读取操作。</returns>
|
||||
protected ValueTask<TBlockResult> ProtectedReadAsync(CancellationToken token)
|
||||
{
|
||||
this.m_resetEventForCompleteRead.Set();
|
||||
}
|
||||
this.ThrowIfDisposed();
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
#region Class
|
||||
|
||||
internal class BlockResult : IBlockResult<T>
|
||||
{
|
||||
private readonly Action m_disAction;
|
||||
|
||||
/// <summary>
|
||||
/// ReceiverResult
|
||||
/// </summary>
|
||||
/// <param name="disAction"></param>
|
||||
public BlockResult(Action disAction)
|
||||
if (this.m_blockResult.IsCompleted)
|
||||
{
|
||||
this.m_disAction = disAction;
|
||||
return EasyValueTask.FromResult<TBlockResult>(this.m_blockResult);
|
||||
}
|
||||
|
||||
public ReadOnlyMemory<T> Memory { get; set; }
|
||||
public bool IsCompleted { get; set; }
|
||||
public string Message { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
if (token.CanBeCanceled)
|
||||
{
|
||||
this.m_disAction.Invoke();
|
||||
if (this.m_tokenRegistration == default)
|
||||
{
|
||||
this.m_tokenRegistration = token.Register(this.Cancel);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.m_tokenRegistration.Dispose();
|
||||
this.m_tokenRegistration = token.Register(this.Cancel);
|
||||
}
|
||||
}
|
||||
|
||||
return new ValueTask<TBlockResult>(this, this.m_valueTaskSourceCore.Version);
|
||||
}
|
||||
|
||||
#endregion Class
|
||||
}
|
||||
/// <summary>
|
||||
/// 设置异常。
|
||||
/// </summary>
|
||||
/// <param name="ex">异常实例。</param>
|
||||
protected void SetException(Exception ex)
|
||||
{
|
||||
this.m_valueTaskSourceCore.SetException(ex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 触发异步操作。
|
||||
/// </summary>
|
||||
/// <returns>表示异步操作的任务。</returns>
|
||||
protected async Task TriggerAsync()
|
||||
{
|
||||
this.m_valueTaskSourceCore.SetResult(this.m_blockResult);
|
||||
await this.m_resetEventForCompleteRead.WaitAsync().ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
#region IValueTaskSource
|
||||
|
||||
/// <inheritdoc/>
|
||||
TBlockResult IValueTaskSource<TBlockResult>.GetResult(short token)
|
||||
{
|
||||
return this.m_valueTaskSourceCore.GetResult(token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
ValueTaskSourceStatus IValueTaskSource<TBlockResult>.GetStatus(short token)
|
||||
{
|
||||
return this.m_valueTaskSourceCore.GetStatus(token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
void IValueTaskSource<TBlockResult>.OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags)
|
||||
{
|
||||
this.m_valueTaskSourceCore.OnCompleted(continuation, state, token, flags);
|
||||
}
|
||||
|
||||
#endregion IValueTaskSource
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// 表示一个块段,用于异步操作中作为值任务的源,提供 <see cref="IBlockResult{T}"/> 类型的结果。
|
||||
///// </summary>
|
||||
///// <typeparam name="T">块段中元素的类型。</typeparam>
|
||||
//public abstract class BlockSegment<T> : DisposableObject, IValueTaskSource<IBlockResult<T>>
|
||||
//{
|
||||
// public static readonly IBlockResult<T> Completed = new InternalCompletedBlockResult();
|
||||
|
||||
// #region 字段
|
||||
|
||||
// private readonly AsyncAutoResetEvent m_resetEventForCompleteRead = new AsyncAutoResetEvent(false);
|
||||
// private readonly InternalBlockResult m_result;
|
||||
// private CancellationTokenRegistration m_tokenRegistration;
|
||||
// private ManualResetValueTaskSourceCore<IBlockResult<T>> m_valueTaskSourceCore;
|
||||
|
||||
// #endregion 字段
|
||||
|
||||
// /// <summary>
|
||||
// /// 初始化BlockSegment类的新实例。
|
||||
// /// </summary>
|
||||
// public BlockSegment(bool runContinuationsAsynchronously = false)
|
||||
// {
|
||||
// m_valueTaskSourceCore = new ManualResetValueTaskSourceCore<IBlockResult<T>>()
|
||||
// {
|
||||
// RunContinuationsAsynchronously = runContinuationsAsynchronously
|
||||
// };
|
||||
// // 初始化块结果,与CompleteRead方法关联
|
||||
// this.m_result = new InternalBlockResult(this.CompleteRead);
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// 获取当前块段的结果。
|
||||
// /// </summary>
|
||||
// protected IBlockResult<T> BlockResult => this.m_result;
|
||||
|
||||
// public static IBlockResult<T> FromResult(T result)
|
||||
// {
|
||||
// return new InternalBlockResult(() => { }) { IsCompleted = true };
|
||||
// }
|
||||
|
||||
// protected void Cancel()
|
||||
// {
|
||||
// this.m_valueTaskSourceCore.SetException(new OperationCanceledException());
|
||||
// }
|
||||
|
||||
// /// <inheritdoc/>
|
||||
// protected override void Dispose(bool disposing)
|
||||
// {
|
||||
// if (this.DisposedValue)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// if (disposing)
|
||||
// {
|
||||
// this.m_resetEventForCompleteRead.Set();
|
||||
// this.m_resetEventForCompleteRead.SafeDispose();
|
||||
// }
|
||||
// base.Dispose(disposing);
|
||||
// }
|
||||
|
||||
// protected async Task InputAsync(T result)
|
||||
// {
|
||||
// // 设置结果中的内存数据
|
||||
// this.m_result.Result = result;
|
||||
|
||||
// this.m_valueTaskSourceCore.SetResult(this.m_result);
|
||||
// // 等待读取完成
|
||||
// await this.m_resetEventForCompleteRead.WaitOneAsync().ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
// }
|
||||
|
||||
// protected async Task<Result> ProtectedComplete(string msg)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// this.m_result.IsCompleted = true;
|
||||
// this.m_result.Message = msg;
|
||||
// await this.InputAsync(default).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
// return Result.Success;
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// return Result.FromException(ex);
|
||||
// }
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// 重置块段的状态,为下一次使用做准备。
|
||||
// /// </summary>
|
||||
// protected virtual void Reset()
|
||||
// {
|
||||
// // 重置等待读取完成的事件
|
||||
// this.m_resetEventForCompleteRead.Reset();
|
||||
// // 将块结果标记为未完成
|
||||
// this.m_result.IsCompleted = false;
|
||||
// // 清除结果中的内存数据
|
||||
// this.m_result.Result = default;
|
||||
// // 清除结果中的消息
|
||||
// this.m_result.Message = default;
|
||||
// this.m_valueTaskSourceCore.Reset();
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// 值等待异步操作。
|
||||
// /// </summary>
|
||||
// /// <param name="token">取消令牌。</param>
|
||||
// /// <returns>值任务。</returns>
|
||||
// protected ValueTask<IBlockResult<T>> ValueWaitAsync(CancellationToken token)
|
||||
// {
|
||||
// this.ThrowIfDisposed();
|
||||
// token.ThrowIfCancellationRequested();
|
||||
|
||||
// if (this.m_result.IsCompleted)
|
||||
// {
|
||||
// return EasyValueTask.FromResult<IBlockResult<T>>(this.m_result);
|
||||
// }
|
||||
|
||||
// if (token.CanBeCanceled)
|
||||
// {
|
||||
// if (this.m_tokenRegistration == default)
|
||||
// {
|
||||
// this.m_tokenRegistration = token.Register(this.Cancel);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// this.m_tokenRegistration.Dispose();
|
||||
// this.m_tokenRegistration = token.Register(this.Cancel);
|
||||
// }
|
||||
// }
|
||||
// this.Reset();
|
||||
// return new ValueTask<IBlockResult<T>>(this, this.m_valueTaskSourceCore.Version);
|
||||
// }
|
||||
|
||||
// private void CompleteRead()
|
||||
// {
|
||||
// this.m_resetEventForCompleteRead.Set();
|
||||
// }
|
||||
|
||||
// #region IValueTaskSource
|
||||
|
||||
// IBlockResult<T> IValueTaskSource<IBlockResult<T>>.GetResult(short token)
|
||||
// {
|
||||
// return this.m_valueTaskSourceCore.GetResult(token);
|
||||
// }
|
||||
|
||||
// ValueTaskSourceStatus IValueTaskSource<IBlockResult<T>>.GetStatus(short token)
|
||||
// {
|
||||
// return this.m_valueTaskSourceCore.GetStatus(token);
|
||||
// }
|
||||
|
||||
// void IValueTaskSource<IBlockResult<T>>.OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags)
|
||||
// {
|
||||
// this.m_valueTaskSourceCore.OnCompleted(continuation, state, token, flags);
|
||||
// }
|
||||
|
||||
// #endregion IValueTaskSource
|
||||
|
||||
// #region Class
|
||||
|
||||
// internal sealed class InternalBlockResult : IBlockResult<T>
|
||||
// {
|
||||
// private readonly Action m_disAction;
|
||||
|
||||
// /// <summary>
|
||||
// /// ReceiverResult
|
||||
// /// </summary>
|
||||
// /// <param name="disAction"></param>
|
||||
// public InternalBlockResult(Action disAction)
|
||||
// {
|
||||
// this.m_disAction = disAction;
|
||||
// }
|
||||
|
||||
// public bool IsCompleted { get; set; }
|
||||
// public string Message { get; set; }
|
||||
|
||||
// public T Result { get; set; }
|
||||
|
||||
// public void Dispose()
|
||||
// {
|
||||
// this.m_disAction.Invoke();
|
||||
// }
|
||||
// }
|
||||
|
||||
// private sealed class InternalCompletedBlockResult : IBlockResult<T>
|
||||
// {
|
||||
// public bool IsCompleted => true;
|
||||
// public string Message => string.Empty;
|
||||
// public T Result => default;
|
||||
|
||||
// public void Dispose()
|
||||
// {
|
||||
// }
|
||||
// }
|
||||
|
||||
// #endregion Class
|
||||
//}
|
||||
@@ -285,7 +285,7 @@ public static partial class FilePool
|
||||
var keys = new List<string>();
|
||||
foreach (var item in m_pathStorage)
|
||||
{
|
||||
if (DateTime.UtcNow - item.Value.AccessTime > item.Value.AccessTimeout)
|
||||
if (DateTimeOffset.UtcNow - item.Value.AccessTime > item.Value.AccessTimeout)
|
||||
{
|
||||
keys.Add(item.Key);
|
||||
}
|
||||
|
||||
@@ -41,14 +41,14 @@ public partial class FileStorage
|
||||
|
||||
private FileStorage()
|
||||
{
|
||||
this.AccessTime = DateTime.UtcNow;
|
||||
this.AccessTime = DateTimeOffset.UtcNow;
|
||||
this.AccessTimeout = TimeSpan.FromSeconds(60);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 最后访问时间。
|
||||
/// </summary>
|
||||
public DateTime AccessTime { get; private set; }
|
||||
public DateTimeOffset AccessTime { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 访问超时时间。默认60s
|
||||
@@ -91,7 +91,7 @@ public partial class FileStorage
|
||||
/// </summary>
|
||||
public void Flush()
|
||||
{
|
||||
this.AccessTime = DateTime.UtcNow;
|
||||
this.AccessTime = DateTimeOffset.UtcNow;
|
||||
this.FileStream.Flush();
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ public partial class FileStorage
|
||||
public int Read(long startPos, Span<byte> span)
|
||||
{
|
||||
// 更新访问时间,用于跟踪文件的最近访问时间。
|
||||
this.AccessTime = DateTime.UtcNow;
|
||||
this.AccessTime = DateTimeOffset.UtcNow;
|
||||
// 使用写锁保护共享资源,确保读操作的线程安全性。
|
||||
using (var writeLock = new WriteLock(this.m_lockSlim))
|
||||
{
|
||||
@@ -147,7 +147,7 @@ public partial class FileStorage
|
||||
public void Write(long startPos, ReadOnlySpan<byte> span)
|
||||
{
|
||||
// 更新文件的访问时间。
|
||||
this.AccessTime = DateTime.UtcNow;
|
||||
this.AccessTime = DateTimeOffset.UtcNow;
|
||||
|
||||
// 使用写锁确保线程安全。
|
||||
using (var writeLock = new WriteLock(this.m_lockSlim))
|
||||
|
||||
@@ -14,24 +14,18 @@ using System;
|
||||
|
||||
namespace TouchSocket.Core;
|
||||
|
||||
/// <summary>
|
||||
/// 定义了一个泛型接口,用于表示一块不可变内存的处理结果。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">内存中元素的类型。</typeparam>
|
||||
public interface IBlockResult<T> : IDisposable
|
||||
/// <summary>
|
||||
/// 表示一个块操作的结果。
|
||||
/// </summary>
|
||||
public interface IBlockResult : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取只读内存块。
|
||||
/// </summary>
|
||||
ReadOnlyMemory<T> Memory { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取表示内存处理是否完成的布尔值。
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// 获取一个值,该值消息处理是否完成。
|
||||
/// </summary>
|
||||
bool IsCompleted { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取处理结果的消息。
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// 获取处理结果的消息。
|
||||
/// </summary>
|
||||
string Message { get; }
|
||||
}
|
||||
}
|
||||
|
||||
29
src/TouchSocket.Core/IO/IReadOnlyMemoryBlockResult.cs
Normal file
29
src/TouchSocket.Core/IO/IReadOnlyMemoryBlockResult.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:https://touchsocket.net/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TouchSocket.Core;
|
||||
/// <summary>
|
||||
/// 表示一个只读内存块的结果。
|
||||
/// </summary>
|
||||
public interface IReadOnlyMemoryBlockResult : IBlockResult
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取只读内存块。
|
||||
/// </summary>
|
||||
ReadOnlyMemory<byte> Memory { get; }
|
||||
}
|
||||
@@ -15,103 +15,136 @@ using System;
|
||||
namespace TouchSocket.Core;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// 日志扩展方法
|
||||
/// </summary>
|
||||
public static class LoggerExtensions
|
||||
{
|
||||
#region LoggerGroup日志
|
||||
#region LoggerGroup 日志
|
||||
|
||||
/// <summary>
|
||||
/// 指定在<see cref="LoggerGroup"/>中的特定日志类型中输出中断日志
|
||||
/// 在指定类型的日志记录器中记录严重级别日志
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <typeparam name="TLog">日志记录器类型</typeparam>
|
||||
/// <param name="logger">日志组实例</param>
|
||||
/// <param name="msg">日志消息</param>
|
||||
public static void Critical<TLog>(this ILog logger, string msg) where TLog : ILog
|
||||
{
|
||||
logger.Log<TLog>(LogLevel.Critical, null, msg, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 指定在<see cref="LoggerGroup"/>中的特定日志类型中输出调试日志
|
||||
/// 在指定类型的日志记录器中记录严重级别日志
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <typeparam name="TLog">日志记录器类型</typeparam>
|
||||
/// <param name="logger">日志组实例</param>
|
||||
/// <param name="source">日志来源</param>
|
||||
/// <param name="msg">日志消息</param>
|
||||
public static void Critical<TLog>(this ILog logger, object source, string msg) where TLog : ILog
|
||||
{
|
||||
logger.Log<TLog>(LogLevel.Critical, source, msg, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在指定类型的日志记录器中记录调试级别日志
|
||||
/// </summary>
|
||||
/// <typeparam name="TLog">日志记录器类型</typeparam>
|
||||
/// <param name="logger">日志组实例</param>
|
||||
/// <param name="msg">日志消息</param>
|
||||
public static void Debug<TLog>(this ILog logger, string msg) where TLog : ILog
|
||||
{
|
||||
logger.Log<TLog>(LogLevel.Debug, null, msg, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 指定在<see cref="LoggerGroup"/>中的特定日志类型中输出错误日志
|
||||
/// 在指定类型的日志记录器中记录调试级别日志
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <typeparam name="TLog">日志记录器类型</typeparam>
|
||||
/// <param name="logger">日志组实例</param>
|
||||
/// <param name="source">日志来源</param>
|
||||
/// <param name="msg">日志消息</param>
|
||||
public static void Debug<TLog>(this ILog logger, object source, string msg) where TLog : ILog
|
||||
{
|
||||
logger.Log<TLog>(LogLevel.Debug, source, msg, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在指定类型的日志记录器中记录错误级别日志
|
||||
/// </summary>
|
||||
/// <typeparam name="TLog">日志记录器类型</typeparam>
|
||||
/// <param name="logger">日志组实例</param>
|
||||
/// <param name="msg">日志消息</param>
|
||||
public static void Error<TLog>(this ILog logger, string msg) where TLog : ILog
|
||||
{
|
||||
logger.Log<TLog>(LogLevel.Error, null, msg, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 指定在<see cref="LoggerGroup"/>中的特定日志类型中输出错误日志
|
||||
/// 在指定类型的日志记录器中记录错误级别日志
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <typeparam name="TLog">日志记录器类型</typeparam>
|
||||
/// <param name="logger">日志组实例</param>
|
||||
/// <param name="source">日志来源</param>
|
||||
/// <param name="msg">日志消息</param>
|
||||
public static void Error<TLog>(this ILog logger, object source, string msg) where TLog : ILog
|
||||
{
|
||||
logger.Log<TLog>(LogLevel.Error, source, msg, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 指定在<see cref="LoggerGroup"/>中的特定日志类型中输出异常日志
|
||||
/// 在指定类型的日志记录器中记录异常日志
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="ex"></param>
|
||||
/// <typeparam name="TLog">日志记录器类型</typeparam>
|
||||
/// <param name="logger">日志组实例</param>
|
||||
/// <param name="ex">异常实例</param>
|
||||
public static void Exception<TLog>(this ILog logger, Exception ex) where TLog : ILog
|
||||
{
|
||||
logger.Log<TLog>(LogLevel.Error, null, ex.Message, ex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 指定在<see cref="LoggerGroup"/>中的特定日志类型中输出异常日志
|
||||
/// 在指定类型的日志记录器中记录异常日志
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="ex"></param>
|
||||
/// <typeparam name="TLog">日志记录器类型</typeparam>
|
||||
/// <param name="logger">日志组实例</param>
|
||||
/// <param name="source">日志来源</param>
|
||||
/// <param name="ex">异常实例</param>
|
||||
public static void Exception<TLog>(this ILog logger, object source, Exception ex) where TLog : ILog
|
||||
{
|
||||
logger.Log<TLog>(LogLevel.Error, source, ex.Message, ex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 指定在<see cref="LoggerGroup"/>中的特定日志类型中输出消息日志
|
||||
/// 在指定类型的日志记录器中记录信息级别日志
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <typeparam name="TLog">日志记录器类型</typeparam>
|
||||
/// <param name="logger">日志组实例</param>
|
||||
/// <param name="msg">日志消息</param>
|
||||
public static void Info<TLog>(this ILog logger, string msg) where TLog : ILog
|
||||
{
|
||||
logger.Log<TLog>(LogLevel.Info, null, msg, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 指定在<see cref="LoggerGroup"/>中的特定日志类型中输出消息日志
|
||||
/// 在指定类型的日志记录器中记录信息级别日志
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <typeparam name="TLog">日志记录器类型</typeparam>
|
||||
/// <param name="logger">日志组实例</param>
|
||||
/// <param name="source">日志来源</param>
|
||||
/// <param name="msg">日志消息</param>
|
||||
public static void Info<TLog>(this ILog logger, object source, string msg) where TLog : ILog
|
||||
{
|
||||
logger.Log<TLog>(LogLevel.Info, source, msg, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 指定在<see cref="LoggerGroup"/>中的特定日志类型中输出日志
|
||||
/// 在指定类型的日志记录器中记录日志
|
||||
/// </summary>
|
||||
/// <param name="logLevel"></param>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="exception"></param>
|
||||
/// <param name="logger"></param>
|
||||
/// <typeparam name="TLog">日志记录器类型</typeparam>
|
||||
/// <param name="logger">日志组实例</param>
|
||||
/// <param name="logLevel">日志级别</param>
|
||||
/// <param name="source">日志来源</param>
|
||||
/// <param name="message">日志消息</param>
|
||||
/// <param name="exception">异常实例</param>
|
||||
public static void Log<TLog>(this ILog logger, LogLevel logLevel, object source, string message, Exception exception) where TLog : ILog
|
||||
{
|
||||
if (logger is LoggerGroup loggerGroup)
|
||||
@@ -121,165 +154,225 @@ public static class LoggerExtensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 指定在<see cref="LoggerGroup"/>中的特定日志类型中输出详细日志
|
||||
/// 在指定类型的日志记录器中记录跟踪级别日志
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <typeparam name="TLog">日志记录器类型</typeparam>
|
||||
/// <param name="logger">日志组实例</param>
|
||||
/// <param name="msg">日志消息</param>
|
||||
public static void Trace<TLog>(this ILog logger, string msg) where TLog : ILog
|
||||
{
|
||||
logger.Log<TLog>(LogLevel.Trace, null, msg, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 指定在<see cref="LoggerGroup"/>中的特定日志类型中输出警示日志
|
||||
/// 在指定类型的日志记录器中记录跟踪级别日志
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <typeparam name="TLog">日志记录器类型</typeparam>
|
||||
/// <param name="logger">日志组实例</param>
|
||||
/// <param name="source">日志来源</param>
|
||||
/// <param name="msg">日志消息</param>
|
||||
public static void Trace<TLog>(this ILog logger, object source, string msg) where TLog : ILog
|
||||
{
|
||||
logger.Log<TLog>(LogLevel.Trace, source, msg, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在指定类型的日志记录器中记录警告级别日志
|
||||
/// </summary>
|
||||
/// <typeparam name="TLog">日志记录器类型</typeparam>
|
||||
/// <param name="logger">日志组实例</param>
|
||||
/// <param name="msg">日志消息</param>
|
||||
public static void Warning<TLog>(this ILog logger, string msg) where TLog : ILog
|
||||
{
|
||||
logger.Log<TLog>(LogLevel.Warning, null, msg, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 指定在<see cref="LoggerGroup"/>中的特定日志类型中输出警示日志
|
||||
/// 在指定类型的日志记录器中记录警告级别日志
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <typeparam name="TLog">日志记录器类型</typeparam>
|
||||
/// <param name="logger">日志组实例</param>
|
||||
/// <param name="source">日志来源</param>
|
||||
/// <param name="msg">日志消息</param>
|
||||
public static void Warning<TLog>(this ILog logger, object source, string msg) where TLog : ILog
|
||||
{
|
||||
logger.Log<TLog>(LogLevel.Warning, source, msg, null);
|
||||
}
|
||||
|
||||
#endregion LoggerGroup日志
|
||||
#endregion
|
||||
|
||||
#region 日志
|
||||
#region 普通日志
|
||||
|
||||
/// <summary>
|
||||
/// 输出中断日志
|
||||
/// 记录严重级别日志
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <param name="logger">日志实例</param>
|
||||
/// <param name="msg">日志消息</param>
|
||||
public static void Critical(this ILog logger, string msg)
|
||||
{
|
||||
logger.Log(LogLevel.Critical, null, msg, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 输出调试日志
|
||||
/// 记录严重级别日志
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <param name="logger">日志实例</param>
|
||||
/// <param name="source">日志来源</param>
|
||||
/// <param name="msg">日志消息</param>
|
||||
public static void Critical(this ILog logger, object source, string msg)
|
||||
{
|
||||
logger.Log(LogLevel.Critical, source, msg, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 记录调试级别日志
|
||||
/// </summary>
|
||||
/// <param name="logger">日志实例</param>
|
||||
/// <param name="msg">日志消息</param>
|
||||
public static void Debug(this ILog logger, string msg)
|
||||
{
|
||||
logger.Log(LogLevel.Debug, null, msg, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 输出错误日志
|
||||
/// 记录调试级别日志
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <param name="logger">日志实例</param>
|
||||
/// <param name="source">日志来源</param>
|
||||
/// <param name="msg">日志消息</param>
|
||||
public static void Debug(this ILog logger, object source, string msg)
|
||||
{
|
||||
logger.Log(LogLevel.Debug, source, msg, null);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 记录调试级别日志
|
||||
/// </summary>
|
||||
/// <param name="logger">日志实例</param>
|
||||
/// <param name="source">日志来源</param>
|
||||
/// <param name="ex">由异常来的日志消息</param>
|
||||
public static void Debug(this ILog logger, object source, Exception ex)
|
||||
{
|
||||
logger.Log(LogLevel.Debug, source, ex.Message, ex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 记录错误级别日志
|
||||
/// </summary>
|
||||
/// <param name="logger">日志实例</param>
|
||||
/// <param name="msg">日志消息</param>
|
||||
public static void Error(this ILog logger, string msg)
|
||||
{
|
||||
logger.Log(LogLevel.Error, null, msg, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 输出错误日志
|
||||
/// 记录错误级别日志
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <param name="logger">日志实例</param>
|
||||
/// <param name="source">日志来源</param>
|
||||
/// <param name="msg">日志消息</param>
|
||||
public static void Error(this ILog logger, object source, string msg)
|
||||
{
|
||||
logger.Log(LogLevel.Error, source, msg, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 输出异常日志
|
||||
/// 记录异常日志
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="ex"></param>
|
||||
/// <param name="logger">日志实例</param>
|
||||
/// <param name="ex">异常实例</param>
|
||||
public static void Exception(this ILog logger, Exception ex)
|
||||
{
|
||||
logger.Log(LogLevel.Error, null, ex.Message, ex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 输出异常日志
|
||||
/// 记录异常日志
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="ex"></param>
|
||||
/// <param name="logger">日志实例</param>
|
||||
/// <param name="source">日志来源</param>
|
||||
/// <param name="ex">异常实例</param>
|
||||
public static void Exception(this ILog logger, object source, Exception ex)
|
||||
{
|
||||
logger.Log(LogLevel.Error, source, ex.Message, ex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 输出异常日志
|
||||
/// 记录异常日志(包含自定义消息)
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <param name="ex"></param>
|
||||
/// <param name="logger">日志实例</param>
|
||||
/// <param name="source">日志来源</param>
|
||||
/// <param name="msg">自定义消息</param>
|
||||
/// <param name="ex">异常实例</param>
|
||||
public static void Exception(this ILog logger, object source, string msg, Exception ex)
|
||||
{
|
||||
logger.Log(LogLevel.Error, source, msg, ex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 输出消息日志
|
||||
/// 记录信息级别日志
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <param name="logger">日志实例</param>
|
||||
/// <param name="msg">日志消息</param>
|
||||
public static void Info(this ILog logger, string msg)
|
||||
{
|
||||
logger.Log(LogLevel.Info, null, msg, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 输出消息日志
|
||||
/// 记录信息级别日志
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <param name="logger">日志实例</param>
|
||||
/// <param name="source">日志来源</param>
|
||||
/// <param name="msg">日志消息</param>
|
||||
public static void Info(this ILog logger, object source, string msg)
|
||||
{
|
||||
logger.Log(LogLevel.Info, source, msg, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 输出详细日志
|
||||
/// 记录跟踪级别日志
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <param name="logger">日志实例</param>
|
||||
/// <param name="msg">日志消息</param>
|
||||
public static void Trace(this ILog logger, string msg)
|
||||
{
|
||||
logger.Log(LogLevel.Trace, null, msg, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 输出警示日志
|
||||
/// 记录跟踪级别日志
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <param name="logger">日志实例</param>
|
||||
/// <param name="source">日志来源</param>
|
||||
/// <param name="msg">日志消息</param>
|
||||
public static void Trace(this ILog logger, object source, string msg)
|
||||
{
|
||||
logger.Log(LogLevel.Trace, source, msg, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 记录警告级别日志
|
||||
/// </summary>
|
||||
/// <param name="logger">日志实例</param>
|
||||
/// <param name="msg">日志消息</param>
|
||||
public static void Warning(this ILog logger, string msg)
|
||||
{
|
||||
logger.Log(LogLevel.Warning, null, msg, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 输出警示日志
|
||||
/// 记录警告级别日志
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="msg"></param>
|
||||
/// <param name="logger">日志实例</param>
|
||||
/// <param name="source">日志来源</param>
|
||||
/// <param name="msg">日志消息</param>
|
||||
public static void Warning(this ILog logger, object source, string msg)
|
||||
{
|
||||
logger.Log(LogLevel.Warning, source, msg, null);
|
||||
}
|
||||
|
||||
#endregion 日志
|
||||
#endregion
|
||||
}
|
||||
@@ -26,7 +26,7 @@ public static class PackageExtensions
|
||||
public static byte[] PackageAsBytes<TPackage>(this TPackage package) where TPackage : IPackage
|
||||
{
|
||||
// 创建一个字节块对象,用于存储序列化的字节数据
|
||||
var byteBlock = new ByteBlock();
|
||||
var byteBlock = new ByteBlock(1024*64);
|
||||
try
|
||||
{
|
||||
// 调用IPackage接口的Package方法,将实例序列化到字节块中
|
||||
|
||||
@@ -1,367 +1,362 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:https://touchsocket.net/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
////------------------------------------------------------------------------------
|
||||
//// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
//// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
//// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
//// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
//// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
//// Github源代码仓库:https://github.com/RRQM
|
||||
//// API首页:https://touchsocket.net/
|
||||
//// 交流QQ群:234762506
|
||||
//// 感谢您的下载和使用
|
||||
////------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
using System.Numerics;
|
||||
#endif
|
||||
|
||||
namespace TouchSocket.Core;
|
||||
//#if NET45
|
||||
|
||||
/// <summary>
|
||||
/// 提供一个数组对象的池化容器。
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public class ArrayPool<T>
|
||||
{
|
||||
private const int DefaultMaxArrayLength = 1024 * 1024;
|
||||
//using System;
|
||||
//using System.Diagnostics;
|
||||
//using System.Runtime.CompilerServices;
|
||||
//using System.Threading;
|
||||
|
||||
private const int DefaultMaxNumberOfArraysPerBucket = 50;
|
||||
//namespace TouchSocket.Core;
|
||||
|
||||
private readonly Bucket[] m_buckets;
|
||||
///// <summary>
|
||||
///// 提供一个数组对象的池化容器。
|
||||
///// </summary>
|
||||
///// <typeparam name="T"></typeparam>
|
||||
//public class ArrayPool<T>
|
||||
//{
|
||||
// private const int DefaultMaxArrayLength = 1024 * 1024;
|
||||
|
||||
/// <summary>
|
||||
/// 提供一个数组对象的池化容器。
|
||||
/// </summary>
|
||||
public ArrayPool() : this(DefaultMaxArrayLength, DefaultMaxNumberOfArraysPerBucket)
|
||||
{
|
||||
}
|
||||
// private const int DefaultMaxNumberOfArraysPerBucket = 50;
|
||||
|
||||
/// <summary>
|
||||
/// 提供一个数组对象的池化容器。
|
||||
/// </summary>
|
||||
/// <param name="maxArrayLength"></param>
|
||||
/// <param name="maxArraysPerBucket"></param>
|
||||
public ArrayPool(int maxArrayLength, int maxArraysPerBucket)
|
||||
{
|
||||
const int MinimumArrayLength = 16, MaximumArrayLength = int.MaxValue;
|
||||
if (maxArrayLength > MaximumArrayLength)
|
||||
{
|
||||
maxArrayLength = MaximumArrayLength;
|
||||
}
|
||||
else if (maxArrayLength < MinimumArrayLength)
|
||||
{
|
||||
maxArrayLength = MinimumArrayLength;
|
||||
}
|
||||
// private readonly Bucket[] m_buckets;
|
||||
|
||||
var capacity = 0L;
|
||||
var maxBuckets = SelectBucketIndex(maxArrayLength);
|
||||
var buckets = new Bucket[maxBuckets + 1];
|
||||
for (var i = 0; i < buckets.Length; i++)
|
||||
{
|
||||
buckets[i] = new Bucket(GetMaxSizeForBucket(i), maxArraysPerBucket);
|
||||
long num = GetMaxSizeForBucket(i) * maxArraysPerBucket;
|
||||
capacity += num;
|
||||
}
|
||||
this.m_buckets = buckets;
|
||||
this.Capacity = capacity;
|
||||
}
|
||||
// /// <summary>
|
||||
// /// 提供一个数组对象的池化容器。
|
||||
// /// </summary>
|
||||
// public ArrayPool() : this(DefaultMaxArrayLength, DefaultMaxNumberOfArraysPerBucket)
|
||||
// {
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// 对象池的最大容量。
|
||||
/// </summary>
|
||||
public long Capacity { get; private set; }
|
||||
// public static ArrayPool<T> Shared { get; } = new ArrayPool<T>(DefaultMaxArrayLength, DefaultMaxNumberOfArraysPerBucket);
|
||||
|
||||
/// <summary>
|
||||
/// 清理池中所有对象。
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
foreach (var item in this.m_buckets)
|
||||
{
|
||||
item.Clear();
|
||||
}
|
||||
}
|
||||
// /// <summary>
|
||||
// /// 提供一个数组对象的池化容器。
|
||||
// /// </summary>
|
||||
// /// <param name="maxArrayLength"></param>
|
||||
// /// <param name="maxArraysPerBucket"></param>
|
||||
// public ArrayPool(int maxArrayLength, int maxArraysPerBucket)
|
||||
// {
|
||||
// const int MinimumArrayLength = 16, MaximumArrayLength = int.MaxValue;
|
||||
// if (maxArrayLength > MaximumArrayLength)
|
||||
// {
|
||||
// maxArrayLength = MaximumArrayLength;
|
||||
// }
|
||||
// else if (maxArrayLength < MinimumArrayLength)
|
||||
// {
|
||||
// maxArrayLength = MinimumArrayLength;
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前池中的所有对象。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public long GetPoolSize()
|
||||
{
|
||||
long size = 0;
|
||||
foreach (var item in this.m_buckets)
|
||||
{
|
||||
size += item.Size;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
// var capacity = 0L;
|
||||
// var maxBuckets = SelectBucketIndex(maxArrayLength);
|
||||
// var buckets = new Bucket[maxBuckets + 1];
|
||||
// for (var i = 0; i < buckets.Length; i++)
|
||||
// {
|
||||
// buckets[i] = new Bucket(GetMaxSizeForBucket(i), maxArraysPerBucket);
|
||||
// long num = GetMaxSizeForBucket(i) * maxArraysPerBucket;
|
||||
// capacity += num;
|
||||
// }
|
||||
// this.m_buckets = buckets;
|
||||
// this.Capacity = capacity;
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// 最大请求尺寸梯度。
|
||||
/// </summary>
|
||||
public int MaxBucketsToTry { get; set; } = 5;
|
||||
// /// <summary>
|
||||
// /// 对象池的最大容量。
|
||||
// /// </summary>
|
||||
// public long Capacity { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取一个不小于指定尺寸的池化数组对象。
|
||||
/// </summary>
|
||||
/// <param name="minimumLength"></param>
|
||||
/// <returns></returns>
|
||||
public virtual T[] Rent(int minimumLength)
|
||||
{
|
||||
if (minimumLength == 0)
|
||||
{
|
||||
#if !NET45_OR_GREATER
|
||||
return Array.Empty<T>();
|
||||
#else
|
||||
return new T[0];
|
||||
#endif
|
||||
}
|
||||
// /// <summary>
|
||||
// /// 清理池中所有对象。
|
||||
// /// </summary>
|
||||
// public void Clear()
|
||||
// {
|
||||
// foreach (var item in this.m_buckets)
|
||||
// {
|
||||
// item.Clear();
|
||||
// }
|
||||
// }
|
||||
|
||||
T[] buffer;
|
||||
// /// <summary>
|
||||
// /// 获取当前池中的所有对象。
|
||||
// /// </summary>
|
||||
// /// <returns></returns>
|
||||
// public long GetPoolSize()
|
||||
// {
|
||||
// long size = 0;
|
||||
// foreach (var item in this.m_buckets)
|
||||
// {
|
||||
// size += item.Size;
|
||||
// }
|
||||
// return size;
|
||||
// }
|
||||
|
||||
var index = SelectBucketIndex(minimumLength);
|
||||
if (index < this.m_buckets.Length)
|
||||
{
|
||||
var i = index;
|
||||
do
|
||||
{
|
||||
buffer = this.m_buckets[i].Rent();
|
||||
if (buffer != null)
|
||||
{
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
while (++i < this.m_buckets.Length && i != index + this.MaxBucketsToTry);
|
||||
// /// <summary>
|
||||
// /// 最大请求尺寸梯度。
|
||||
// /// </summary>
|
||||
// public int MaxBucketsToTry { get; set; } = 5;
|
||||
|
||||
buffer = new T[this.m_buckets[index].m_bufferLength];
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer = new T[minimumLength];
|
||||
}
|
||||
// /// <summary>
|
||||
// /// 获取一个不小于指定尺寸的池化数组对象。
|
||||
// /// </summary>
|
||||
// /// <param name="minimumLength"></param>
|
||||
// /// <returns></returns>
|
||||
// public virtual T[] Rent(int minimumLength)
|
||||
// {
|
||||
// if (minimumLength == 0)
|
||||
// {
|
||||
// return new T[0];
|
||||
// }
|
||||
|
||||
return buffer;
|
||||
}
|
||||
// T[] buffer;
|
||||
|
||||
/// <summary>
|
||||
/// 归还池化对象。
|
||||
/// </summary>
|
||||
/// <param name="array"></param>
|
||||
/// <param name="clearArray"></param>
|
||||
public virtual void Return(T[] array, bool clearArray = false)
|
||||
{
|
||||
if (array is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(array));
|
||||
}
|
||||
// var index = SelectBucketIndex(minimumLength);
|
||||
// if (index < this.m_buckets.Length)
|
||||
// {
|
||||
// var i = index;
|
||||
// do
|
||||
// {
|
||||
// buffer = this.m_buckets[i].Rent();
|
||||
// if (buffer != null)
|
||||
// {
|
||||
// return buffer;
|
||||
// }
|
||||
// }
|
||||
// while (++i < this.m_buckets.Length && i != index + this.MaxBucketsToTry);
|
||||
|
||||
if (array.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// buffer = new T[this.m_buckets[index].m_bufferLength];
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// buffer = new T[minimumLength];
|
||||
// }
|
||||
|
||||
var bucket = SelectBucketIndex(array.Length);
|
||||
// return buffer;
|
||||
// }
|
||||
|
||||
var haveBucket = bucket < this.m_buckets.Length;
|
||||
if (haveBucket)
|
||||
{
|
||||
if (clearArray)
|
||||
{
|
||||
Array.Clear(array, 0, array.Length);
|
||||
}
|
||||
// /// <summary>
|
||||
// /// 归还池化对象。
|
||||
// /// </summary>
|
||||
// /// <param name="array"></param>
|
||||
// /// <param name="clearArray"></param>
|
||||
// public virtual void Return(T[] array, bool clearArray = false)
|
||||
// {
|
||||
// if (array is null)
|
||||
// {
|
||||
// throw new ArgumentNullException(nameof(array));
|
||||
// }
|
||||
|
||||
this.m_buckets[bucket].Return(array);
|
||||
}
|
||||
}
|
||||
// if (array.Length == 0)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// 命中匹配尺寸
|
||||
/// </summary>
|
||||
/// <param name="size"></param>
|
||||
/// <returns></returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int HitSize(int size)
|
||||
{
|
||||
return GetMaxSizeForBucket(SelectBucketIndex(size));
|
||||
}
|
||||
// var bucket = SelectBucketIndex(array.Length);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static int GetMaxSizeForBucket(int binIndex)
|
||||
{
|
||||
return 16 << binIndex;
|
||||
}
|
||||
// var haveBucket = bucket < this.m_buckets.Length;
|
||||
// if (haveBucket)
|
||||
// {
|
||||
// if (clearArray)
|
||||
// {
|
||||
// Array.Clear(array, 0, array.Length);
|
||||
// }
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static int SelectBucketIndex(int bufferSize)
|
||||
{
|
||||
#if NET6_0_OR_GREATER
|
||||
return BitOperations.Log2((uint)(bufferSize - 1) | 15u) - 3;
|
||||
#else
|
||||
return (int)(Math.Log((uint)(bufferSize - 1) | 15u, 2) - 3);
|
||||
#endif
|
||||
}
|
||||
// this.m_buckets[bucket].Return(array);
|
||||
// }
|
||||
// }
|
||||
|
||||
[DebuggerDisplay("Count={Count},Size={Size}")]
|
||||
private sealed class Bucket
|
||||
{
|
||||
internal readonly int m_bufferLength;
|
||||
private readonly int m_numberOfBuffers;
|
||||
private T[][] m_buffers;
|
||||
// /// <summary>
|
||||
// /// 命中匹配尺寸
|
||||
// /// </summary>
|
||||
// /// <param name="size"></param>
|
||||
// /// <returns></returns>
|
||||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
// public static int HitSize(int size)
|
||||
// {
|
||||
// return GetMaxSizeForBucket(SelectBucketIndex(size));
|
||||
// }
|
||||
|
||||
private int m_index;
|
||||
private SpinLock m_lock;
|
||||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
// internal static int GetMaxSizeForBucket(int binIndex)
|
||||
// {
|
||||
// return 16 << binIndex;
|
||||
// }
|
||||
|
||||
internal Bucket(int bufferLength, int numberOfBuffers)
|
||||
{
|
||||
this.m_lock = new SpinLock(Debugger.IsAttached);
|
||||
this.m_buffers = new T[numberOfBuffers][];
|
||||
this.m_bufferLength = bufferLength;
|
||||
this.m_numberOfBuffers = numberOfBuffers;
|
||||
}
|
||||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
// internal static int SelectBucketIndex(int bufferSize)
|
||||
// {
|
||||
// return (int)(Math.Log((uint)(bufferSize - 1) | 15u, 2) - 3);
|
||||
// }
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
var lockTaken = false;
|
||||
try
|
||||
{
|
||||
this.m_lock.Enter(ref lockTaken);
|
||||
this.m_buffers = new T[this.m_numberOfBuffers][];
|
||||
this.m_index = 0;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (lockTaken)
|
||||
{
|
||||
this.m_lock.Exit(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
// [DebuggerDisplay("Count={Count},Size={Size}")]
|
||||
// private sealed class Bucket
|
||||
// {
|
||||
// internal readonly int m_bufferLength;
|
||||
// private readonly int m_numberOfBuffers;
|
||||
// private T[][] m_buffers;
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
var lockTaken = false;
|
||||
try
|
||||
{
|
||||
this.m_lock.Enter(ref lockTaken);
|
||||
// private int m_index;
|
||||
// private SpinLock m_lock;
|
||||
|
||||
var count = 0;
|
||||
foreach (var item in this.m_buffers)
|
||||
{
|
||||
if (item != null)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
// internal Bucket(int bufferLength, int numberOfBuffers)
|
||||
// {
|
||||
// this.m_lock = new SpinLock(Debugger.IsAttached);
|
||||
// this.m_buffers = new T[numberOfBuffers][];
|
||||
// this.m_bufferLength = bufferLength;
|
||||
// this.m_numberOfBuffers = numberOfBuffers;
|
||||
// }
|
||||
|
||||
return count;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (lockTaken)
|
||||
{
|
||||
this.m_lock.Exit(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// public void Clear()
|
||||
// {
|
||||
// var lockTaken = false;
|
||||
// try
|
||||
// {
|
||||
// this.m_lock.Enter(ref lockTaken);
|
||||
// this.m_buffers = new T[this.m_numberOfBuffers][];
|
||||
// this.m_index = 0;
|
||||
// }
|
||||
// finally
|
||||
// {
|
||||
// if (lockTaken)
|
||||
// {
|
||||
// this.m_lock.Exit(false);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
internal int Id => this.GetHashCode();
|
||||
// public int Count
|
||||
// {
|
||||
// get
|
||||
// {
|
||||
// var lockTaken = false;
|
||||
// try
|
||||
// {
|
||||
// this.m_lock.Enter(ref lockTaken);
|
||||
|
||||
public long Size
|
||||
{
|
||||
get
|
||||
{
|
||||
var lockTaken = false;
|
||||
try
|
||||
{
|
||||
this.m_lock.Enter(ref lockTaken);
|
||||
// var count = 0;
|
||||
// foreach (var item in this.m_buffers)
|
||||
// {
|
||||
// if (item != null)
|
||||
// {
|
||||
// count++;
|
||||
// }
|
||||
// }
|
||||
|
||||
long size = 0;
|
||||
foreach (var item in this.m_buffers)
|
||||
{
|
||||
if (item != null)
|
||||
{
|
||||
size += item.LongLength;
|
||||
}
|
||||
}
|
||||
// return count;
|
||||
// }
|
||||
// finally
|
||||
// {
|
||||
// if (lockTaken)
|
||||
// {
|
||||
// this.m_lock.Exit(false);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
return size;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (lockTaken)
|
||||
{
|
||||
this.m_lock.Exit(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// internal int Id => this.GetHashCode();
|
||||
|
||||
internal T[] Rent()
|
||||
{
|
||||
T[] buffer = null;
|
||||
// public long Size
|
||||
// {
|
||||
// get
|
||||
// {
|
||||
// var lockTaken = false;
|
||||
// try
|
||||
// {
|
||||
// this.m_lock.Enter(ref lockTaken);
|
||||
|
||||
bool lockTaken = false, allocateBuffer = false;
|
||||
try
|
||||
{
|
||||
this.m_lock.Enter(ref lockTaken);
|
||||
// long size = 0;
|
||||
// foreach (var item in this.m_buffers)
|
||||
// {
|
||||
// if (item != null)
|
||||
// {
|
||||
// size += item.LongLength;
|
||||
// }
|
||||
// }
|
||||
|
||||
if (this.m_index < this.m_buffers.Length)
|
||||
{
|
||||
buffer = this.m_buffers[this.m_index];
|
||||
this.m_buffers[this.m_index++] = null;
|
||||
allocateBuffer = buffer == null;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (lockTaken)
|
||||
{
|
||||
this.m_lock.Exit(false);
|
||||
}
|
||||
}
|
||||
// return size;
|
||||
// }
|
||||
// finally
|
||||
// {
|
||||
// if (lockTaken)
|
||||
// {
|
||||
// this.m_lock.Exit(false);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
if (allocateBuffer)
|
||||
{
|
||||
buffer = new T[this.m_bufferLength];
|
||||
}
|
||||
// internal T[] Rent()
|
||||
// {
|
||||
// T[] buffer = null;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
// bool lockTaken = false, allocateBuffer = false;
|
||||
// try
|
||||
// {
|
||||
// this.m_lock.Enter(ref lockTaken);
|
||||
|
||||
internal void Return(T[] array)
|
||||
{
|
||||
if (array.Length != this.m_bufferLength)
|
||||
{
|
||||
throw new ArgumentException();
|
||||
}
|
||||
// if (this.m_index < this.m_buffers.Length)
|
||||
// {
|
||||
// buffer = this.m_buffers[this.m_index];
|
||||
// this.m_buffers[this.m_index++] = null;
|
||||
// allocateBuffer = buffer == null;
|
||||
// }
|
||||
// }
|
||||
// finally
|
||||
// {
|
||||
// if (lockTaken)
|
||||
// {
|
||||
// this.m_lock.Exit(false);
|
||||
// }
|
||||
// }
|
||||
|
||||
bool returned;
|
||||
// if (allocateBuffer)
|
||||
// {
|
||||
// buffer = new T[this.m_bufferLength];
|
||||
// }
|
||||
|
||||
var lockTaken = false;
|
||||
try
|
||||
{
|
||||
this.m_lock.Enter(ref lockTaken);
|
||||
// return buffer;
|
||||
// }
|
||||
|
||||
returned = this.m_index != 0;
|
||||
if (returned)
|
||||
{
|
||||
this.m_buffers[--this.m_index] = array;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (lockTaken)
|
||||
{
|
||||
this.m_lock.Exit(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// internal void Return(T[] array)
|
||||
// {
|
||||
// if (array.Length != this.m_bufferLength)
|
||||
// {
|
||||
// throw new ArgumentException();
|
||||
// }
|
||||
|
||||
// bool returned;
|
||||
|
||||
// var lockTaken = false;
|
||||
// try
|
||||
// {
|
||||
// this.m_lock.Enter(ref lockTaken);
|
||||
|
||||
// returned = this.m_index != 0;
|
||||
// if (returned)
|
||||
// {
|
||||
// this.m_buffers[--this.m_index] = array;
|
||||
// }
|
||||
// }
|
||||
// finally
|
||||
// {
|
||||
// if (lockTaken)
|
||||
// {
|
||||
// this.m_lock.Exit(false);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//#endif
|
||||
|
||||
@@ -16,6 +16,7 @@ using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Buffers;
|
||||
|
||||
namespace TouchSocket.Core;
|
||||
|
||||
@@ -26,7 +27,7 @@ namespace TouchSocket.Core;
|
||||
public sealed partial class ByteBlock : DisposableObject, IByteBlock
|
||||
{
|
||||
private byte[] m_buffer;
|
||||
private BytePool m_bytePool;
|
||||
private ArrayPool<byte> m_bytePool;
|
||||
private int m_dis;
|
||||
private bool m_holding;
|
||||
private int m_length;
|
||||
@@ -53,13 +54,13 @@ public sealed partial class ByteBlock : DisposableObject, IByteBlock
|
||||
/// <summary>
|
||||
/// 无参数构造函数,初始化一个具有默认大小的 ByteBlock 对象。
|
||||
/// </summary>
|
||||
/// <param name="byteSize">ByteBlock 的初始大小,默认为 64KB。</param>
|
||||
public ByteBlock(int byteSize = 1024 * 64)
|
||||
/// <param name="byteSize">ByteBlock 的初始大小。</param>
|
||||
public ByteBlock(int byteSize)
|
||||
{
|
||||
// 使用默认字节池初始化。
|
||||
this.m_bytePool = BytePool.Default;
|
||||
this.m_bytePool = ArrayPool<byte>.Shared;
|
||||
// 从字节池租用指定大小的字节数组。
|
||||
this.m_buffer = BytePool.Default.Rent(byteSize);
|
||||
this.m_buffer = this.m_bytePool.Rent(byteSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -67,7 +68,7 @@ public sealed partial class ByteBlock : DisposableObject, IByteBlock
|
||||
/// </summary>
|
||||
/// <param name="byteSize">ByteBlock 的初始大小。</param>
|
||||
/// <param name="bytePool">用于 ByteBlock 的 BytePool 实例。</param>
|
||||
public ByteBlock(int byteSize, BytePool bytePool)
|
||||
public ByteBlock(int byteSize, ArrayPool<byte> bytePool)
|
||||
{
|
||||
// 确保字节池不为空。
|
||||
this.m_bytePool = ThrowHelper.ThrowArgumentNullExceptionIf(bytePool, nameof(bytePool));
|
||||
@@ -161,7 +162,7 @@ public sealed partial class ByteBlock : DisposableObject, IByteBlock
|
||||
public bool IsStruct => false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public BytePool BytePool { get => this.m_bytePool; }
|
||||
public ArrayPool<byte> BytePool => this.m_bytePool;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public byte this[int index]
|
||||
@@ -239,7 +240,7 @@ public sealed partial class ByteBlock : DisposableObject, IByteBlock
|
||||
bool canReturn;
|
||||
if (this.m_bytePool == null)
|
||||
{
|
||||
this.m_bytePool = BytePool.Default;
|
||||
this.m_bytePool = ArrayPool<byte>.Shared;
|
||||
canReturn = false;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -10,12 +10,16 @@
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
|
||||
namespace TouchSocket.Core;
|
||||
|
||||
/// <summary>
|
||||
/// 内存池
|
||||
/// </summary>
|
||||
public sealed class BytePool : ArrayPool<byte>
|
||||
[Obsolete("此类已被弃用,请使用ArrayPool<byte>代替",true)]
|
||||
public sealed class BytePool
|
||||
{
|
||||
static BytePool()
|
||||
{
|
||||
@@ -34,7 +38,7 @@ public sealed class BytePool : ArrayPool<byte>
|
||||
/// </summary>
|
||||
/// <param name="maxArrayLength"></param>
|
||||
/// <param name="maxArraysPerBucket"></param>
|
||||
public BytePool(int maxArrayLength, int maxArraysPerBucket) : base(maxArrayLength, maxArraysPerBucket)
|
||||
public BytePool(int maxArrayLength, int maxArraysPerBucket)
|
||||
{
|
||||
this.AutoZero = false;
|
||||
this.MaxBlockSize = maxArrayLength;
|
||||
@@ -71,7 +75,7 @@ public sealed class BytePool : ArrayPool<byte>
|
||||
/// <returns></returns>
|
||||
public ByteBlock GetByteBlock(int byteSize)
|
||||
{
|
||||
return new ByteBlock(byteSize, this);
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -81,6 +85,6 @@ public sealed class BytePool : ArrayPool<byte>
|
||||
/// <returns></returns>
|
||||
public ValueByteBlock GetValueByteBlock(int byteSize)
|
||||
{
|
||||
return new ValueByteBlock(byteSize, this);
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Buffers;
|
||||
|
||||
namespace TouchSocket.Core;
|
||||
|
||||
@@ -27,7 +28,7 @@ public partial struct ValueByteBlock : IByteBlock, IEquatable<ValueByteBlock>
|
||||
{
|
||||
private static ValueByteBlock s_empty = new ValueByteBlock();
|
||||
private byte[] m_buffer;
|
||||
private BytePool m_bytePool;
|
||||
private ArrayPool<byte> m_bytePool;
|
||||
private int m_dis;
|
||||
private bool m_holding;
|
||||
private int m_length;
|
||||
@@ -42,8 +43,8 @@ public partial struct ValueByteBlock : IByteBlock, IEquatable<ValueByteBlock>
|
||||
/// <param name="byteSize">要从字节池租用的字节数。</param>
|
||||
public ValueByteBlock(int byteSize)
|
||||
{
|
||||
this.m_bytePool = BytePool.Default;
|
||||
this.m_buffer = BytePool.Default.Rent(byteSize);
|
||||
this.m_bytePool = ArrayPool<byte>.Shared;
|
||||
this.m_buffer = this.m_bytePool.Rent(byteSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -51,7 +52,7 @@ public partial struct ValueByteBlock : IByteBlock, IEquatable<ValueByteBlock>
|
||||
/// </summary>
|
||||
/// <param name="byteSize">要从字节池租用的字节数。</param>
|
||||
/// <param name="bytePool">用于租用字节的 BytePool 实例。</param>
|
||||
public ValueByteBlock(int byteSize, BytePool bytePool)
|
||||
public ValueByteBlock(int byteSize, ArrayPool<byte> bytePool)
|
||||
{
|
||||
this.m_bytePool = bytePool;
|
||||
this.m_buffer = bytePool.Rent(byteSize);
|
||||
@@ -86,7 +87,7 @@ public partial struct ValueByteBlock : IByteBlock, IEquatable<ValueByteBlock>
|
||||
public static ValueByteBlock Empty => s_empty;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public readonly BytePool BytePool => this.m_bytePool;
|
||||
public readonly ArrayPool<byte> BytePool => this.m_bytePool;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool CanRead => this.Using && this.CanReadLength > 0;
|
||||
@@ -229,7 +230,7 @@ public partial struct ValueByteBlock : IByteBlock, IEquatable<ValueByteBlock>
|
||||
bool canReturn;
|
||||
if (this.m_bytePool == null)
|
||||
{
|
||||
this.m_bytePool = BytePool.Default;
|
||||
this.m_bytePool = ArrayPool<byte>.Shared;
|
||||
canReturn = false;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -31,7 +31,7 @@ public interface IByteBlock : IDisposable, IBufferWriter<byte>
|
||||
/// <summary>
|
||||
/// 获取字节池。
|
||||
/// </summary>
|
||||
BytePool BytePool { get; }
|
||||
ArrayPool<byte> BytePool { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取是否可以读取。
|
||||
|
||||
@@ -63,7 +63,7 @@ public class DynamicMethodMemberAccessor : IMemberAccessor
|
||||
var memberAccessor = new MemberAccessor(instance.GetType());
|
||||
if (this.OnGetFieldInfes != null)
|
||||
{
|
||||
memberAccessor.OnGetFieldInfes = this.OnGetFieldInfes;
|
||||
memberAccessor.OnGetFieldInfos = this.OnGetFieldInfes;
|
||||
}
|
||||
|
||||
if (this.OnGetProperties != null)
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
@@ -38,7 +39,7 @@ public class MemberAccessor<T> : MemberAccessor
|
||||
public class MemberAccessor : IMemberAccessor
|
||||
{
|
||||
private Func<object, string, object> m_getValueDelegate;
|
||||
private Dictionary<string, FieldInfo> m_dicFieldInfes;
|
||||
private Dictionary<string, FieldInfo> m_dicFieldInfos;
|
||||
private Dictionary<string, PropertyInfo> m_dicProperties;
|
||||
private Action<object, string, object> m_setValueDelegate;
|
||||
|
||||
@@ -49,14 +50,14 @@ public class MemberAccessor : IMemberAccessor
|
||||
public MemberAccessor(Type type)
|
||||
{
|
||||
this.Type = type;
|
||||
this.OnGetFieldInfes = (t) => { return t.GetFields(); };
|
||||
this.OnGetFieldInfos = (t) => { return t.GetFields(); };
|
||||
this.OnGetProperties = (t) => { return t.GetProperties(); };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取字段
|
||||
/// </summary>
|
||||
public Func<Type, FieldInfo[]> OnGetFieldInfes { get; set; }
|
||||
public Func<Type, FieldInfo[]> OnGetFieldInfos { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取属性
|
||||
@@ -75,7 +76,7 @@ public class MemberAccessor : IMemberAccessor
|
||||
{
|
||||
if (GlobalEnvironment.DynamicBuilderType == DynamicBuilderType.Reflect)
|
||||
{
|
||||
this.m_dicFieldInfes = this.OnGetFieldInfes.Invoke(this.Type).ToDictionary(a => a.Name);
|
||||
this.m_dicFieldInfos = this.OnGetFieldInfos.Invoke(this.Type).ToDictionary(a => a.Name);
|
||||
this.m_dicProperties = this.OnGetProperties.Invoke(this.Type).ToDictionary(a => a.Name);
|
||||
}
|
||||
|
||||
@@ -95,13 +96,14 @@ public class MemberAccessor : IMemberAccessor
|
||||
this.m_setValueDelegate(instance, memberName, newValue);
|
||||
}
|
||||
|
||||
|
||||
private Func<object, string, object> GenerateGetValue()
|
||||
{
|
||||
if (GlobalEnvironment.DynamicBuilderType == DynamicBuilderType.Reflect)
|
||||
{
|
||||
return (obj, key) =>
|
||||
{
|
||||
return this.m_dicFieldInfes.TryGetValue(key, out var value1)
|
||||
return this.m_dicFieldInfos.TryGetValue(key, out var value1)
|
||||
? value1.GetValue(obj)
|
||||
: this.m_dicProperties.TryGetValue(key, out var value2) ? value2.GetValue(obj) : default;
|
||||
};
|
||||
@@ -112,7 +114,7 @@ public class MemberAccessor : IMemberAccessor
|
||||
var nameHash = Expression.Variable(typeof(int), "nameHash");
|
||||
var calHash = Expression.Assign(nameHash, Expression.Call(memberName, typeof(object).GetMethod("GetHashCode")));
|
||||
var cases = new List<SwitchCase>();
|
||||
foreach (var propertyInfo in this.OnGetFieldInfes.Invoke(this.Type))
|
||||
foreach (var propertyInfo in this.OnGetFieldInfos.Invoke(this.Type))
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -154,7 +156,7 @@ public class MemberAccessor : IMemberAccessor
|
||||
{
|
||||
return (obj, key, value) =>
|
||||
{
|
||||
if (this.m_dicFieldInfes.TryGetValue(key, out var value1))
|
||||
if (this.m_dicFieldInfos.TryGetValue(key, out var value1))
|
||||
{
|
||||
value1.SetValue(obj, value);
|
||||
}
|
||||
@@ -171,7 +173,7 @@ public class MemberAccessor : IMemberAccessor
|
||||
var nameHash = Expression.Variable(typeof(int), "nameHash");
|
||||
var calHash = Expression.Assign(nameHash, Expression.Call(memberName, typeof(object).GetMethod("GetHashCode")));
|
||||
var cases = new List<SwitchCase>();
|
||||
foreach (var propertyInfo in this.OnGetFieldInfes.Invoke(this.Type))
|
||||
foreach (var propertyInfo in this.OnGetFieldInfos.Invoke(this.Type))
|
||||
{
|
||||
var property = Expression.Field(Expression.Convert(instance, this.Type), propertyInfo.Name);
|
||||
var setValue = Expression.Assign(property, Expression.Convert(newValue, propertyInfo.FieldType));
|
||||
|
||||
@@ -107,11 +107,13 @@ public class Method
|
||||
/// <param name="targetType">目标类型</param>
|
||||
/// <param name="methodName">目标方法</param>
|
||||
/// <param name="dynamicBuilderType">指定构建的类型</param>
|
||||
public Method([DynamicallyAccessedMembersAttribute( DynamicallyAccessedMemberTypes.PublicMethods)] Type targetType,string methodName, DynamicBuilderType? dynamicBuilderType = default)
|
||||
public Method([DynamicallyAccessedMembers( DynamicallyAccessedMemberTypes.PublicMethods)] Type targetType,string methodName, DynamicBuilderType? dynamicBuilderType = default)
|
||||
:this(targetType.GetMethod(methodName),dynamicBuilderType)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
private bool CreateInvokeFromIL()
|
||||
{
|
||||
if (this.m_invoker != null)
|
||||
@@ -391,18 +393,31 @@ public class Method
|
||||
for (var i = 0; i < paramTypes.Length; i++)
|
||||
{
|
||||
if (ps[i].ParameterType.IsByRef)
|
||||
{
|
||||
il.Emit(OpCodes.Ldloca_S, locals[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
il.Emit(OpCodes.Ldloc, locals[i]);
|
||||
}
|
||||
}
|
||||
if (methodInfo.IsStatic)
|
||||
{
|
||||
il.EmitCall(OpCodes.Call, methodInfo, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
il.EmitCall(OpCodes.Callvirt, methodInfo, null);
|
||||
}
|
||||
|
||||
if (methodInfo.ReturnType == typeof(void))
|
||||
{
|
||||
il.Emit(OpCodes.Ldnull);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitBoxIfNeeded(il, methodInfo.ReturnType);
|
||||
}
|
||||
|
||||
for (var i = 0; i < paramTypes.Length; i++)
|
||||
{
|
||||
@@ -412,14 +427,17 @@ public class Method
|
||||
EmitFastInt(il, i);
|
||||
il.Emit(OpCodes.Ldloc, locals[i]);
|
||||
if (locals[i].LocalType.IsValueType)
|
||||
{
|
||||
il.Emit(OpCodes.Box, locals[i].LocalType);
|
||||
}
|
||||
|
||||
il.Emit(OpCodes.Stelem_Ref);
|
||||
}
|
||||
}
|
||||
|
||||
il.Emit(OpCodes.Ret);
|
||||
var invoder = (Func<object, object[], object>)dynamicMethod.CreateDelegate(typeof(Func<object, object[], object>));
|
||||
return invoder;
|
||||
var invoker= (Func<object, object[], object>)dynamicMethod.CreateDelegate(typeof(Func<object, object[], object>));
|
||||
return invoker;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -78,6 +78,24 @@ namespace TouchSocket.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 The assertion that the value of '{0}' is false failed, but the actual value is' {1} '. 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string AssertFalseFail {
|
||||
get {
|
||||
return ResourceManager.GetString("AssertFalseFail", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 The assertion that the value of '{0}' is true failed, but the actual value is' {1} '. 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string AssertTrueFail {
|
||||
get {
|
||||
return ResourceManager.GetString("AssertTrueFail", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 The current byteBlock does not allow reading. 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -141,6 +159,15 @@ namespace TouchSocket.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Invalid after Completed. 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string InvalidAfterCompleted {
|
||||
get {
|
||||
return ResourceManager.GetString("InvalidAfterCompleted", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 For enumeration type: '{0}', an invalid enumeration value '{1}' was set. 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -231,6 +258,15 @@ namespace TouchSocket.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 QueueEmpty。 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string QueueEmpty {
|
||||
get {
|
||||
return ResourceManager.GetString("QueueEmpty", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Type: {0}, Information: {1} 的本地化字符串。
|
||||
/// </summary>
|
||||
|
||||
@@ -184,4 +184,16 @@
|
||||
<data name="PluginIsNull" xml:space="preserve">
|
||||
<value>Plugin '{0}' is null, it may be missing registration information in the container.</value>
|
||||
</data>
|
||||
<data name="InvalidAfterCompleted" xml:space="preserve">
|
||||
<value>Invalid after Completed.</value>
|
||||
</data>
|
||||
<data name="QueueEmpty" xml:space="preserve">
|
||||
<value>QueueEmpty。</value>
|
||||
</data>
|
||||
<data name="AssertTrueFail" xml:space="preserve">
|
||||
<value>The assertion that the value of '{0}' is true failed, but the actual value is' {1} '.</value>
|
||||
</data>
|
||||
<data name="AssertFalseFail" xml:space="preserve">
|
||||
<value>The assertion that the value of '{0}' is false failed, but the actual value is' {1} '.</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -233,4 +233,16 @@
|
||||
<value>插件‘{0}’为null,它可能在容器中缺少注册信息。</value>
|
||||
<comment>插件‘{0}’为null,它可能在容器中缺少注册信息。</comment>
|
||||
</data>
|
||||
<data name="InvalidAfterCompleted" xml:space="preserve">
|
||||
<value>完成后无效。</value>
|
||||
</data>
|
||||
<data name="QueueEmpty" xml:space="preserve">
|
||||
<value>队列为空。</value>
|
||||
</data>
|
||||
<data name="AssertTrueFail" xml:space="preserve">
|
||||
<value>断言”{0}“的值为true失败,实际值为”{1}“。</value>
|
||||
</data>
|
||||
<data name="AssertFalseFail" xml:space="preserve">
|
||||
<value>断言”{0}“的值为false失败,实际值为”{1}“。</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -30,10 +30,15 @@ public record struct Result : IResult
|
||||
/// </summary>
|
||||
public static readonly Result Default = new Result(ResultCode.Default, TouchSocketCoreResource.Default);
|
||||
|
||||
/// <summary>
|
||||
/// 操作对象已被释放
|
||||
/// </summary>
|
||||
public static readonly Result Disposed = new Result(ResultCode.Disposed, TouchSocketCoreResource.ObjectDisposed);
|
||||
|
||||
/// <summary>
|
||||
/// 未知失败
|
||||
/// </summary>
|
||||
public static readonly Result UnknownFail = new Result(ResultCode.Fail, TouchSocketCoreResource.UnknownError);
|
||||
public static readonly Result UnknownFail = new Result(ResultCode.Failure, TouchSocketCoreResource.UnknownError);
|
||||
|
||||
/// <summary>
|
||||
/// 超时
|
||||
@@ -72,8 +77,16 @@ public record struct Result : IResult
|
||||
/// <param name="exception">异常对象,用于提取错误信息</param>
|
||||
public Result(Exception exception)
|
||||
{
|
||||
this.ResultCode = ResultCode.Exception; // 设置结果代码为异常
|
||||
this.Message = exception.Message; // 设置结果消息为异常的详细信息
|
||||
if (typeof(OperationCanceledException) == exception.GetType())
|
||||
{
|
||||
this.ResultCode = ResultCode.Canceled;
|
||||
this.Message = exception.Message;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.ResultCode = ResultCode.Exception; // 设置结果代码为异常
|
||||
this.Message = exception.Message; // 设置结果消息为异常的详细信息
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -133,17 +146,17 @@ public record struct Result : IResult
|
||||
public static Result FromException(Exception ex)
|
||||
{
|
||||
// 返回一个新的Result实例,包含异常错误代码和异常消息
|
||||
return new Result(ResultCode.Exception, ex.Message);
|
||||
return new Result(ex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建来自<see cref="ResultCode.Fail"/>的<see cref="Result"/>
|
||||
/// 创建来自<see cref="ResultCode.Failure"/>的<see cref="Result"/>
|
||||
/// </summary>
|
||||
/// <param name="msg">关联的消息</param>
|
||||
/// <returns>创建的Result对象</returns>
|
||||
public static Result FromFail(string msg)
|
||||
{
|
||||
return new Result(ResultCode.Fail, msg);
|
||||
return new Result(ResultCode.Failure, msg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -18,19 +18,9 @@ namespace TouchSocket.Core;
|
||||
public enum ResultCode : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// 默认
|
||||
/// 默认,表示没有特定的结果状态
|
||||
/// </summary>
|
||||
Default,
|
||||
|
||||
/// <summary>
|
||||
/// 错误
|
||||
/// </summary>
|
||||
Error,
|
||||
|
||||
/// <summary>
|
||||
/// 异常
|
||||
/// </summary>
|
||||
Exception,
|
||||
Default = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 成功
|
||||
@@ -38,9 +28,19 @@ public enum ResultCode : byte
|
||||
Success,
|
||||
|
||||
/// <summary>
|
||||
/// 失败
|
||||
/// 错误,程度较重的错误,但不影响系统的运行
|
||||
/// </summary>
|
||||
Fail,
|
||||
Error,
|
||||
|
||||
/// <summary>
|
||||
/// 异常,程度较重的错误,可能是由于系统异常或其他不可恢复的原因导致的
|
||||
/// </summary>
|
||||
Exception,
|
||||
|
||||
/// <summary>
|
||||
/// 失败,程度较轻的错误,可能是由于参数错误或其他可恢复的原因导致的
|
||||
/// </summary>
|
||||
Failure,
|
||||
|
||||
/// <summary>
|
||||
/// 操作超时
|
||||
@@ -50,5 +50,11 @@ public enum ResultCode : byte
|
||||
/// <summary>
|
||||
/// 操作取消
|
||||
/// </summary>
|
||||
Canceled
|
||||
Canceled,
|
||||
|
||||
/// <summary>
|
||||
/// 操作对象已被释放
|
||||
/// </summary>
|
||||
Disposed,
|
||||
|
||||
}
|
||||
@@ -98,7 +98,7 @@ public readonly struct Result<T> : IResult<T>
|
||||
{
|
||||
if (value is null)
|
||||
{
|
||||
return new Result<T>(ResultCode.Fail, "value is null.");
|
||||
return new Result<T>(ResultCode.Failure, "value is null.");
|
||||
}
|
||||
return new Result<T>(value);
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ public sealed class SerializObject
|
||||
this.InstanceType = InstanceType.Class;
|
||||
this.MemberAccessor = new MemberAccessor(type)
|
||||
{
|
||||
OnGetFieldInfes = GetFieldInfos,
|
||||
OnGetFieldInfos = GetFieldInfos,
|
||||
OnGetProperties = GetProperties
|
||||
};
|
||||
this.MemberAccessor.Build();
|
||||
|
||||
@@ -34,8 +34,8 @@ public abstract class FastSerializerContext
|
||||
this.AddConverter(typeof(ByteBlock), new ByteBlockFastBinaryConverter());
|
||||
this.AddConverter(typeof(MemoryStream), new MemoryStreamFastBinaryConverter());
|
||||
this.AddConverter(typeof(Guid), new GuidFastBinaryConverter());
|
||||
this.AddConverter(typeof(DataTable), new DataTableFastBinaryConverter());
|
||||
this.AddConverter(typeof(DataSet), new DataSetFastBinaryConverter());
|
||||
//this.AddConverter(typeof(DataTable), new DataTableFastBinaryConverter());
|
||||
//this.AddConverter(typeof(DataSet), new DataSetFastBinaryConverter());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
@@ -23,6 +24,7 @@ namespace TouchSocket.Core;
|
||||
/// 高性能序列化器
|
||||
/// </summary>
|
||||
|
||||
|
||||
public static partial class SerializeConvert
|
||||
{
|
||||
#pragma warning disable SYSLIB0011 // 微软觉得不安全,不推荐使用
|
||||
@@ -83,6 +85,7 @@ public static partial class SerializeConvert
|
||||
/// <param name="length"></param>
|
||||
/// <param name="binder"></param>
|
||||
/// <returns></returns>
|
||||
|
||||
public static T BinaryDeserialize<T>(byte[] data, int offset, int length, SerializationBinder binder = null)
|
||||
{
|
||||
using (var DeserializeStream = new MemoryStream(data, offset, length))
|
||||
@@ -214,7 +217,7 @@ public static partial class SerializeConvert
|
||||
///// <returns></returns>
|
||||
//public static byte[] FastBinarySerialize<[DynamicallyAccessedMembers(FastBinaryFormatter.DynamicallyAccessed)] T>( in T obj)
|
||||
//{
|
||||
// var byteBlock = new ByteBlock();
|
||||
// var byteBlock = new ByteBlock(1024*64);
|
||||
// try
|
||||
// {
|
||||
// FastBinarySerialize(ref byteBlock, obj);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -19,7 +20,7 @@ namespace TouchSocket.Core;
|
||||
/// <summary>
|
||||
/// EasyTask 类简化了对异步任务的处理,提供了简便的静态方法来创建和操作任务。
|
||||
/// </summary>
|
||||
public static class EasyTask
|
||||
public static partial class EasyTask
|
||||
{
|
||||
/// <summary>
|
||||
/// EasyTask 类的静态构造函数,在类加载时初始化 CompletedTask 属性。
|
||||
|
||||
231
src/TouchSocket.Core/Threading/EasyTask_Run.cs
Normal file
231
src/TouchSocket.Core/Threading/EasyTask_Run.cs
Normal file
@@ -0,0 +1,231 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:https://touchsocket.net/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TouchSocket.Resources;
|
||||
|
||||
namespace TouchSocket.Core;
|
||||
|
||||
public static partial class EasyTask
|
||||
{
|
||||
/// <summary>
|
||||
/// 运行一个带有状态和取消令牌的异步方法。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">状态的类型。</typeparam>
|
||||
/// <param name="func">要运行的异步方法。</param>
|
||||
/// <param name="status">传递给方法的状态。</param>
|
||||
/// <param name="ct">取消令牌。</param>
|
||||
/// <returns>表示异步操作的任务。</returns>
|
||||
public static Task Run<T>(Func<T, CancellationToken, Task> func, T status, CancellationToken ct = default)
|
||||
{
|
||||
ThrowHelper.ThrowArgumentNullExceptionIf(func, nameof(func));
|
||||
|
||||
return Task.Run(() => func(status, ct), ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 运行一个带有状态的异步方法。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">状态的类型。</typeparam>
|
||||
/// <param name="func">要运行的异步方法。</param>
|
||||
/// <param name="status">传递给方法的状态。</param>
|
||||
/// <param name="ct">取消令牌。</param>
|
||||
/// <returns>表示异步操作的任务。</returns>
|
||||
public static Task Run<T>(Func<T, Task> func, T status, CancellationToken ct = default)
|
||||
{
|
||||
ThrowHelper.ThrowArgumentNullExceptionIf(func, nameof(func));
|
||||
return Task.Run(() => func(status), ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 安全地运行一个带有状态的异步方法。
|
||||
/// </summary>
|
||||
/// <typeparam name="T1">状态的类型。</typeparam>
|
||||
/// <param name="func">要运行的异步方法。</param>
|
||||
/// <param name="status">传递给方法的状态。</param>
|
||||
/// <param name="ct">取消令牌。</param>
|
||||
/// <returns>表示异步操作的任务。</returns>
|
||||
public static async Task SafeRun<T1>(Func<T1, Task> func, T1 status, CancellationToken ct = default)
|
||||
{
|
||||
if (func is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (ct.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
await Task.Run(() => func(status), ct);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 安全地等待一个任务完成。
|
||||
/// </summary>
|
||||
/// <param name="task">要等待的任务。</param>
|
||||
/// <param name="ct">取消令牌。</param>
|
||||
/// <returns>表示任务结果的 <see cref="Result"/> 对象。</returns>
|
||||
public static async Task<Result> SafeWaitAsync(this Task task, CancellationToken ct = default)
|
||||
{
|
||||
if (task is null)
|
||||
{
|
||||
return Result.FromFail(TouchSocketCoreResource.ArgumentIsNull.Format(nameof(task)));
|
||||
}
|
||||
if (ct.IsCancellationRequested)
|
||||
{
|
||||
return Result.Canceled;
|
||||
}
|
||||
try
|
||||
{
|
||||
await task.ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
return Result.Success;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.FromException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 安全地等待一个任务完成并返回结果。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">任务结果的类型。</typeparam>
|
||||
/// <param name="task">要等待的任务。</param>
|
||||
/// <param name="ct">取消令牌。</param>
|
||||
/// <returns>表示任务结果的 <see cref="Result{T}"/> 对象。</returns>
|
||||
public static async Task<Result<T>> SafeWaitAsync<T>(this Task<T> task, CancellationToken ct = default)
|
||||
{
|
||||
if (task is null)
|
||||
{
|
||||
return Result.FromFail(TouchSocketCoreResource.ArgumentIsNull.Format(nameof(task)));
|
||||
}
|
||||
if (ct.IsCancellationRequested)
|
||||
{
|
||||
return Result.Canceled;
|
||||
}
|
||||
try
|
||||
{
|
||||
return new Result<T>(await task.ConfigureAwait(EasyTask.ContinueOnCapturedContext));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.FromException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 安全地运行一个带有状态的异步方法。
|
||||
/// </summary>
|
||||
/// <param name="func">要运行的异步方法。</param>
|
||||
/// <param name="ct">取消令牌。</param>
|
||||
/// <returns>表示异步操作的任务。</returns>
|
||||
public static async Task SafeRun(Func<Task> func, CancellationToken ct = default)
|
||||
{
|
||||
if (func is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (ct.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
await Task.Run(() => func(), ct);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 安全地运行一个带有两个状态的异步方法。
|
||||
/// </summary>
|
||||
/// <typeparam name="T1">第一个状态的类型。</typeparam>
|
||||
/// <typeparam name="T2">第二个状态的类型。</typeparam>
|
||||
/// <param name="func">要运行的异步方法。</param>
|
||||
/// <param name="status1">传递给方法的第一个状态。</param>
|
||||
/// <param name="status2">传递给方法的第二个状态。</param>
|
||||
/// <param name="ct">取消令牌。</param>
|
||||
/// <returns>表示异步操作的任务。</returns>
|
||||
public static async Task SafeRun<T1, T2>(Func<T1, T2, Task> func, T1 status1, T2 status2, CancellationToken ct = default)
|
||||
{
|
||||
if (func is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (ct.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
await Task.Run(() => func(status1, status2), ct);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 运行一个无状态的异步方法。
|
||||
/// </summary>
|
||||
/// <param name="func">要运行的异步方法。</param>
|
||||
/// <param name="ct">取消令牌。</param>
|
||||
/// <returns>表示异步操作的任务。</returns>
|
||||
public static Task Run(Func<Task> func, CancellationToken ct = default)
|
||||
{
|
||||
ThrowHelper.ThrowArgumentNullExceptionIf(func, nameof(func));
|
||||
|
||||
return Task.Run(func, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 运行一个带有状态的同步方法。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">状态的类型。</typeparam>
|
||||
/// <param name="func">要运行的同步方法。</param>
|
||||
/// <param name="status">传递给方法的状态。</param>
|
||||
/// <param name="ct">取消令牌。</param>
|
||||
/// <returns>表示异步操作的任务。</returns>
|
||||
public static Task Run<T>(Action<T> func, T status, CancellationToken ct = default)
|
||||
{
|
||||
ThrowHelper.ThrowArgumentNullExceptionIf(func, nameof(func));
|
||||
|
||||
return Task.Run(() => func(status), ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 运行一个带有状态和取消令牌的同步方法。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">状态的类型。</typeparam>
|
||||
/// <param name="func">要运行的同步方法。</param>
|
||||
/// <param name="status">传递给方法的状态。</param>
|
||||
/// <param name="ct">取消令牌。</param>
|
||||
/// <returns>表示异步操作的任务。</returns>
|
||||
public static Task Run<T>(Action<T, CancellationToken> func, T status, CancellationToken ct = default)
|
||||
{
|
||||
ThrowHelper.ThrowArgumentNullExceptionIf(func, nameof(func));
|
||||
|
||||
return Task.Run(() => func(status, ct), ct);
|
||||
}
|
||||
}
|
||||
42
src/TouchSocket.Core/Threading/ReadLock.cs
Normal file
42
src/TouchSocket.Core/Threading/ReadLock.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:https://touchsocket.net/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace TouchSocket.Core;
|
||||
|
||||
/// <summary>
|
||||
/// 读取锁
|
||||
/// </summary>
|
||||
public readonly struct ReadLock : IDisposable
|
||||
{
|
||||
private readonly ReaderWriterLockSlim m_locks;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="locks"></param>
|
||||
public ReadLock(ReaderWriterLockSlim locks)
|
||||
{
|
||||
this.m_locks = locks;
|
||||
this.m_locks.EnterReadLock();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
this.m_locks.ExitReadLock();
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@
|
||||
// {
|
||||
// return func.Invoke(obj);
|
||||
// }
|
||||
// Task.Factory.StartNew(func,status);
|
||||
// EasyTask.Run(func,status);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
@@ -15,32 +15,6 @@ using System.Threading;
|
||||
|
||||
namespace TouchSocket.Core;
|
||||
|
||||
/// <summary>
|
||||
/// 读取锁
|
||||
/// </summary>
|
||||
public readonly struct ReadLock : IDisposable
|
||||
{
|
||||
private readonly ReaderWriterLockSlim m_locks;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="locks"></param>
|
||||
public ReadLock(ReaderWriterLockSlim locks)
|
||||
{
|
||||
this.m_locks = locks;
|
||||
this.m_locks.EnterReadLock();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
this.m_locks.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入锁
|
||||
/// </summary>
|
||||
@@ -21,20 +21,25 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net45'">
|
||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net462'">
|
||||
<PackageReference Include="System.ValueTuple" Version="4.6.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net472'">
|
||||
<PackageReference Include="System.ValueTuple" Version="4.6.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net481'">
|
||||
<PackageReference Include="System.Text.Json" Version="8.0.5" />
|
||||
<PackageReference Include="System.ValueTuple" Version="4.6.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.1'">
|
||||
<PackageReference Include="System.Text.Json" Version="8.0.5" />
|
||||
<PackageReference Include="System.ValueTuple" Version="4.6.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net6.0'">
|
||||
@@ -53,10 +58,6 @@
|
||||
<PackageReference Include="System.Memory" Version="4.5.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<RdXmlFile Include="rd.xml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Resources\TouchSocketCoreResource.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
|
||||
@@ -10,7 +10,10 @@
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
@@ -36,25 +39,17 @@ public class WaitHandlePool<TWaitData, TWaitDataAsync, T> : DisposableObject, IW
|
||||
where TWaitDataAsync : IWaitDataAsync<T>, new()
|
||||
where T : IWaitHandle
|
||||
{
|
||||
private readonly ConcurrentDictionary<int, TWaitData> m_waitDic;
|
||||
private readonly ConcurrentDictionary<int, TWaitDataAsync> m_waitDicAsync;
|
||||
private readonly ConcurrentQueue<TWaitData> m_waitQueue;
|
||||
private readonly ConcurrentQueue<TWaitDataAsync> m_waitQueueAsync;
|
||||
/// <summary>
|
||||
/// 不要设为readonly
|
||||
/// </summary>
|
||||
private SpinLock m_lock = new SpinLock(Debugger.IsAttached);
|
||||
private readonly Dictionary<int, TWaitData> m_waitDic = new();
|
||||
private readonly Dictionary<int, TWaitDataAsync> m_waitDicAsync = new();
|
||||
private readonly Queue<TWaitData> m_waitQueue = new();
|
||||
private readonly Queue<TWaitDataAsync> m_waitQueueAsync = new();
|
||||
private int m_currentSign;
|
||||
private int m_maxSign = int.MaxValue;
|
||||
private int m_minSign = int.MinValue;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化等待句柄池。
|
||||
/// <see cref="WaitHandlePool{T}"/>"/>
|
||||
/// </summary>
|
||||
public WaitHandlePool()
|
||||
{
|
||||
this.m_waitDic = new ConcurrentDictionary<int, TWaitData>();
|
||||
this.m_waitDicAsync = new ConcurrentDictionary<int, TWaitDataAsync>();
|
||||
this.m_waitQueue = new ConcurrentQueue<TWaitData>();
|
||||
this.m_waitQueueAsync = new ConcurrentQueue<TWaitDataAsync>();
|
||||
}
|
||||
private int m_minSign = 0;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int MaxSign { get => this.m_maxSign; set => this.m_maxSign = value; }
|
||||
@@ -65,60 +60,111 @@ public class WaitHandlePool<TWaitData, TWaitDataAsync, T> : DisposableObject, IW
|
||||
/// <inheritdoc/>
|
||||
public void CancelAll()
|
||||
{
|
||||
foreach (var item in this.m_waitDic.Values)
|
||||
var lockTaken = false;
|
||||
try
|
||||
{
|
||||
item.Cancel();
|
||||
this.m_lock.Enter(ref lockTaken);
|
||||
foreach (var item in this.m_waitDic.Values)
|
||||
{
|
||||
item.Cancel();
|
||||
}
|
||||
foreach (var item in this.m_waitDicAsync.Values)
|
||||
{
|
||||
item.Cancel();
|
||||
}
|
||||
}
|
||||
foreach (var item in this.m_waitDicAsync.Values)
|
||||
finally
|
||||
{
|
||||
item.Cancel();
|
||||
if (lockTaken)
|
||||
{
|
||||
this.m_lock.Exit(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Destroy(TWaitData waitData)
|
||||
{
|
||||
if (waitData.WaitResult == null)
|
||||
var lockTaken = false;
|
||||
try
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (this.m_waitDic.TryRemove(waitData.WaitResult.Sign, out var wait))
|
||||
{
|
||||
if (wait.DisposedValue)
|
||||
this.m_lock.Enter(ref lockTaken);
|
||||
if (waitData.WaitResult == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (this.m_waitDic.Remove(waitData.WaitResult.Sign))
|
||||
{
|
||||
if (waitData.DisposedValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
wait.Reset();
|
||||
this.m_waitQueue.Enqueue(wait);
|
||||
waitData.Reset();
|
||||
this.m_waitQueue.Enqueue(waitData);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (lockTaken)
|
||||
{
|
||||
this.m_lock.Exit(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Destroy(TWaitDataAsync waitData)
|
||||
{
|
||||
if (waitData.WaitResult == null)
|
||||
var lockTaken = false;
|
||||
try
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (this.m_waitDicAsync.TryRemove(waitData.WaitResult.Sign, out var wait))
|
||||
{
|
||||
if (wait.DisposedValue)
|
||||
this.m_lock.Enter(ref lockTaken);
|
||||
|
||||
if (waitData.WaitResult == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (this.m_waitDicAsync.Remove(waitData.WaitResult.Sign))
|
||||
{
|
||||
if (waitData.DisposedValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
wait.Reset();
|
||||
this.m_waitQueueAsync.Enqueue(wait);
|
||||
waitData.Reset();
|
||||
this.m_waitQueueAsync.Enqueue(waitData);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (lockTaken)
|
||||
{
|
||||
this.m_lock.Exit(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public TWaitData GetWaitData(T result, bool autoSign = true)
|
||||
{
|
||||
// 尝试从同步等待队列中取出一个等待数据对象
|
||||
if (this.m_waitQueue.TryDequeue(out var waitData))
|
||||
var lockTaken = false;
|
||||
try
|
||||
{
|
||||
this.m_lock.Enter(ref lockTaken);
|
||||
if (this.m_waitQueue.TryDequeue(out var waitData))
|
||||
{
|
||||
if (autoSign)
|
||||
{
|
||||
result.Sign = this.GetSign();
|
||||
}
|
||||
waitData.SetResult(result);
|
||||
this.m_waitDic.Add(result.Sign, waitData);
|
||||
return waitData;
|
||||
}
|
||||
|
||||
// 如果队列中没有可取出的等待数据对象,则新建一个
|
||||
waitData = new TWaitData();
|
||||
// 如果自动签名开启,则为结果对象设置签名
|
||||
if (autoSign)
|
||||
{
|
||||
@@ -127,56 +173,80 @@ public class WaitHandlePool<TWaitData, TWaitDataAsync, T> : DisposableObject, IW
|
||||
// 设置等待数据对象的结果
|
||||
waitData.SetResult(result);
|
||||
// 将结果对象的签名和等待数据对象添加到字典中
|
||||
this.m_waitDic.TryAdd(result.Sign, waitData);
|
||||
this.m_waitDic.Add(result.Sign, waitData);
|
||||
return waitData;
|
||||
}
|
||||
|
||||
// 如果队列中没有可取出的等待数据对象,则新建一个
|
||||
waitData = new TWaitData();
|
||||
// 如果自动签名开启,则为结果对象设置签名
|
||||
if (autoSign)
|
||||
finally
|
||||
{
|
||||
result.Sign = this.GetSign();
|
||||
if (lockTaken)
|
||||
{
|
||||
this.m_lock.Exit(false);
|
||||
}
|
||||
}
|
||||
// 设置等待数据对象的结果
|
||||
waitData.SetResult(result);
|
||||
// 将结果对象的签名和等待数据对象添加到字典中
|
||||
this.m_waitDic.TryAdd(result.Sign, waitData);
|
||||
return waitData;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public TWaitData GetWaitData(out int sign)
|
||||
{
|
||||
// 尝试从同步等待队列中取出一个等待数据对象
|
||||
if (this.m_waitQueue.TryDequeue(out var waitData))
|
||||
var lockTaken = false;
|
||||
try
|
||||
{
|
||||
this.m_lock.Enter(ref lockTaken);
|
||||
// 尝试从同步等待队列中取出一个等待数据对象
|
||||
if (this.m_waitQueue.TryDequeue(out var waitData))
|
||||
{
|
||||
// 生成签名
|
||||
sign = this.GetSign();
|
||||
// 设置等待数据对象的默认结果
|
||||
waitData.SetResult(default);
|
||||
// 将签名和等待数据对象添加到字典中
|
||||
this.m_waitDic.Add(sign, waitData);
|
||||
return waitData;
|
||||
}
|
||||
|
||||
// 如果队列中没有可取出的等待数据对象,则新建一个
|
||||
waitData = new TWaitData();
|
||||
// 生成签名
|
||||
sign = this.GetSign();
|
||||
// 设置等待数据对象的默认结果
|
||||
waitData.SetResult(default);
|
||||
// 将签名和等待数据对象添加到字典中
|
||||
this.m_waitDic.TryAdd(sign, waitData);
|
||||
this.m_waitDic.Add(sign, waitData);
|
||||
return waitData;
|
||||
}
|
||||
|
||||
// 如果队列中没有可取出的等待数据对象,则新建一个
|
||||
waitData = new TWaitData();
|
||||
// 生成签名
|
||||
sign = this.GetSign();
|
||||
// 设置等待数据对象的默认结果
|
||||
waitData.SetResult(default);
|
||||
// 将签名和等待数据对象添加到字典中
|
||||
this.m_waitDic.TryAdd(sign, waitData);
|
||||
return waitData;
|
||||
finally
|
||||
{
|
||||
if (lockTaken)
|
||||
{
|
||||
this.m_lock.Exit(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public TWaitDataAsync GetWaitDataAsync(T result, bool autoSign = true)
|
||||
{
|
||||
// 尝试从异步等待队列中取出一个等待数据对象
|
||||
if (this.m_waitQueueAsync.TryDequeue(out var waitData))
|
||||
var lockTaken = false;
|
||||
try
|
||||
{
|
||||
this.m_lock.Enter(ref lockTaken);
|
||||
// 尝试从异步等待队列中取出一个等待数据对象
|
||||
if (this.m_waitQueueAsync.TryDequeue(out var waitData))
|
||||
{
|
||||
// 如果自动签名开启,则为结果对象设置签名
|
||||
if (autoSign)
|
||||
{
|
||||
result.Sign = this.GetSign();
|
||||
}
|
||||
// 设置等待数据对象的结果
|
||||
waitData.SetResult(result);
|
||||
// 将结果对象的签名和等待数据对象添加到字典中
|
||||
this.m_waitDicAsync.Add(result.Sign, waitData);
|
||||
return waitData;
|
||||
}
|
||||
|
||||
// 如果队列中没有可取出的等待数据对象,则新建一个
|
||||
waitData = new TWaitDataAsync();
|
||||
// 如果自动签名开启,则为结果对象设置签名
|
||||
if (autoSign)
|
||||
{
|
||||
@@ -185,119 +255,185 @@ public class WaitHandlePool<TWaitData, TWaitDataAsync, T> : DisposableObject, IW
|
||||
// 设置等待数据对象的结果
|
||||
waitData.SetResult(result);
|
||||
// 将结果对象的签名和等待数据对象添加到字典中
|
||||
this.m_waitDicAsync.TryAdd(result.Sign, waitData);
|
||||
this.m_waitDicAsync.Add(result.Sign, waitData);
|
||||
return waitData;
|
||||
}
|
||||
|
||||
// 如果队列中没有可取出的等待数据对象,则新建一个
|
||||
waitData = new TWaitDataAsync();
|
||||
// 如果自动签名开启,则为结果对象设置签名
|
||||
if (autoSign)
|
||||
finally
|
||||
{
|
||||
result.Sign = this.GetSign();
|
||||
if (lockTaken)
|
||||
{
|
||||
this.m_lock.Exit(false);
|
||||
}
|
||||
}
|
||||
// 设置等待数据对象的结果
|
||||
waitData.SetResult(result);
|
||||
// 将结果对象的签名和等待数据对象添加到字典中
|
||||
this.m_waitDicAsync.TryAdd(result.Sign, waitData);
|
||||
return waitData;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public TWaitDataAsync GetWaitDataAsync(out int sign)
|
||||
{
|
||||
// 尝试从异步等待队列中取出一个等待数据对象
|
||||
if (this.m_waitQueueAsync.TryDequeue(out var waitData))
|
||||
var lockTaken = false;
|
||||
try
|
||||
{
|
||||
this.m_lock.Enter(ref lockTaken);
|
||||
// 尝试从异步等待队列中取出一个等待数据对象
|
||||
if (this.m_waitQueueAsync.TryDequeue(out var waitData))
|
||||
{
|
||||
// 生成签名
|
||||
sign = this.GetSign();
|
||||
// 设置等待数据对象的默认结果
|
||||
waitData.SetResult(default);
|
||||
// 将签名和等待数据对象添加到字典中
|
||||
this.m_waitDicAsync.Add(sign, waitData);
|
||||
return waitData;
|
||||
}
|
||||
|
||||
// 如果队列中没有可取出的等待数据对象,则新建一个
|
||||
waitData = new TWaitDataAsync();
|
||||
// 生成签名
|
||||
sign = this.GetSign();
|
||||
// 设置等待数据对象的默认结果
|
||||
waitData.SetResult(default);
|
||||
// 将签名和等待数据对象添加到字典中
|
||||
this.m_waitDicAsync.TryAdd(sign, waitData);
|
||||
this.m_waitDicAsync.Add(sign, waitData);
|
||||
return waitData;
|
||||
}
|
||||
|
||||
// 如果队列中没有可取出的等待数据对象,则新建一个
|
||||
waitData = new TWaitDataAsync();
|
||||
// 生成签名
|
||||
sign = this.GetSign();
|
||||
// 设置等待数据对象的默认结果
|
||||
waitData.SetResult(default);
|
||||
// 将签名和等待数据对象添加到字典中
|
||||
this.m_waitDicAsync.TryAdd(sign, waitData);
|
||||
return waitData;
|
||||
finally
|
||||
{
|
||||
if (lockTaken)
|
||||
{
|
||||
this.m_lock.Exit(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool SetRun(int sign)
|
||||
{
|
||||
// 尝试从异步等待数据字典中获取并设置等待数据
|
||||
if (this.m_waitDicAsync.TryGetValue(sign, out var waitDataAsync))
|
||||
var lockTaken = false;
|
||||
try
|
||||
{
|
||||
waitDataAsync.Set();
|
||||
return true;
|
||||
}
|
||||
this.m_lock.Enter(ref lockTaken);
|
||||
// 尝试从异步等待数据字典中获取并设置等待数据
|
||||
if (this.m_waitDicAsync.TryGetValue(sign, out var waitDataAsync))
|
||||
{
|
||||
waitDataAsync.Set();
|
||||
return true;
|
||||
}
|
||||
|
||||
// 尝试从同步等待数据字典中获取并设置等待数据
|
||||
if (this.m_waitDic.TryGetValue(sign, out var waitData))
|
||||
// 尝试从同步等待数据字典中获取并设置等待数据
|
||||
if (this.m_waitDic.TryGetValue(sign, out var waitData))
|
||||
{
|
||||
waitData.Set();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
waitData.Set();
|
||||
return true;
|
||||
if (lockTaken)
|
||||
{
|
||||
this.m_lock.Exit(false);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool SetRun(int sign, T waitResult)
|
||||
{
|
||||
// 尝试从异步等待数据字典中获取并设置等待数据
|
||||
if (this.m_waitDicAsync.TryGetValue(sign, out var waitDataAsync))
|
||||
var lockTaken = false;
|
||||
try
|
||||
{
|
||||
waitDataAsync.Set(waitResult);
|
||||
return true;
|
||||
}
|
||||
// 尝试从同步等待数据字典中获取并设置等待数据
|
||||
if (this.m_waitDic.TryGetValue(sign, out var waitData))
|
||||
{
|
||||
waitData.Set(waitResult);
|
||||
return true;
|
||||
}
|
||||
this.m_lock.Enter(ref lockTaken);
|
||||
// 尝试从异步等待数据字典中获取并设置等待数据
|
||||
if (this.m_waitDicAsync.TryGetValue(sign, out var waitDataAsync))
|
||||
{
|
||||
waitDataAsync.Set(waitResult);
|
||||
return true;
|
||||
}
|
||||
// 尝试从同步等待数据字典中获取并设置等待数据
|
||||
if (this.m_waitDic.TryGetValue(sign, out var waitData))
|
||||
{
|
||||
waitData.Set(waitResult);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (lockTaken)
|
||||
{
|
||||
this.m_lock.Exit(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool SetRun(T waitResult)
|
||||
{
|
||||
// 尝试从异步等待数据字典中获取并设置等待数据
|
||||
if (this.m_waitDicAsync.TryGetValue(waitResult.Sign, out var waitDataAsync))
|
||||
var lockTaken = false;
|
||||
try
|
||||
{
|
||||
waitDataAsync.Set(waitResult);
|
||||
return true;
|
||||
}
|
||||
this.m_lock.Enter(ref lockTaken);
|
||||
// 尝试从异步等待数据字典中获取并设置等待数据
|
||||
if (this.m_waitDicAsync.TryGetValue(waitResult.Sign, out var waitDataAsync))
|
||||
{
|
||||
waitDataAsync.Set(waitResult);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 尝试从同步等待数据字典中获取并设置等待数据
|
||||
if (this.m_waitDic.TryGetValue(waitResult.Sign, out var waitData))
|
||||
// 尝试从同步等待数据字典中获取并设置等待数据
|
||||
if (this.m_waitDic.TryGetValue(waitResult.Sign, out var waitData))
|
||||
{
|
||||
waitData.Set(waitResult);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
waitData.Set(waitResult);
|
||||
return true;
|
||||
if (lockTaken)
|
||||
{
|
||||
this.m_lock.Exit(false);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TryGetData(int sign, out TWaitData waitData)
|
||||
{
|
||||
return this.m_waitDic.TryGetValue(sign, out waitData);
|
||||
var lockTaken = false;
|
||||
try
|
||||
{
|
||||
this.m_lock.Enter(ref lockTaken);
|
||||
return this.m_waitDic.TryGetValue(sign, out waitData);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (lockTaken)
|
||||
{
|
||||
this.m_lock.Exit(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool TryGetDataAsync(int sign, out TWaitDataAsync waitDataAsync)
|
||||
{
|
||||
return this.m_waitDicAsync.TryGetValue(sign, out waitDataAsync);
|
||||
var lockTaken = false;
|
||||
try
|
||||
{
|
||||
this.m_lock.Enter(ref lockTaken);
|
||||
return this.m_waitDicAsync.TryGetValue(sign, out waitDataAsync);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (lockTaken)
|
||||
{
|
||||
this.m_lock.Exit(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -305,17 +441,29 @@ public class WaitHandlePool<TWaitData, TWaitDataAsync, T> : DisposableObject, IW
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
foreach (var item in this.m_waitDic.Values)
|
||||
var lockTaken = false;
|
||||
try
|
||||
{
|
||||
item.SafeDispose();
|
||||
}
|
||||
foreach (var item in this.m_waitQueue)
|
||||
{
|
||||
item.SafeDispose();
|
||||
}
|
||||
this.m_waitDic.Clear();
|
||||
this.m_lock.Enter(ref lockTaken);
|
||||
foreach (var item in this.m_waitDic.Values)
|
||||
{
|
||||
item.SafeDispose();
|
||||
}
|
||||
foreach (var item in this.m_waitQueue)
|
||||
{
|
||||
item.SafeDispose();
|
||||
}
|
||||
this.m_waitDic.Clear();
|
||||
|
||||
this.m_waitQueue.Clear();
|
||||
this.m_waitQueue.Clear();
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (lockTaken)
|
||||
{
|
||||
this.m_lock.Exit(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
@@ -324,7 +472,11 @@ public class WaitHandlePool<TWaitData, TWaitDataAsync, T> : DisposableObject, IW
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private int GetSign()
|
||||
{
|
||||
Interlocked.CompareExchange(ref this.m_currentSign, 0, this.m_maxSign);
|
||||
return Interlocked.Increment(ref this.m_currentSign);
|
||||
var sign = this.m_currentSign++;
|
||||
if (this.m_currentSign >= this.m_maxSign)
|
||||
{
|
||||
this.m_currentSign = this.m_minSign;
|
||||
}
|
||||
return sign;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
|
||||
<Application>
|
||||
<Assembly Name="TouchSocket.Core" Dynamic="Required All"></Assembly>
|
||||
</Application>
|
||||
</Directives>
|
||||
@@ -13,6 +13,7 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -25,7 +26,7 @@ namespace TouchSocket.Dmtp;
|
||||
/// <summary>
|
||||
/// 提供Dmtp协议的最基础功能件
|
||||
/// </summary>
|
||||
public abstract class DmtpActor : DependencyObject, IDmtpActor
|
||||
public abstract class DmtpActor : DisposableObject, IDmtpActor
|
||||
{
|
||||
#region 委托
|
||||
|
||||
@@ -94,7 +95,7 @@ public abstract class DmtpActor : DependencyObject, IDmtpActor
|
||||
public bool IsReliable { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public DateTime LastActiveTime { get; protected set; }
|
||||
public DateTimeOffset LastActiveTime { get; protected set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ILog Logger { get; set; }
|
||||
@@ -112,6 +113,7 @@ public abstract class DmtpActor : DependencyObject, IDmtpActor
|
||||
private readonly AsyncResetEvent m_handshakeFinished = new AsyncResetEvent(false, false);
|
||||
private CancellationTokenSource m_cancellationTokenSource;
|
||||
private readonly Lock m_syncRoot = new Lock();
|
||||
private Dictionary<Type, IActor> m_actors = new Dictionary<Type, IActor>();
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
@@ -123,7 +125,7 @@ public abstract class DmtpActor : DependencyObject, IDmtpActor
|
||||
{
|
||||
this.WaitHandlePool = new WaitHandlePool<IWaitResult>();
|
||||
this.AllowRoute = allowRoute;
|
||||
this.LastActiveTime = DateTime.UtcNow;
|
||||
this.LastActiveTime = DateTimeOffset.UtcNow;
|
||||
this.IsReliable = isReliable;
|
||||
}
|
||||
|
||||
@@ -180,12 +182,12 @@ public abstract class DmtpActor : DependencyObject, IDmtpActor
|
||||
{
|
||||
this.Id = verifyResult.Id;
|
||||
this.Online = true;
|
||||
_ = Task.Factory.StartNew(this.PrivateOnHandshaked, new DmtpVerifyEventArgs()
|
||||
_ = EasyTask.SafeRun(this.PrivateOnHandshaked, new DmtpVerifyEventArgs()
|
||||
{
|
||||
Id = verifyResult.Id,
|
||||
Metadata = verifyResult.Metadata,
|
||||
Token = verifyResult.Token,
|
||||
}).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
});
|
||||
|
||||
this.m_cancellationTokenSource = new CancellationTokenSource();
|
||||
this.m_handshakeFinished.Set();
|
||||
@@ -230,6 +232,13 @@ public abstract class DmtpActor : DependencyObject, IDmtpActor
|
||||
this.Online = false;
|
||||
this.WaitHandlePool.CancelAll();
|
||||
this.m_cancellationTokenSource?.Cancel();
|
||||
|
||||
foreach (var item in this.m_actors)
|
||||
{
|
||||
item.Value.SafeDispose();
|
||||
}
|
||||
|
||||
this.m_actors.Clear();
|
||||
}
|
||||
|
||||
if (manual || this.Closing == null)
|
||||
@@ -295,15 +304,9 @@ public abstract class DmtpActor : DependencyObject, IDmtpActor
|
||||
return EasyTask.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task PrivateOnHandshaked(object obj)
|
||||
private async Task PrivateOnHandshaked(DmtpVerifyEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
await this.OnHandshaked((DmtpVerifyEventArgs)obj);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
await this.OnHandshaked(e);
|
||||
}
|
||||
|
||||
private async Task PrivateOnCreatedChannel(object obj)
|
||||
@@ -394,7 +397,7 @@ public abstract class DmtpActor : DependencyObject, IDmtpActor
|
||||
/// <returns></returns>
|
||||
public virtual async Task<bool> InputReceivedData(DmtpMessage message)
|
||||
{
|
||||
this.LastActiveTime = DateTime.UtcNow;
|
||||
this.LastActiveTime = DateTimeOffset.UtcNow;
|
||||
var byteBlock = message.BodyByteBlock;
|
||||
switch (message.ProtocolFlags)
|
||||
{
|
||||
@@ -433,7 +436,7 @@ public abstract class DmtpActor : DependencyObject, IDmtpActor
|
||||
this.Online = true;
|
||||
args.Message ??= TouchSocketCoreResource.OperationSuccessful;
|
||||
this.m_cancellationTokenSource = new CancellationTokenSource();
|
||||
_ = Task.Factory.StartNew(this.PrivateOnHandshaked, args).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
_ = EasyTask.SafeRun(this.PrivateOnHandshaked, args);
|
||||
}
|
||||
else//不允许连接
|
||||
{
|
||||
@@ -783,7 +786,7 @@ public abstract class DmtpActor : DependencyObject, IDmtpActor
|
||||
/// <inheritdoc/>
|
||||
public virtual async Task SendPackageAsync(ushort protocol, IPackage package)
|
||||
{
|
||||
using (var byteBlock = new ByteBlock())
|
||||
using (var byteBlock = new ByteBlock(1024 * 64))
|
||||
{
|
||||
var block = byteBlock;
|
||||
package.Package(ref block);
|
||||
@@ -874,6 +877,33 @@ public abstract class DmtpActor : DependencyObject, IDmtpActor
|
||||
}
|
||||
}
|
||||
|
||||
#region Actor
|
||||
/// <inheritdoc/>
|
||||
public void AddActor<TActor>(TActor actor) where TActor : class, IActor
|
||||
{
|
||||
ThrowHelper.ThrowArgumentNullExceptionIf(actor, nameof(actor));
|
||||
var type = typeof(TActor);
|
||||
|
||||
if (this.m_actors.ContainsKey(type))
|
||||
{
|
||||
ThrowHelper.ThrowException(TouchSocketDmtpResource.ActorAlreadyExists.Format(type));
|
||||
}
|
||||
this.m_actors.Add(type, actor);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public TActor GetActor<TActor>() where TActor : class, IActor
|
||||
{
|
||||
var type = typeof(TActor);
|
||||
if (this.m_actors.TryGetValue(type, out var actor))
|
||||
{
|
||||
return (TActor)actor;
|
||||
}
|
||||
return default;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 断开
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -906,26 +936,38 @@ public abstract class DmtpActor : DependencyObject, IDmtpActor
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task CloseAsync(string msg)
|
||||
public async Task<Result> CloseAsync(string msg, CancellationToken token = default)
|
||||
{
|
||||
await this.OnClosed(true, msg).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
try
|
||||
{
|
||||
await this.OnClosed(true, msg).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
return Result.Success;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.FromException(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<bool> SendCloseAsync(string msg)
|
||||
/// <summary>
|
||||
/// 异步发送关闭消息
|
||||
/// </summary>
|
||||
/// <param name="msg"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<Result> SendCloseAsync(string msg)
|
||||
{
|
||||
if (!this.Online)
|
||||
{
|
||||
return false;
|
||||
return Result.FromFail(TouchSocketResource.ClientNotConnected);
|
||||
}
|
||||
try
|
||||
{
|
||||
await this.SendStringAsync(0, msg).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
return true;
|
||||
return Result.Success;
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
return false;
|
||||
return Result.FromException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -945,14 +987,8 @@ public abstract class DmtpActor : DependencyObject, IDmtpActor
|
||||
|
||||
await this.OutputSendAsync.Invoke(this, byteBlock.Memory).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
//var transferBytes = new ArraySegment<byte>[]
|
||||
//{
|
||||
// new ArraySegment<byte>(DmtpMessage.Head),
|
||||
// new ArraySegment<byte>(TouchSocketBitConverter.BigEndian.GetBytes(protocol)),
|
||||
// new ArraySegment<byte>(TouchSocketBitConverter.BigEndian.GetBytes(length)),
|
||||
// new ArraySegment<byte>(buffer,offset,length)
|
||||
//};
|
||||
this.LastActiveTime = DateTime.UtcNow;
|
||||
|
||||
this.LastActiveTime = DateTimeOffset.UtcNow;
|
||||
}
|
||||
|
||||
#endregion 协议异步发送
|
||||
@@ -1070,7 +1106,7 @@ public abstract class DmtpActor : DependencyObject, IDmtpActor
|
||||
}
|
||||
}
|
||||
|
||||
var byteBlock = new ByteBlock();
|
||||
var byteBlock = new ByteBlock(1024*64);
|
||||
var waitCreateChannel = new WaitCreateChannelPackage()
|
||||
{
|
||||
Random = random,
|
||||
@@ -1151,7 +1187,7 @@ public abstract class DmtpActor : DependencyObject, IDmtpActor
|
||||
channel.SetId(id);
|
||||
if (this.m_userChannels.TryAdd(id, channel))
|
||||
{
|
||||
_ = Task.Factory.StartNew(this.PrivateOnCreatedChannel, new CreateChannelEventArgs(id, metadata));
|
||||
_ = EasyTask.SafeRun(this.PrivateOnCreatedChannel, new CreateChannelEventArgs(id, metadata));
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace TouchSocket.Dmtp;
|
||||
/// <summary>
|
||||
/// 提供Dmtp协议的最基础功能件
|
||||
/// </summary>
|
||||
public interface IDmtpActor : IDependencyObject, IOnlineClient, IClosableClient, IIdClient
|
||||
public interface IDmtpActor : IDisposableObject, IOnlineClient, IClosableClient, IIdClient
|
||||
{
|
||||
#region 属性
|
||||
|
||||
@@ -35,6 +35,11 @@ public interface IDmtpActor : IDependencyObject, IOnlineClient, IClosableClient,
|
||||
/// </summary>
|
||||
IDmtpActorObject Client { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 关闭标记
|
||||
/// </summary>
|
||||
CancellationToken ClosedToken { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否基于可靠协议构建。例如:基于Tcp则为<see langword="true"/>,基于Udp则为<see langword="false"/>。
|
||||
/// </summary>
|
||||
@@ -43,7 +48,7 @@ public interface IDmtpActor : IDependencyObject, IOnlineClient, IClosableClient,
|
||||
/// <summary>
|
||||
/// 最后一次活动时间。
|
||||
/// </summary>
|
||||
DateTime LastActiveTime { get; }
|
||||
DateTimeOffset LastActiveTime { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 日志
|
||||
@@ -55,11 +60,6 @@ public interface IDmtpActor : IDependencyObject, IOnlineClient, IClosableClient,
|
||||
/// </summary>
|
||||
WaitHandlePool<IWaitResult> WaitHandlePool { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 关闭标记
|
||||
/// </summary>
|
||||
CancellationToken ClosedToken { get; }
|
||||
|
||||
#endregion 属性
|
||||
|
||||
#region IDmtpChannel
|
||||
@@ -115,6 +115,20 @@ public interface IDmtpActor : IDependencyObject, IOnlineClient, IClosableClient,
|
||||
|
||||
#region 方法
|
||||
|
||||
/// <summary>
|
||||
/// 添加一个实现了 <see cref="IActor"/> 接口的 Actor 实例。
|
||||
/// </summary>
|
||||
/// <typeparam name="TActor">Actor 的具体类型,必须实现 <see cref="IActor"/> 接口。</typeparam>
|
||||
/// <param name="actor">要添加的 Actor 实例。</param>
|
||||
void AddActor<TActor>(TActor actor) where TActor : class, IActor;
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定类型的 Actor 实例。
|
||||
/// </summary>
|
||||
/// <typeparam name="TActor">Actor 的具体类型,必须实现 <see cref="IActor"/> 接口。</typeparam>
|
||||
/// <returns>返回指定类型的 Actor 实例。</returns>
|
||||
TActor GetActor<TActor>() where TActor : class, IActor;
|
||||
|
||||
/// <summary>
|
||||
/// 向当前对点发送一个Ping报文,并且等待回应。
|
||||
/// </summary>
|
||||
|
||||
@@ -80,7 +80,7 @@ public partial interface IDmtpChannel : IDisposable, IEnumerable<ByteBlock>
|
||||
/// <summary>
|
||||
/// 获取上次操作的时间。
|
||||
/// </summary>
|
||||
DateTime LastOperationTime { get; }
|
||||
DateTimeOffset LastOperationTime { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 异步取消操作
|
||||
|
||||
@@ -28,13 +28,13 @@ internal sealed partial class InternalChannel : DisposableObject, IDmtpChannel
|
||||
private readonly ConcurrentQueue<ChannelPackage> m_dataQueue;
|
||||
private readonly FlowGate m_flowGate;
|
||||
private ByteBlock m_currentData;
|
||||
private DateTime m_lastOperationTime;
|
||||
private DateTimeOffset m_lastOperationTime;
|
||||
private long m_maxSpeed;
|
||||
|
||||
public InternalChannel(DmtpActor client, string targetId, Metadata metadata)
|
||||
{
|
||||
this.m_actor = client;
|
||||
this.m_lastOperationTime = DateTime.UtcNow;
|
||||
this.m_lastOperationTime = DateTimeOffset.UtcNow;
|
||||
this.TargetId = targetId;
|
||||
this.Status = ChannelStatus.Default;
|
||||
this.m_dataQueue = new ConcurrentQueue<ChannelPackage>();
|
||||
@@ -66,7 +66,7 @@ internal sealed partial class InternalChannel : DisposableObject, IDmtpChannel
|
||||
|
||||
public string LastOperationMes { get; private set; }
|
||||
|
||||
public DateTime LastOperationTime { get => this.m_lastOperationTime; }
|
||||
public DateTimeOffset LastOperationTime => this.m_lastOperationTime;
|
||||
|
||||
public long MaxSpeed
|
||||
{
|
||||
@@ -113,7 +113,7 @@ internal sealed partial class InternalChannel : DisposableObject, IDmtpChannel
|
||||
TargetId = this.TargetId
|
||||
};
|
||||
await this.m_actor.SendChannelPackageAsync(channelPackage).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
this.m_lastOperationTime = DateTime.UtcNow;
|
||||
this.m_lastOperationTime = DateTimeOffset.UtcNow;
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -138,7 +138,7 @@ internal sealed partial class InternalChannel : DisposableObject, IDmtpChannel
|
||||
TargetId = this.TargetId
|
||||
};
|
||||
await this.m_actor.SendChannelPackageAsync(channelPackage).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
this.m_lastOperationTime = DateTime.UtcNow;
|
||||
this.m_lastOperationTime = DateTimeOffset.UtcNow;
|
||||
}
|
||||
|
||||
public async Task HoldOnAsync(string operationMes = null)
|
||||
@@ -157,7 +157,7 @@ internal sealed partial class InternalChannel : DisposableObject, IDmtpChannel
|
||||
TargetId = this.TargetId
|
||||
};
|
||||
await this.m_actor.SendChannelPackageAsync(channelPackage).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
this.m_lastOperationTime = DateTime.UtcNow;
|
||||
this.m_lastOperationTime = DateTimeOffset.UtcNow;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
@@ -181,7 +181,7 @@ internal sealed partial class InternalChannel : DisposableObject, IDmtpChannel
|
||||
TargetId = this.TargetId
|
||||
};
|
||||
this.m_actor.SendChannelPackageAsync(channelPackage).GetFalseAwaitResult();
|
||||
this.m_lastOperationTime = DateTime.UtcNow;
|
||||
this.m_lastOperationTime = DateTimeOffset.UtcNow;
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -331,13 +331,13 @@ internal sealed partial class InternalChannel : DisposableObject, IDmtpChannel
|
||||
using (channelPackage.Data)
|
||||
{
|
||||
await this.m_actor.SendChannelPackageAsync(channelPackage).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
this.m_lastOperationTime = DateTime.UtcNow;
|
||||
this.m_lastOperationTime = DateTimeOffset.UtcNow;
|
||||
}
|
||||
}
|
||||
|
||||
internal void ReceivedData(ChannelPackage channelPackage)
|
||||
{
|
||||
this.m_lastOperationTime = DateTime.UtcNow;
|
||||
this.m_lastOperationTime = DateTimeOffset.UtcNow;
|
||||
if (channelPackage.RunNow)
|
||||
{
|
||||
switch (channelPackage.DataType)
|
||||
@@ -435,14 +435,14 @@ internal sealed partial class InternalChannel : DisposableObject, IDmtpChannel
|
||||
private bool Wait()
|
||||
{
|
||||
var spinWait = new SpinWait();
|
||||
var now = DateTime.UtcNow;
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
while (true)
|
||||
{
|
||||
if (this.m_dataQueue.Count > 0)
|
||||
if (!this.m_dataQueue.IsEmpty)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (DateTime.UtcNow - now > this.Timeout)
|
||||
if (DateTimeOffset.UtcNow - now > this.Timeout)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -452,14 +452,14 @@ internal sealed partial class InternalChannel : DisposableObject, IDmtpChannel
|
||||
|
||||
private async Task<bool> WaitAsync()
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
while (true)
|
||||
{
|
||||
if (this.m_dataQueue.Count > 0)
|
||||
if (!this.m_dataQueue.IsEmpty) // Replaced Count with IsEmpty
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (DateTime.UtcNow - now > this.Timeout)
|
||||
if (DateTimeOffset.UtcNow - now > this.Timeout)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ using System.Threading.Tasks;
|
||||
using TouchSocket.Core;
|
||||
using TouchSocket.Http;
|
||||
using TouchSocket.Sockets;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace TouchSocket.Dmtp;
|
||||
|
||||
@@ -113,8 +114,9 @@ public partial class HttpDmtpClient : HttpClientBase, IHttpDmtpClient
|
||||
/// 发送<see cref="IDmtpActor"/>关闭消息。
|
||||
/// </summary>
|
||||
/// <param name="msg">关闭消息的内容</param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns>异步任务</returns>
|
||||
public override async Task CloseAsync(string msg)
|
||||
public override async Task<Result> CloseAsync(string msg, CancellationToken token = default)
|
||||
{
|
||||
// 检查是否已初始化IDmtpActor对象
|
||||
if (this.m_dmtpActor != null)
|
||||
@@ -122,10 +124,11 @@ public partial class HttpDmtpClient : HttpClientBase, IHttpDmtpClient
|
||||
// 向IDmtpActor对象发送关闭消息
|
||||
await this.m_dmtpActor.SendCloseAsync(msg).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
// 关闭IDmtpActor对象
|
||||
await this.m_dmtpActor.CloseAsync(msg).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
await this.m_dmtpActor.CloseAsync(msg,token).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
// 调用基类的关闭方法
|
||||
await base.CloseAsync(msg).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
return await base.CloseAsync(msg,token).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
#endregion 断开
|
||||
@@ -291,7 +294,7 @@ public partial class HttpDmtpClient : HttpClientBase, IHttpDmtpClient
|
||||
/// <para>
|
||||
/// 该触发条件有2种:
|
||||
/// <list type="number">
|
||||
/// <item>终端主动调用<see cref="CloseAsync(string)"/>。</item>
|
||||
/// <item>终端主动调用<see cref="IClosableClient.CloseAsync(string, System.Threading.CancellationToken)"/>。</item>
|
||||
/// <item>终端收到<see cref="DmtpActor.P0_Close"/>的请求。</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TouchSocket.Core;
|
||||
using TouchSocket.Http;
|
||||
@@ -24,8 +25,8 @@ namespace TouchSocket.Dmtp;
|
||||
/// </summary>
|
||||
public abstract class HttpDmtpSessionClient : HttpSessionClient, IHttpDmtpSessionClient
|
||||
{
|
||||
internal Func<DmtpActor> m_internalOnRpcActorInit;
|
||||
private DmtpActor m_dmtpActor;
|
||||
internal Func<SealedDmtpActor> m_internalOnRpcActorInit;
|
||||
private SealedDmtpActor m_dmtpActor;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IDmtpActor DmtpActor => this.m_dmtpActor;
|
||||
@@ -46,14 +47,14 @@ public abstract class HttpDmtpSessionClient : HttpSessionClient, IHttpDmtpSessio
|
||||
#region 断开
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task CloseAsync(string msg)
|
||||
public override async Task<Result> CloseAsync(string msg, CancellationToken token = default)
|
||||
{
|
||||
if (this.m_dmtpActor != null)
|
||||
{
|
||||
await this.m_dmtpActor.CloseAsync(msg).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
await this.m_dmtpActor.CloseAsync(msg,token).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
await base.CloseAsync(msg).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
return await base.CloseAsync(msg,token).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -92,12 +93,11 @@ public abstract class HttpDmtpSessionClient : HttpSessionClient, IHttpDmtpSessio
|
||||
await this.ProtectedResetIdAsync(e.NewId).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
private void SetRpcActor(DmtpActor actor)
|
||||
private void SetRpcActor(SealedDmtpActor actor)
|
||||
{
|
||||
actor.Id = this.Id;
|
||||
actor.IdChanged = this.OnDmtpIdChanged;
|
||||
actor.OutputSendAsync = this.ThisDmtpActorOutputSendAsync;
|
||||
//actor.OutputSend = this.ThisDmtpActorOutputSend;
|
||||
actor.Client = this;
|
||||
actor.Closing = this.OnDmtpActorClose;
|
||||
actor.Routing = this.OnDmtpActorRouting;
|
||||
@@ -242,7 +242,7 @@ public abstract class HttpDmtpSessionClient : HttpSessionClient, IHttpDmtpSessio
|
||||
/// <para>
|
||||
/// 该触发条件有2种:
|
||||
/// <list type="number">
|
||||
/// <item>终端主动调用<see cref="CloseAsync(string)"/>。</item>
|
||||
/// <item>终端主动调用<see cref="IClosableClient.CloseAsync(string, System.Threading.CancellationToken)"/>。</item>
|
||||
/// <item>终端收到<see cref="DmtpActor.P0_Close"/>的请求。</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
|
||||
@@ -58,8 +58,9 @@ public partial class TcpDmtpClient : TcpClientBase, ITcpDmtpClient
|
||||
/// 发送<see cref="IDmtpActor"/>关闭消息。
|
||||
/// </summary>
|
||||
/// <param name="msg">关闭消息的内容</param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns>异步任务</returns>
|
||||
public override async Task CloseAsync(string msg)
|
||||
public override async Task<Result> CloseAsync(string msg, CancellationToken token = default)
|
||||
{
|
||||
// 检查是否已初始化IDmtpActor对象
|
||||
if (this.m_dmtpActor != null)
|
||||
@@ -67,11 +68,11 @@ public partial class TcpDmtpClient : TcpClientBase, ITcpDmtpClient
|
||||
// 向IDmtpActor对象发送关闭消息
|
||||
await this.m_dmtpActor.SendCloseAsync(msg).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
// 关闭IDmtpActor对象
|
||||
await this.m_dmtpActor.CloseAsync(msg).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
await this.m_dmtpActor.CloseAsync(msg,token).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
// 调用基类的CloseAsync方法完成后续关闭操作
|
||||
await base.CloseAsync(msg).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
return await base.CloseAsync(msg,token).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
#endregion 断开
|
||||
@@ -201,7 +202,7 @@ public partial class TcpDmtpClient : TcpClientBase, ITcpDmtpClient
|
||||
/// <para>
|
||||
/// 该触发条件有2种:
|
||||
/// <list type="number">
|
||||
/// <item>终端主动调用<see cref="CloseAsync(string)"/>。</item>
|
||||
/// <item>终端主动调用<see cref="IClosableClient.CloseAsync(string, System.Threading.CancellationToken)"/>。</item>
|
||||
/// <item>终端收到<see cref="DmtpActor.P0_Close"/>的请求。</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TouchSocket.Core;
|
||||
using TouchSocket.Sockets;
|
||||
@@ -133,7 +134,7 @@ public abstract class TcpDmtpSessionClient : TcpSessionClientBase, ITcpDmtpSessi
|
||||
/// <para>
|
||||
/// 该触发条件有2种:
|
||||
/// <list type="number">
|
||||
/// <item>终端主动调用<see cref="CloseAsync(string)"/>。</item>
|
||||
/// <item>终端主动调用<see cref="IClosableClient.CloseAsync(string, System.Threading.CancellationToken)"/>。</item>
|
||||
/// <item>终端收到<see cref="DmtpActor.P0_Close"/>的请求。</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
@@ -204,18 +205,19 @@ public abstract class TcpDmtpSessionClient : TcpSessionClientBase, ITcpDmtpSessi
|
||||
/// 发送<see cref="IDmtpActor"/>关闭消息。
|
||||
/// </summary>
|
||||
/// <param name="msg">关闭消息的内容</param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns>异步任务</returns>
|
||||
public override async Task CloseAsync(string msg)
|
||||
public override async Task<Result> CloseAsync(string msg, CancellationToken token = default)
|
||||
{
|
||||
// 检查是否已初始化IDmtpActor接口
|
||||
if (this.m_dmtpActor != null)
|
||||
{
|
||||
// 如果已初始化,则调用IDmtpActor的CloseAsync方法发送关闭消息
|
||||
await this.m_dmtpActor.CloseAsync(msg).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
await this.m_dmtpActor.CloseAsync(msg, token).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
// 调用基类的CloseAsync方法发送关闭消息
|
||||
await base.CloseAsync(msg).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
return await base.CloseAsync(msg, token).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
||||
@@ -19,6 +19,6 @@ namespace TouchSocket.Dmtp;
|
||||
/// 该接口的目的是为UdpDmtp通信协议提供一个标准的服务接口,使得客户端和服务端可以在分布式系统中进行交互。
|
||||
/// 它结合了服务的基本特性、客户端功能以及分布式对象的交互行为。
|
||||
/// </summary>
|
||||
public interface IUdpDmtp : IServiceBase, IClient, IDmtpActorObject
|
||||
public interface IUdpDmtp : IServiceBase, IDependencyClient, IDmtpActorObject
|
||||
{
|
||||
}
|
||||
@@ -42,7 +42,7 @@ public partial class UdpDmtp : UdpSessionBase, IUdpDmtp
|
||||
this.m_udpDmtpClients.RemoveWhen((kv) =>
|
||||
{
|
||||
// 如果客户端最后一次活跃时间距现在超过1分钟,则认为该客户端不活跃
|
||||
if (DateTime.UtcNow - kv.Value.LastActiveTime > TimeSpan.FromMinutes(1))
|
||||
if (DateTimeOffset.UtcNow - kv.Value.LastActiveTime > TimeSpan.FromMinutes(1))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,6 @@ namespace TouchSocket.Dmtp;
|
||||
/// <summary>
|
||||
/// 定义WebSocketDmtp客户端接口,继承多个客户端和配置相关接口
|
||||
/// </summary>
|
||||
public interface IWebSocketDmtpClient : IDmtpClient, IClient, IDmtpActorObject, IOnlineClient, IClosableClient, ISetupConfigObject, ITcpConnectableClient, IIdClient
|
||||
public interface IWebSocketDmtpClient : IDmtpClient, IDependencyClient, IDmtpActorObject, IOnlineClient, IClosableClient, ISetupConfigObject, ITcpConnectableClient, IIdClient
|
||||
{
|
||||
}
|
||||
@@ -12,9 +12,11 @@
|
||||
|
||||
using System;
|
||||
using System.Net.WebSockets;
|
||||
using System.Security;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TouchSocket.Core;
|
||||
using TouchSocket.Http.WebSockets;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace TouchSocket.Dmtp;
|
||||
@@ -23,50 +25,33 @@ namespace TouchSocket.Dmtp;
|
||||
/// WebSocketDmtpClient 类,继承自 SetupConfigObject 并实现了 IWebSocketDmtpClient 接口。
|
||||
/// 该类负责 WebSocket 客户端的配置和管理,提供与 Dmtp 协议相关的功能。
|
||||
/// </summary>
|
||||
public class WebSocketDmtpClient : SetupConfigObject, IWebSocketDmtpClient
|
||||
public class WebSocketDmtpClient : SetupClientWebSocket, IWebSocketDmtpClient
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化WebSocketDmtpClient类的新实例。
|
||||
/// </summary>
|
||||
public WebSocketDmtpClient()
|
||||
{
|
||||
// 初始化接收消息计数器,用于统计每秒接收的消息数量。
|
||||
this.m_receiveCounter = new ValueCounter
|
||||
{
|
||||
Period = TimeSpan.FromSeconds(1), // 设置统计周期为1秒。
|
||||
OnPeriod = this.OnReceivePeriod // 每隔一个周期调用OnReceivePeriod方法处理接收统计逻辑。
|
||||
};
|
||||
// 初始化发送消息计数器,用于统计每秒发送的消息数量。
|
||||
this.m_sentCounter = new ValueCounter
|
||||
{
|
||||
Period = TimeSpan.FromSeconds(1), // 设置统计周期为1秒。
|
||||
OnPeriod = this.OnSendPeriod // 每隔一个周期调用OnSendPeriod方法处理发送统计逻辑。
|
||||
};
|
||||
this.Protocol = DmtpUtility.DmtpProtocol;
|
||||
}
|
||||
|
||||
#region 字段
|
||||
|
||||
private readonly SemaphoreSlim m_semaphoreForConnect = new SemaphoreSlim(1, 1);
|
||||
private ClientWebSocket m_client;
|
||||
private readonly SemaphoreSlim m_connectionSemaphore = new SemaphoreSlim(1, 1);
|
||||
private bool m_allowRoute;
|
||||
private SealedDmtpActor m_dmtpActor;
|
||||
private DmtpAdapter m_dmtpAdapter;
|
||||
private bool m_allowRoute;
|
||||
private Func<string, Task<IDmtpActor>> m_findDmtpActor;
|
||||
private int m_receiveBufferSize = 1024 * 10;
|
||||
private ValueCounter m_receiveCounter;
|
||||
private int m_sendBufferSize = 1024 * 10;
|
||||
private ValueCounter m_sentCounter;
|
||||
private Task m_receiveTask;
|
||||
private bool m_online;
|
||||
private CancellationTokenSource m_tokenSourceForReceive;
|
||||
|
||||
#endregion 字段
|
||||
|
||||
#region 连接
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task ConnectAsync(int millisecondsTimeout, CancellationToken token)
|
||||
public override async Task ConnectAsync(int millisecondsTimeout, CancellationToken token)
|
||||
{
|
||||
await this.m_semaphoreForConnect.WaitTimeAsync(millisecondsTimeout, token).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
await this.m_connectionSemaphore.WaitTimeAsync(millisecondsTimeout, token).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
try
|
||||
{
|
||||
if (this.Online)
|
||||
@@ -74,41 +59,35 @@ public class WebSocketDmtpClient : SetupConfigObject, IWebSocketDmtpClient
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.m_client == null || this.m_client.State != WebSocketState.Open)
|
||||
if (!base.Online)
|
||||
{
|
||||
this.m_client.SafeDispose();
|
||||
this.m_client = new ClientWebSocket();
|
||||
await this.m_client.ConnectAsync(this.RemoteIPHost, token).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
|
||||
this.m_dmtpActor = new SealedDmtpActor(this.m_allowRoute)
|
||||
{
|
||||
//OutputSend = this.OnDmtpActorSend,
|
||||
OutputSendAsync = this.OnDmtpActorSendAsync,
|
||||
Routing = this.OnDmtpActorRouting,
|
||||
Handshaking = this.OnDmtpActorHandshaking,
|
||||
Handshaked = this.OnDmtpActorHandshaked,
|
||||
Closing = this.OnDmtpActorClose,
|
||||
Logger = this.Logger,
|
||||
Client = this,
|
||||
FindDmtpActor = this.m_findDmtpActor,
|
||||
CreatedChannel = this.OnDmtpActorCreateChannel
|
||||
};
|
||||
|
||||
this.m_dmtpAdapter = new DmtpAdapter();
|
||||
this.m_dmtpAdapter.Config(this.Config);
|
||||
|
||||
this.m_receiveTask = Task.Factory.StartNew(this.BeginReceive).Unwrap();
|
||||
this.m_receiveTask.FireAndForget();
|
||||
await base.ConnectAsync(millisecondsTimeout, token);
|
||||
}
|
||||
|
||||
this.m_dmtpActor = new SealedDmtpActor(this.m_allowRoute)
|
||||
{
|
||||
OutputSendAsync = this.OnDmtpActorSendAsync,
|
||||
Routing = this.OnDmtpActorRouting,
|
||||
Handshaking = this.OnDmtpActorHandshaking,
|
||||
Handshaked = this.OnDmtpActorHandshaked,
|
||||
Closing = this.OnDmtpActorClose,
|
||||
Logger = this.Logger,
|
||||
Client = this,
|
||||
FindDmtpActor = this.m_findDmtpActor,
|
||||
CreatedChannel = this.OnDmtpActorCreateChannel
|
||||
};
|
||||
|
||||
this.m_dmtpAdapter = new DmtpAdapter();
|
||||
this.m_dmtpAdapter.Config(this.Config);
|
||||
this.m_tokenSourceForReceive = new CancellationTokenSource();
|
||||
|
||||
var option = this.Config.GetValue(DmtpConfigExtension.DmtpOptionProperty);
|
||||
|
||||
await this.m_dmtpActor.HandshakeAsync(option.VerifyToken, option.Id, millisecondsTimeout, option.Metadata, token).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
this.m_online = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.m_semaphoreForConnect.Release();
|
||||
this.m_connectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,38 +103,33 @@ public class WebSocketDmtpClient : SetupConfigObject, IWebSocketDmtpClient
|
||||
public bool IsClient => true;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public DateTime LastReceivedTime => this.m_receiveCounter.LastIncrement;
|
||||
public override bool Online => base.Online && this.m_dmtpActor != null && this.m_dmtpActor.Online;
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public DateTime LastSentTime => this.m_sentCounter.LastIncrement;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Online => this.m_online;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Protocol Protocol { get; protected set; } = DmtpUtility.DmtpProtocol;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IPHost RemoteIPHost { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 发送<see cref="IDmtpActor"/>关闭消息。
|
||||
/// </summary>
|
||||
/// <param name="msg">关闭消息的内容</param>
|
||||
/// <returns>异步操作的任务</returns>
|
||||
public async Task CloseAsync(string msg)
|
||||
public override async Task<Result> CloseAsync(string msg, CancellationToken token = default)
|
||||
{
|
||||
if (this.m_dmtpActor != null)
|
||||
try
|
||||
{
|
||||
// 向IDmtpActor对象发送关闭消息
|
||||
await this.m_dmtpActor.SendCloseAsync(msg).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
// 关闭IDmtpActor对象
|
||||
await this.m_dmtpActor.CloseAsync(msg).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
await this.OnDmtpClosing(new ClosingEventArgs(msg)).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
|
||||
if (this.m_client != null)
|
||||
var dmtpActor = this.m_dmtpActor;
|
||||
if (dmtpActor != null)
|
||||
{
|
||||
// 向IDmtpActor对象发送关闭消息
|
||||
await dmtpActor.SendCloseAsync(msg).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
|
||||
// 关闭IDmtpActor对象
|
||||
await dmtpActor.CloseAsync(msg, token).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
await base.CloseAsync(msg, token).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await this.m_client.CloseAsync(WebSocketCloseStatus.NormalClosure, msg, CancellationToken.None).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
return Result.FromException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +157,6 @@ public class WebSocketDmtpClient : SetupConfigObject, IWebSocketDmtpClient
|
||||
/// <inheritdoc/>
|
||||
protected override void LoadConfig(TouchSocketConfig config)
|
||||
{
|
||||
this.RemoteIPHost = config.GetValue(TouchSocketConfigExtension.RemoteIPHostProperty);
|
||||
var dmtpRouteService = this.Resolver.Resolve<IDmtpRouteService>();
|
||||
if (dmtpRouteService != null)
|
||||
{
|
||||
@@ -192,109 +165,44 @@ public class WebSocketDmtpClient : SetupConfigObject, IWebSocketDmtpClient
|
||||
}
|
||||
}
|
||||
|
||||
private void Abort(bool manual, string msg)
|
||||
/// <inheritdoc/>
|
||||
protected override async Task OnReceived(WebSocketReceiveResult result, ByteBlock byteBlock)
|
||||
{
|
||||
lock (this.m_semaphoreForConnect)
|
||||
{
|
||||
if (this.m_online)
|
||||
{
|
||||
this.m_online = false;
|
||||
this.m_client.SafeDispose();
|
||||
this.m_dmtpActor.SafeDispose();
|
||||
_ = Task.Factory.StartNew(this.PrivateOnDmtpClosed, new ClosedEventArgs(manual, msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task BeginReceive()
|
||||
{
|
||||
var byteBlock = new ByteBlock(this.m_receiveBufferSize);
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
//处理数据
|
||||
while (byteBlock.CanRead)
|
||||
{
|
||||
try
|
||||
if (this.m_dmtpAdapter.TryParseRequest(ref byteBlock, out var message))
|
||||
{
|
||||
#if NET6_0_OR_GREATER
|
||||
var result = await this.m_client.ReceiveAsync(byteBlock.TotalMemory, default).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
#else
|
||||
var segment = byteBlock.TotalMemory.GetArray();
|
||||
|
||||
var result = await this.m_client.ReceiveAsync(segment, default).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
#endif
|
||||
|
||||
if (result.Count == 0)
|
||||
using (message)
|
||||
{
|
||||
break;
|
||||
}
|
||||
byteBlock.SetLength(result.Count);
|
||||
this.m_receiveCounter.Increment(result.Count);
|
||||
|
||||
//处理数据
|
||||
while (byteBlock.CanRead)
|
||||
{
|
||||
if (this.m_dmtpAdapter.TryParseRequest(ref byteBlock, out var message))
|
||||
if (!await this.m_dmtpActor.InputReceivedData(message).ConfigureAwait(EasyTask.ContinueOnCapturedContext))
|
||||
{
|
||||
using (message)
|
||||
{
|
||||
if (!await this.m_dmtpActor.InputReceivedData(message).ConfigureAwait(EasyTask.ContinueOnCapturedContext))
|
||||
{
|
||||
await this.PluginManager.RaiseAsync(typeof(IDmtpReceivedPlugin), this.Resolver, this, new DmtpMessageEventArgs(message)).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.Logger?.Exception(ex);
|
||||
break;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (byteBlock.Holding || byteBlock.DisposedValue)
|
||||
{
|
||||
byteBlock.Dispose();//释放上个内存
|
||||
byteBlock = new ByteBlock(this.m_receiveBufferSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
byteBlock.Reset();
|
||||
if (this.m_receiveBufferSize > byteBlock.Capacity)
|
||||
{
|
||||
byteBlock.SetCapacity(this.m_receiveBufferSize);
|
||||
await this.PluginManager.RaiseAsync(typeof(IDmtpReceivedPlugin), this.Resolver, this, new DmtpMessageEventArgs(message)).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.Abort(false, "远程终端主动关闭");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.Abort(false, ex.Message);
|
||||
this.Logger?.Debug(this, ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
byteBlock.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnReceivePeriod(long value)
|
||||
{
|
||||
this.m_receiveBufferSize = TouchSocketCoreUtility.HitBufferLength(value);
|
||||
}
|
||||
|
||||
private void OnSendPeriod(long value)
|
||||
{
|
||||
this.m_sendBufferSize = TouchSocketCoreUtility.HitBufferLength(value);
|
||||
}
|
||||
|
||||
#region 内部委托绑定
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override Task OnWebSocketClosed(ClosedEventArgs e)
|
||||
{
|
||||
return this.PrivateOnDmtpClosed(e);
|
||||
}
|
||||
|
||||
private async Task OnDmtpActorClose(DmtpActor actor, string msg)
|
||||
{
|
||||
await this.OnDmtpClosing(new ClosingEventArgs(msg)).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
|
||||
this.Abort(false, msg);
|
||||
}
|
||||
|
||||
@@ -320,28 +228,27 @@ public class WebSocketDmtpClient : SetupConfigObject, IWebSocketDmtpClient
|
||||
|
||||
private async Task OnDmtpActorSendAsync(DmtpActor actor, ReadOnlyMemory<byte> memory)
|
||||
{
|
||||
await this.m_client.SendAsync(memory.GetArray(), WebSocketMessageType.Binary, true, CancellationToken.None).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
|
||||
this.m_sentCounter.Increment(memory.Length);
|
||||
await base.ProtectedSendAsync(memory, WebSocketMessageType.Binary, true, CancellationToken.None).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
#endregion 内部委托绑定
|
||||
|
||||
#region 事件触发
|
||||
|
||||
private async Task PrivateOnDmtpClosed(object obj)
|
||||
/// <summary>
|
||||
/// 当创建通道时触发的事件处理程序
|
||||
/// </summary>
|
||||
/// <param name="e">包含通道创建信息的事件参数</param>
|
||||
protected virtual async Task OnCreateChannel(CreateChannelEventArgs e)
|
||||
{
|
||||
try
|
||||
// 如果事件已经被处理,则直接返回
|
||||
if (e.Handled)
|
||||
{
|
||||
var e = (ClosedEventArgs)obj;
|
||||
await this.m_receiveTask.ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
|
||||
await this.OnDmtpClosed(e).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
return;
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
// 异步调用插件管理器,通知所有实现IDmtpCreatedChannelPlugin接口的插件处理通道创建事件
|
||||
await this.PluginManager.RaiseAsync(typeof(IDmtpCreatedChannelPlugin), this.Resolver, this, e).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -358,12 +265,13 @@ public class WebSocketDmtpClient : SetupConfigObject, IWebSocketDmtpClient
|
||||
// 异步触发插件管理器中的 IDmtpClosedPlugin 接口的事件,并传递相关参数
|
||||
await this.PluginManager.RaiseAsync(typeof(IDmtpClosedPlugin), this.Resolver, this, e).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当Dmtp即将被关闭时触发。
|
||||
/// <para>
|
||||
/// 该触发条件有2种:
|
||||
/// <list type="number">
|
||||
/// <item>终端主动调用<see cref="CloseAsync(string)"/>。</item>
|
||||
/// <item>终端主动调用<see cref="IClosableClient.CloseAsync(string, System.Threading.CancellationToken)"/>。</item>
|
||||
/// <item>终端收到<see cref="DmtpActor.P0_Close"/>的请求。</item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
@@ -381,22 +289,6 @@ public class WebSocketDmtpClient : SetupConfigObject, IWebSocketDmtpClient
|
||||
await this.PluginManager.RaiseAsync(typeof(IDmtpClosingPlugin), this.Resolver, this, e).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当创建通道时触发的事件处理程序
|
||||
/// </summary>
|
||||
/// <param name="e">包含通道创建信息的事件参数</param>
|
||||
protected virtual async Task OnCreateChannel(CreateChannelEventArgs e)
|
||||
{
|
||||
// 如果事件已经被处理,则直接返回
|
||||
if (e.Handled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 异步调用插件管理器,通知所有实现IDmtpCreatedChannelPlugin接口的插件处理通道创建事件
|
||||
await this.PluginManager.RaiseAsync(typeof(IDmtpCreatedChannelPlugin), this.Resolver, this, e).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在完成握手连接时
|
||||
/// </summary>
|
||||
@@ -426,6 +318,7 @@ public class WebSocketDmtpClient : SetupConfigObject, IWebSocketDmtpClient
|
||||
// 触发握手过程的插件事件
|
||||
await this.PluginManager.RaiseAsync(typeof(IDmtpHandshakingPlugin), this.Resolver, this, e).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当需要转发路由包时
|
||||
/// </summary>
|
||||
@@ -440,5 +333,11 @@ public class WebSocketDmtpClient : SetupConfigObject, IWebSocketDmtpClient
|
||||
// 异步调用插件管理器,通知所有实现了IDmtpRoutingPlugin接口的插件处理路由包
|
||||
await this.PluginManager.RaiseAsync(typeof(IDmtpRoutingPlugin), this.Resolver, this, e).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
private async Task PrivateOnDmtpClosed(ClosedEventArgs e)
|
||||
{
|
||||
await this.OnDmtpClosed(e).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
#endregion 事件触发
|
||||
}
|
||||
@@ -11,6 +11,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -22,7 +23,7 @@ namespace TouchSocket.Dmtp.FileTransfer;
|
||||
/// <summary>
|
||||
/// 能够基于Dmtp协议提供文件传输功能
|
||||
/// </summary>
|
||||
public class DmtpFileTransferActor : IDmtpFileTransferActor
|
||||
internal sealed class DmtpFileTransferActor :DisposableObject, IDmtpFileTransferActor
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建一个<see cref="DmtpFileTransferActor"/>
|
||||
@@ -108,7 +109,7 @@ public class DmtpFileTransferActor : IDmtpFileTransferActor
|
||||
{
|
||||
//fileTransferRouterPackage.UnpackageBody(byteBlock);
|
||||
|
||||
_ = Task.Factory.StartNew(this.RequestPullFileResourceInfo, fileTransferRouterPackage);
|
||||
_ = EasyTask.Run(this.RequestPullFileResourceInfo, fileTransferRouterPackage);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -171,7 +172,7 @@ public class DmtpFileTransferActor : IDmtpFileTransferActor
|
||||
{
|
||||
waitFileSection.UnpackageBody(ref byteBlock);
|
||||
await this.RequestPullFileSection(waitFileSection).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
//Task.Factory.StartNew(this.RequestPullFileSection, waitFileSection);
|
||||
//EasyTask.Run(this.RequestPullFileSection, waitFileSection);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -303,7 +304,7 @@ public class DmtpFileTransferActor : IDmtpFileTransferActor
|
||||
{
|
||||
waitFileSection.UnpackageBody(ref byteBlock);
|
||||
//this.RequestPushFileSection(waitFileSection);
|
||||
//Task.Factory.StartNew(this.RequestPushFileSection, waitFileSection);
|
||||
//EasyTask.Run(this.RequestPushFileSection, waitFileSection);
|
||||
await this.RequestPushFileSection(waitFileSection).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
}
|
||||
@@ -364,7 +365,7 @@ public class DmtpFileTransferActor : IDmtpFileTransferActor
|
||||
else
|
||||
{
|
||||
waitFinishedPackage.UnpackageBody(ref byteBlock);
|
||||
_ = Task.Factory.StartNew(this.RequestFinishedFileResourceInfo, waitFinishedPackage);
|
||||
_ = EasyTask.Run(this.RequestFinishedFileResourceInfo, waitFinishedPackage);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -433,7 +434,7 @@ public class DmtpFileTransferActor : IDmtpFileTransferActor
|
||||
else
|
||||
{
|
||||
waitSmallFilePackage.UnpackageBody(ref byteBlock);
|
||||
_ = Task.Factory.StartNew(this.RequestPullSmallFile, waitSmallFilePackage);
|
||||
_ = EasyTask.Run(this.RequestPullSmallFile, waitSmallFilePackage);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -783,7 +784,7 @@ public class DmtpFileTransferActor : IDmtpFileTransferActor
|
||||
|
||||
var waitData = this.DmtpActor.WaitHandlePool.GetWaitDataAsync(waitFinishedPackage);
|
||||
|
||||
var byteBlock = new ByteBlock();
|
||||
var byteBlock = new ByteBlock(1024*64);
|
||||
try
|
||||
{
|
||||
var block = byteBlock;
|
||||
@@ -810,7 +811,7 @@ public class DmtpFileTransferActor : IDmtpFileTransferActor
|
||||
}
|
||||
case TouchSocketDmtpStatus.HasUnFinished:
|
||||
{
|
||||
return new FinishedResult(ResultCode.Fail, TouchSocketDmtpStatus.HasUnFinished.GetDescription(), waitFile.ResourceHandle);
|
||||
return new FinishedResult(ResultCode.Failure, TouchSocketDmtpStatus.HasUnFinished.GetDescription(), waitFile.ResourceHandle);
|
||||
}
|
||||
default:
|
||||
{
|
||||
@@ -853,7 +854,7 @@ public class DmtpFileTransferActor : IDmtpFileTransferActor
|
||||
|
||||
var waitData = this.DmtpActor.WaitHandlePool.GetWaitDataAsync(waitFileResource);
|
||||
|
||||
var byteBlock = new ByteBlock();
|
||||
var byteBlock = new ByteBlock(1024*64);
|
||||
try
|
||||
{
|
||||
var block = byteBlock;
|
||||
@@ -921,7 +922,7 @@ public class DmtpFileTransferActor : IDmtpFileTransferActor
|
||||
|
||||
var waitData = this.DmtpActor.WaitHandlePool.GetWaitDataAsync(waitFileSection);
|
||||
|
||||
var byteBlock = new ByteBlock();
|
||||
var byteBlock = new ByteBlock(1024*64);
|
||||
try
|
||||
{
|
||||
var block = byteBlock;
|
||||
@@ -969,7 +970,7 @@ public class DmtpFileTransferActor : IDmtpFileTransferActor
|
||||
case WaitDataStatus.Disposed:
|
||||
default:
|
||||
{
|
||||
return new FileSectionResult(ResultCode.Fail, default, fileSection);
|
||||
return new FileSectionResult(ResultCode.Failure, default, fileSection);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -999,7 +1000,7 @@ public class DmtpFileTransferActor : IDmtpFileTransferActor
|
||||
|
||||
var waitData = this.DmtpActor.WaitHandlePool.GetWaitDataAsync(waitFileResource);
|
||||
|
||||
var byteBlock = new ByteBlock();
|
||||
var byteBlock = new ByteBlock(1024*64);
|
||||
try
|
||||
{
|
||||
var block = byteBlock;
|
||||
@@ -1169,7 +1170,7 @@ public class DmtpFileTransferActor : IDmtpFileTransferActor
|
||||
else
|
||||
{
|
||||
waitFinishedPackage.Status = TouchSocketDmtpStatus.ResourceHandleNotFind.ToValue();
|
||||
resultThis = new Result(ResultCode.Fail, TouchSocketDmtpStatus.ResourceHandleNotFind.GetDescription(waitFinishedPackage.ResourceHandle));
|
||||
resultThis = new Result(ResultCode.Failure, TouchSocketDmtpStatus.ResourceHandleNotFind.GetDescription(waitFinishedPackage.ResourceHandle));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1198,20 +1199,20 @@ public class DmtpFileTransferActor : IDmtpFileTransferActor
|
||||
{
|
||||
waitFinishedPackage.Status = TouchSocketDmtpStatus.HasUnFinished.ToValue();
|
||||
|
||||
resultThis = new Result(ResultCode.Fail, TouchSocketDmtpStatus.HasUnFinished.GetDescription(sections.Length));
|
||||
resultThis = new Result(ResultCode.Failure, TouchSocketDmtpStatus.HasUnFinished.GetDescription(sections.Length));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
waitFinishedPackage.Status = TouchSocketDmtpStatus.ResourceHandleNotFind.ToValue();
|
||||
resultThis = new Result(ResultCode.Fail, TouchSocketDmtpStatus.ResourceHandleNotFind.GetDescription(waitFinishedPackage.ResourceHandle));
|
||||
resultThis = new Result(ResultCode.Failure, TouchSocketDmtpStatus.ResourceHandleNotFind.GetDescription(waitFinishedPackage.ResourceHandle));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
waitFinishedPackage.Status = TouchSocketDmtpStatus.ResourceHandleNotFind.ToValue();
|
||||
resultThis = new Result(ResultCode.Fail, TouchSocketDmtpStatus.ResourceHandleNotFind.GetDescription(waitFinishedPackage.ResourceHandle));
|
||||
resultThis = new Result(ResultCode.Failure, TouchSocketDmtpStatus.ResourceHandleNotFind.GetDescription(waitFinishedPackage.ResourceHandle));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -1231,7 +1232,7 @@ public class DmtpFileTransferActor : IDmtpFileTransferActor
|
||||
await this.OnFileTransferred.Invoke(this.DmtpActor, args).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
|
||||
|
||||
using (var byteBlock = new ByteBlock())
|
||||
using (var byteBlock = new ByteBlock(1024*64))
|
||||
{
|
||||
waitFinishedPackage.SwitchId();
|
||||
var block = byteBlock;
|
||||
@@ -1241,7 +1242,7 @@ public class DmtpFileTransferActor : IDmtpFileTransferActor
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.DmtpActor.Logger?.Exception(ex);
|
||||
this.DmtpActor.Logger?.Debug(this, ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1296,7 +1297,7 @@ public class DmtpFileTransferActor : IDmtpFileTransferActor
|
||||
waitFileResource.Message = ex.Message;
|
||||
waitFileResource.Status = TouchSocketDmtpStatus.Exception.ToValue();
|
||||
}
|
||||
using (var byteBlock = new ByteBlock())
|
||||
using (var byteBlock = new ByteBlock(1024*64))
|
||||
{
|
||||
waitFileResource.SwitchId();
|
||||
var block = byteBlock;
|
||||
@@ -1420,7 +1421,7 @@ public class DmtpFileTransferActor : IDmtpFileTransferActor
|
||||
waitFileResource.Status = TouchSocketDmtpStatus.Exception.ToValue();
|
||||
waitFileResource.Message = ex.Message;
|
||||
}
|
||||
using (var byteBlock = new ByteBlock())
|
||||
using (var byteBlock = new ByteBlock(1024*64))
|
||||
{
|
||||
waitFileResource.SwitchId();
|
||||
var block = byteBlock;
|
||||
@@ -1479,7 +1480,7 @@ public class DmtpFileTransferActor : IDmtpFileTransferActor
|
||||
|
||||
waitFileSection.Value.SafeDispose();
|
||||
waitFileSection.Value = default;
|
||||
using (var byteBlock = new ByteBlock())
|
||||
using (var byteBlock = new ByteBlock(1024*64))
|
||||
{
|
||||
waitFileSection.SwitchId();
|
||||
var block = byteBlock;
|
||||
@@ -1567,7 +1568,7 @@ public class DmtpFileTransferActor : IDmtpFileTransferActor
|
||||
|
||||
try
|
||||
{
|
||||
using (var byteBlock = new ByteBlock())
|
||||
using (var byteBlock = new ByteBlock(1024*64))
|
||||
{
|
||||
var block = byteBlock;
|
||||
waitSmallFilePackage.Package(ref block);
|
||||
@@ -1645,8 +1646,8 @@ public class DmtpFileTransferActor : IDmtpFileTransferActor
|
||||
|
||||
var waitData = this.DmtpActor.WaitHandlePool.GetWaitDataAsync(waitSmallFilePackage);
|
||||
|
||||
var byteBlock = new ByteBlock();
|
||||
var buffer = BytePool.Default.Rent((int)fileInfo.Length);
|
||||
var byteBlock = new ByteBlock(1024*64);
|
||||
var buffer = ArrayPool<byte>.Shared.Rent((int)fileInfo.Length);
|
||||
try
|
||||
{
|
||||
var r = this.FileController.ReadAllBytes(fileInfo, buffer);
|
||||
@@ -1706,7 +1707,7 @@ public class DmtpFileTransferActor : IDmtpFileTransferActor
|
||||
{
|
||||
this.DmtpActor.WaitHandlePool.Destroy(waitData);
|
||||
byteBlock.Dispose();
|
||||
BytePool.Default.Return(buffer);
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1717,7 +1718,7 @@ public class DmtpFileTransferActor : IDmtpFileTransferActor
|
||||
//4.不存在
|
||||
//5.读取文件长度异常
|
||||
|
||||
var buffer = BytePool.Default.Rent(this.MaxSmallFileLength);
|
||||
var buffer = ArrayPool<byte>.Shared.Rent(this.MaxSmallFileLength);
|
||||
try
|
||||
{
|
||||
var waitSmallFilePackage = (WaitSmallFilePackage)o;
|
||||
@@ -1779,7 +1780,7 @@ public class DmtpFileTransferActor : IDmtpFileTransferActor
|
||||
waitSmallFilePackage.Message = ex.Message;
|
||||
}
|
||||
|
||||
using (var byteBlock = new ByteBlock())
|
||||
using (var byteBlock = new ByteBlock(1024*64))
|
||||
{
|
||||
waitSmallFilePackage.SwitchId();
|
||||
var block = byteBlock;
|
||||
@@ -1796,11 +1797,11 @@ public class DmtpFileTransferActor : IDmtpFileTransferActor
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.DmtpActor.Logger?.Exception(ex);
|
||||
this.DmtpActor.Logger?.Debug(this, ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
BytePool.Default.Return(buffer);
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1845,7 +1846,7 @@ public class DmtpFileTransferActor : IDmtpFileTransferActor
|
||||
waitSmallFilePackage.FileInfo = default;
|
||||
waitSmallFilePackage.Data = default;
|
||||
waitSmallFilePackage.SwitchId();
|
||||
using (var byteBlock = new ByteBlock())
|
||||
using (var byteBlock = new ByteBlock(1024*64))
|
||||
{
|
||||
var block = byteBlock;
|
||||
waitSmallFilePackage.Package(ref block);
|
||||
@@ -1861,7 +1862,7 @@ public class DmtpFileTransferActor : IDmtpFileTransferActor
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.DmtpActor.Logger?.Exception(ex);
|
||||
this.DmtpActor.Logger?.Debug(this, ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ public class FileResourceLocator : DisposableObject
|
||||
this.FileAccess = FileAccess.Read;
|
||||
this.FileStorage = FilePool.GetFileStorageForRead(fileResourceInfo.FileInfo.FullName);
|
||||
this.LocatorPath = fileResourceInfo.FileInfo.FullName;
|
||||
this.LastActiveTime = DateTime.UtcNow;
|
||||
this.LastActiveTime = DateTimeOffset.UtcNow;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -61,7 +61,7 @@ public class FileResourceLocator : DisposableObject
|
||||
this.FileResourceInfo = fileResourceInfo;
|
||||
this.FileAccess = FileAccess.Write;
|
||||
this.FileStorage = FilePool.GetFileStorageForWrite(this.LocatorPath + ExtensionName);
|
||||
this.LastActiveTime = DateTime.UtcNow;
|
||||
this.LastActiveTime = DateTimeOffset.UtcNow;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -82,7 +82,7 @@ public class FileResourceLocator : DisposableObject
|
||||
/// <summary>
|
||||
/// 最后活动时间
|
||||
/// </summary>
|
||||
public DateTime LastActiveTime { get; private set; }
|
||||
public DateTimeOffset LastActiveTime { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 定位器指向的实际文件的全名称。
|
||||
@@ -96,7 +96,7 @@ public class FileResourceLocator : DisposableObject
|
||||
public FileSection GetDefaultOrFailFileSection()
|
||||
{
|
||||
// 更新最后活跃时间
|
||||
this.LastActiveTime = DateTime.UtcNow;
|
||||
this.LastActiveTime = DateTimeOffset.UtcNow;
|
||||
// 如果文件访问模式为读取,则返回默认值
|
||||
if (this.FileAccess == FileAccess.Read)
|
||||
{
|
||||
@@ -116,7 +116,7 @@ public class FileResourceLocator : DisposableObject
|
||||
public FileSection[] GetUnfinishedFileSection()
|
||||
{
|
||||
// 更新最后活跃时间
|
||||
this.LastActiveTime = DateTime.UtcNow;
|
||||
this.LastActiveTime = DateTimeOffset.UtcNow;
|
||||
// 根据文件访问模式决定返回值
|
||||
// 如果是读取模式,则返回空数组
|
||||
// 否则,返回所有未完成状态的文件片段集合
|
||||
@@ -134,7 +134,7 @@ public class FileResourceLocator : DisposableObject
|
||||
public int ReadBytes(long pos, Span<byte> span)
|
||||
{
|
||||
// 更新最后一次活动时间,用于跟踪访问时间
|
||||
this.LastActiveTime = DateTime.UtcNow;
|
||||
this.LastActiveTime = DateTimeOffset.UtcNow;
|
||||
// 调用底层文件存储系统的读取方法,读取字节并返回读取的字节数
|
||||
return this.FileStorage.Read(pos, span);
|
||||
}
|
||||
@@ -149,7 +149,7 @@ public class FileResourceLocator : DisposableObject
|
||||
try
|
||||
{
|
||||
// 更新上次活跃时间,用于跟踪文件访问器的使用情况。
|
||||
this.LastActiveTime = DateTime.UtcNow;
|
||||
this.LastActiveTime = DateTimeOffset.UtcNow;
|
||||
// 检查文件访问权限,确保是读权限。
|
||||
if (this.FileAccess != FileAccess.Read)
|
||||
{
|
||||
@@ -199,7 +199,7 @@ public class FileResourceLocator : DisposableObject
|
||||
public void ReloadFileResourceInfo(FileResourceInfo fileResourceInfo)
|
||||
{
|
||||
// 更新资源的最后活跃时间
|
||||
this.LastActiveTime = DateTime.UtcNow;
|
||||
this.LastActiveTime = DateTimeOffset.UtcNow;
|
||||
// 检查新旧资源路径是否一致,如果不一致则抛出异常
|
||||
if (fileResourceInfo.FileInfo.FullName != this.FileResourceInfo.FileInfo.FullName)
|
||||
{
|
||||
@@ -217,7 +217,7 @@ public class FileResourceLocator : DisposableObject
|
||||
public Result TryFinished()
|
||||
{
|
||||
// 更新最后一次活跃时间
|
||||
this.LastActiveTime = DateTime.UtcNow;
|
||||
this.LastActiveTime = DateTimeOffset.UtcNow;
|
||||
|
||||
// 如果是读取模式,则直接返回成功
|
||||
if (this.FileAccess == FileAccess.Read)
|
||||
@@ -230,13 +230,13 @@ public class FileResourceLocator : DisposableObject
|
||||
// 检查是否有未完成的文件块
|
||||
if (this.GetUnfinishedFileSection().Length > 0)
|
||||
{
|
||||
return new Result(ResultCode.Fail, "还有文件块没有完成。");
|
||||
return new Result(ResultCode.Failure, "还有文件块没有完成。");
|
||||
}
|
||||
|
||||
// 确保文件长度一致
|
||||
if (this.FileStorage.Length != this.FileResourceInfo.FileInfo.Length)
|
||||
{
|
||||
return new Result(ResultCode.Fail, "文件长度不一致。");
|
||||
return new Result(ResultCode.Failure, "文件长度不一致。");
|
||||
}
|
||||
|
||||
// 尝试释放文件,最多尝试10次
|
||||
@@ -277,7 +277,7 @@ public class FileResourceLocator : DisposableObject
|
||||
public Result WriteFileSection(FileSection fileSection, ArraySegment<byte> value)
|
||||
{
|
||||
// 更新最后一次活动时间,用于跟踪文件操作的时间点
|
||||
this.LastActiveTime = DateTime.UtcNow;
|
||||
this.LastActiveTime = DateTimeOffset.UtcNow;
|
||||
|
||||
// 检查当前文件访问模式是否为写,确保操作的正确性
|
||||
if (this.FileAccess != FileAccess.Write)
|
||||
@@ -330,7 +330,7 @@ public class FileResourceLocator : DisposableObject
|
||||
public Result WriteFileSection(FileSectionResult fileSectionResult)
|
||||
{
|
||||
// 更新最后一次活跃时间,用于跟踪最近的访问时间
|
||||
this.LastActiveTime = DateTime.UtcNow;
|
||||
this.LastActiveTime = DateTimeOffset.UtcNow;
|
||||
|
||||
// 检查文件访问权限是否为写,如果是读-only,则返回错误结果
|
||||
if (this.FileAccess != FileAccess.Write)
|
||||
|
||||
@@ -64,7 +64,7 @@ public sealed class DmtpFileTransferFeature : PluginBase, IDmtpHandshakingPlugin
|
||||
MaxSmallFileLength = this.MaxSmallFileLength
|
||||
};
|
||||
dmtpFileTransferActor.SetProtocolFlags(this.StartProtocol);
|
||||
client.DmtpActor.SetDmtpFileTransferActor(dmtpFileTransferActor);
|
||||
client.DmtpActor.AddActor<DmtpFileTransferActor>(dmtpFileTransferActor);
|
||||
await e.InvokeNext().ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,11 +40,11 @@ public static class DmtpFileTransferActorExtension
|
||||
|
||||
#region DependencyProperty
|
||||
|
||||
/// <summary>
|
||||
/// DmtpFileTransferActor
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty<IDmtpFileTransferActor> DmtpFileTransferActorProperty =
|
||||
new("DmtpFileTransferActor", default);
|
||||
///// <summary>
|
||||
///// DmtpFileTransferActor
|
||||
///// </summary>
|
||||
//public static readonly DependencyProperty<IDmtpFileTransferActor> DmtpFileTransferActorProperty =
|
||||
// new("DmtpFileTransferActor", default);
|
||||
|
||||
#endregion DependencyProperty
|
||||
|
||||
@@ -55,19 +55,7 @@ public static class DmtpFileTransferActorExtension
|
||||
/// <returns>返回一个<see cref="IDmtpFileTransferActor"/>实例。</returns>
|
||||
public static IDmtpFileTransferActor GetDmtpFileTransferActor(this IDmtpActor dmtpActor)
|
||||
{
|
||||
// 通过DmtpFileTransferActorProperty属性从dmtpActor中获取IDmtpFileTransferActor实例
|
||||
return dmtpActor.GetValue(DmtpFileTransferActorProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向<see cref="DmtpActor"/>中设置<see cref="DmtpFileTransferActor"/>
|
||||
/// </summary>
|
||||
/// <param name="dmtpActor">要设置<see cref="DmtpFileTransferActor"/>的<see cref="IDmtpActor"/>对象</param>
|
||||
/// <param name="dmtpRpcActor">要设置的<see cref="DmtpFileTransferActor"/>实例</param>
|
||||
internal static void SetDmtpFileTransferActor(this IDmtpActor dmtpActor, DmtpFileTransferActor dmtpRpcActor)
|
||||
{
|
||||
// 使用DependencyProperty进行设置,确保可以在WPF等框架中作为属性绑定使用
|
||||
dmtpActor.SetValue(DmtpFileTransferActorProperty, dmtpRpcActor);
|
||||
return dmtpActor.GetActor<DmtpFileTransferActor>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -78,19 +66,9 @@ public static class DmtpFileTransferActorExtension
|
||||
/// <exception cref="ArgumentNullException">如果内部演员对象为空,则抛出此异常</exception>
|
||||
public static IDmtpFileTransferActor GetDmtpFileTransferActor(this IDmtpActorObject client)
|
||||
{
|
||||
// 从client中获取Dmtp文件传输演员
|
||||
var actor = client.DmtpActor.GetDmtpFileTransferActor();
|
||||
// 检查获取的演员是否为空
|
||||
if (actor is null)
|
||||
{
|
||||
// 如果为空,则抛出ArgumentNullException异常
|
||||
throw new ArgumentNullException(nameof(actor), TouchSocketDmtpResource.DmtpFileTransferActorNull);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 返回获取的演员对象
|
||||
return actor;
|
||||
}
|
||||
ThrowHelper.ThrowArgumentNullExceptionIf(actor,nameof(actor), TouchSocketDmtpResource.DmtpFileTransferActorNull);
|
||||
return actor;
|
||||
}
|
||||
|
||||
#region Id文件传输
|
||||
|
||||
@@ -42,7 +42,7 @@ public class FileResourceController : DisposableObject, IFileResourceController
|
||||
var ints = new List<int>();
|
||||
foreach (var item in this.FileResourceStore)
|
||||
{
|
||||
if (DateTime.UtcNow - item.Value.LastActiveTime > this.Timeout)
|
||||
if (DateTimeOffset.UtcNow - item.Value.LastActiveTime > this.Timeout)
|
||||
{
|
||||
ints.Add(item.Key);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace TouchSocket.Dmtp.Redis;
|
||||
/// DmtpRedisActor 类,实现了 IDmtpRedisActor 接口。
|
||||
/// 该类通过 Redis 操作,为分布式消息传输协议(Dmtp)提供演员(Actor)模型的实现。
|
||||
/// </summary>
|
||||
public class DmtpRedisActor : IDmtpRedisActor
|
||||
internal sealed class DmtpRedisActor :DisposableObject, IDmtpRedisActor
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化DmtpRedisActor类的新实例。
|
||||
@@ -75,7 +75,7 @@ public class DmtpRedisActor : IDmtpRedisActor
|
||||
var waitData = this.DmtpActor.WaitHandlePool.GetWaitDataAsync(package);
|
||||
try
|
||||
{
|
||||
using (var byteBlock = new ByteBlock())
|
||||
using (var byteBlock = new ByteBlock(1024*64))
|
||||
{
|
||||
var block = byteBlock;
|
||||
package.Package(ref block);
|
||||
@@ -125,7 +125,7 @@ public class DmtpRedisActor : IDmtpRedisActor
|
||||
var waitData = this.DmtpActor.WaitHandlePool.GetWaitDataAsync(package);
|
||||
try
|
||||
{
|
||||
using (var byteBlock = new ByteBlock())
|
||||
using (var byteBlock = new ByteBlock(1024*64))
|
||||
{
|
||||
var block = byteBlock;
|
||||
package.Package(ref block);
|
||||
@@ -296,7 +296,7 @@ public class DmtpRedisActor : IDmtpRedisActor
|
||||
waitResult.Message = ex.Message;
|
||||
}
|
||||
|
||||
using (var byteBlock = new ByteBlock())
|
||||
using (var byteBlock = new ByteBlock(1024*64))
|
||||
{
|
||||
var block = byteBlock;
|
||||
waitResult.Package(ref block);
|
||||
|
||||
@@ -21,11 +21,11 @@ namespace TouchSocket.Dmtp.Redis;
|
||||
/// </summary>
|
||||
public static class DmtpRedisActorExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取或设置RedisActor的注入键。
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty<IDmtpRedisActor> DmtpRedisActorProperty =
|
||||
new("DmtpRedisActor", null);
|
||||
///// <summary>
|
||||
///// 获取或设置RedisActor的注入键。
|
||||
///// </summary>
|
||||
//public static readonly DependencyProperty<IDmtpRedisActor> DmtpRedisActorProperty =
|
||||
// new("DmtpRedisActor", null);
|
||||
|
||||
/// <summary>
|
||||
/// 获取<see cref="IDmtpRedisActor"/>
|
||||
@@ -35,10 +35,9 @@ public static class DmtpRedisActorExtensions
|
||||
/// <exception cref="Exception">当<see cref="IDmtpRedisActor"/>为null时抛出<see cref="ArgumentException"/></exception>
|
||||
public static IDmtpRedisActor GetDmtpRedisActor(this IDmtpActorObject client)
|
||||
{
|
||||
// 从client的DmtpActor属性中获取存储的IDmtpRedisActor实例
|
||||
var redisClient = client.DmtpActor.GetValue(DmtpRedisActorProperty);
|
||||
// 如果redisClient为null,则抛出ArgumentException,提示RedisActor未设置
|
||||
return redisClient ?? throw new ArgumentException(TouchSocketDmtpResource.RedisActorNull);
|
||||
var actor = client.DmtpActor.GetDmtpRedisActor();
|
||||
ThrowHelper.ThrowArgumentNullExceptionIf(actor, nameof(actor), TouchSocketDmtpResource.RedisActorNull);
|
||||
return actor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -48,13 +47,7 @@ public static class DmtpRedisActorExtensions
|
||||
/// <returns>返回从<see cref="DmtpActor"/>中获取的<see cref="IDmtpRedisActor"/>实例</returns>
|
||||
public static IDmtpRedisActor GetDmtpRedisActor(this IDmtpActor dmtpActor)
|
||||
{
|
||||
// 调用GetValue方法从dmtpActor中获取DmtpRedisActorProperty属性值
|
||||
return dmtpActor.GetValue(DmtpRedisActorProperty);
|
||||
}
|
||||
|
||||
internal static void SetDmtpRedisActor(this IDmtpActor dmtpActor, DmtpRedisActor redisClient)
|
||||
{
|
||||
dmtpActor.SetValue(DmtpRedisActorProperty, redisClient);
|
||||
return dmtpActor.GetActor<DmtpRedisActor>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -50,7 +50,7 @@ public class RedisFeature : PluginBase, IDmtpHandshakingPlugin, IDmtpReceivedPlu
|
||||
public ushort StartProtocol { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task OnDmtpHandshaking(IDmtpActorObject client, DmtpVerifyEventArgs e)
|
||||
public async Task OnDmtpHandshaking(IDmtpActorObject client, DmtpVerifyEventArgs e)
|
||||
{
|
||||
var dmtpRedisActor = new DmtpRedisActor(client.DmtpActor)
|
||||
{
|
||||
@@ -59,9 +59,9 @@ public class RedisFeature : PluginBase, IDmtpHandshakingPlugin, IDmtpReceivedPlu
|
||||
};
|
||||
|
||||
dmtpRedisActor.SetProtocolFlags(this.StartProtocol);
|
||||
client.DmtpActor.SetDmtpRedisActor(dmtpRedisActor);
|
||||
client.DmtpActor.AddActor<DmtpRedisActor>(dmtpRedisActor);
|
||||
|
||||
return e.InvokeNext();
|
||||
await e.InvokeNext().ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace TouchSocket.Dmtp.Rpc;
|
||||
/// DmtpRpcActor 类,继承自 ConcurrentDictionary,并实现 IDmtpRpcActor 接口。
|
||||
/// 该类用于管理远程过程调用(RPC)的上下文,通过关联任务和超时逻辑来实现。
|
||||
/// </summary>
|
||||
public class DmtpRpcActor : IDmtpRpcActor
|
||||
public class DmtpRpcActor :DisposableObject, IDmtpRpcActor
|
||||
{
|
||||
private readonly ConcurrentDictionary<long, DmtpRpcCallContext> m_callContextDic = new ConcurrentDictionary<long, DmtpRpcCallContext>();
|
||||
|
||||
@@ -127,7 +127,7 @@ public class DmtpRpcActor : IDmtpRpcActor
|
||||
rpcPackage.LoadInfo(callContext, this.m_serializationSelector);
|
||||
rpcPackage.UnpackageBody(ref byteBlock);
|
||||
//await this.InvokeThisAsync(callContext).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
//await Task.Factory.StartNew(this.InvokeThisAsync, callContext).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
//await EasyTask.Run(this.InvokeThisAsync, callContext).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
await this.Dispatcher.Dispatcher(this.DmtpActor, callContext, this.InvokeThisAsync).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
}
|
||||
@@ -238,7 +238,7 @@ public class DmtpRpcActor : IDmtpRpcActor
|
||||
|
||||
private async Task CanceledInvokeAsync(CanceledPackage canceled)
|
||||
{
|
||||
using (var byteBlock = new ByteBlock())
|
||||
using (var byteBlock = new ByteBlock(1024*64))
|
||||
{
|
||||
var block = byteBlock;
|
||||
canceled.Package(ref block);
|
||||
@@ -378,6 +378,16 @@ public class DmtpRpcActor : IDmtpRpcActor
|
||||
return default;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
Dispatcher.SafeDispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Rpc
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -398,7 +408,7 @@ public class DmtpRpcActor : IDmtpRpcActor
|
||||
waitData.SetCancellationToken(invokeOption.Token);
|
||||
}
|
||||
|
||||
var byteBlock = new ByteBlock();
|
||||
var byteBlock = new ByteBlock(1024*64);
|
||||
try
|
||||
{
|
||||
rpcPackage.Package(ref byteBlock);
|
||||
@@ -478,7 +488,7 @@ public class DmtpRpcActor : IDmtpRpcActor
|
||||
waitData.SetCancellationToken(invokeOption.Token);
|
||||
}
|
||||
|
||||
var byteBlock = new ByteBlock();
|
||||
var byteBlock = new ByteBlock(1024*64);
|
||||
try
|
||||
{
|
||||
rpcPackage.Package(ref byteBlock);
|
||||
|
||||
@@ -28,11 +28,11 @@ public static class DmtpRpcActorExtension
|
||||
{
|
||||
#region DependencyProperty
|
||||
|
||||
/// <summary>
|
||||
/// DmtpRpcActor
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty<IDmtpRpcActor> DmtpRpcActorProperty =
|
||||
new("DmtpRpcActor", default);
|
||||
///// <summary>
|
||||
///// DmtpRpcActor
|
||||
///// </summary>
|
||||
//public static readonly DependencyProperty<IDmtpRpcActor> DmtpRpcActorProperty =
|
||||
// new("DmtpRpcActor", default);
|
||||
|
||||
#endregion DependencyProperty
|
||||
|
||||
@@ -55,8 +55,7 @@ public static class DmtpRpcActorExtension
|
||||
/// <returns>返回获取到的<see cref="IDmtpRpcActor"/></returns>
|
||||
public static IDmtpRpcActor GetDmtpRpcActor(this IDmtpActor dmtpActor)
|
||||
{
|
||||
// 调用GetValue方法从dmtpActor中获取DmtpRpcActorProperty属性值
|
||||
return dmtpActor.GetValue(DmtpRpcActorProperty);
|
||||
return dmtpActor.GetActor<DmtpRpcActor>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -67,15 +66,9 @@ public static class DmtpRpcActorExtension
|
||||
/// <exception cref="ArgumentNullException">如果<see cref="IDmtpRpcActor"/>对象为null,则抛出此异常。</exception>
|
||||
public static IDmtpRpcActor GetDmtpRpcActor(this IDmtpActorObject client)
|
||||
{
|
||||
// 从client的DmtpActor属性中获取DmtpRpcActor对象。
|
||||
var dmtpRpcActor = client.DmtpActor.GetDmtpRpcActor();
|
||||
// 如果获取的DmtpRpcActor对象为null,则抛出ArgumentNullException异常。
|
||||
if (dmtpRpcActor is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(dmtpRpcActor), TouchSocketDmtpResource.DmtpRpcActorArgumentNull);
|
||||
}
|
||||
// 返回获取到的DmtpRpcActor对象。
|
||||
return dmtpRpcActor;
|
||||
var actor = client.DmtpActor.GetDmtpRpcActor();
|
||||
ThrowHelper.ThrowArgumentNullExceptionIf(actor, nameof(actor), TouchSocketDmtpResource.DmtpRpcActorArgumentNull);
|
||||
return actor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -86,26 +79,11 @@ public static class DmtpRpcActorExtension
|
||||
/// <exception cref="ArgumentNullException">当无法从<paramref name="client"/>中获取到DmtpRpcActor时抛出。</exception>
|
||||
public static TDmtpRpcActor GetDmtpRpcActor<TDmtpRpcActor>(this IDmtpActorObject client) where TDmtpRpcActor : IDmtpRpcActor
|
||||
{
|
||||
// 从client中尝试获取DmtpRpcActor实例
|
||||
var dmtpRpcActor = client.DmtpActor.GetDmtpRpcActor();
|
||||
// 如果获取失败,则抛出ArgumentNullException异常,提示DmtpRpcActor参数为空
|
||||
if (dmtpRpcActor is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(dmtpRpcActor), TouchSocketDmtpResource.DmtpRpcActorArgumentNull);
|
||||
}
|
||||
// 将获取到的DmtpRpcActor实例转换为TDmtpRpcActor类型并返回
|
||||
return (TDmtpRpcActor)dmtpRpcActor;
|
||||
}
|
||||
/// <summary>
|
||||
/// 向<see cref="DmtpActor"/>中设置<see cref="IDmtpRpcActor"/>
|
||||
/// </summary>
|
||||
/// <param name="dmtpActor">要设置的<see cref="IDmtpRpcActor"/>所在的<see cref="DmtpActor"/></param>
|
||||
/// <param name="dmtpRpcActor">要设置的<see cref="IDmtpRpcActor"/>实例</param>
|
||||
internal static void SetDmtpRpcActor(this IDmtpActor dmtpActor, IDmtpRpcActor dmtpRpcActor)
|
||||
{
|
||||
// 使用反射机制将dmtpRpcActor设置到dmtpActor中
|
||||
dmtpActor.SetValue(DmtpRpcActorProperty, dmtpRpcActor);
|
||||
var actor = client.DmtpActor.GetDmtpRpcActor();
|
||||
ThrowHelper.ThrowArgumentNullExceptionIf(actor, nameof(actor), TouchSocketDmtpResource.DmtpRpcActorArgumentNull);
|
||||
return (TDmtpRpcActor)actor;
|
||||
}
|
||||
|
||||
|
||||
#region 插件扩展
|
||||
|
||||
|
||||
@@ -49,11 +49,6 @@ public class DmtpRpcFeature : PluginBase, IDmtpFeature, IDmtpHandshakingPlugin,
|
||||
/// </summary>
|
||||
public ActionMap ActionMap { get; } = new ActionMap(false);
|
||||
|
||||
/// <summary>
|
||||
/// 创建DmtpRpc实例
|
||||
/// </summary>
|
||||
public Func<IDmtpActor, IRpcServerProvider, IRpcDispatcher<IDmtpActor, IDmtpRpcCallContext>, DmtpRpcActor> CreateDmtpRpcActor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置一个函数,该函数创建一个RPC调度器,用于处理IDmtpActor的RPC调用。
|
||||
/// </summary>
|
||||
@@ -62,6 +57,11 @@ public class DmtpRpcFeature : PluginBase, IDmtpFeature, IDmtpHandshakingPlugin,
|
||||
/// </value>
|
||||
public Func<IDmtpActor, IRpcDispatcher<IDmtpActor, IDmtpRpcCallContext>> CreateDispatcher { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建DmtpRpc实例
|
||||
/// </summary>
|
||||
public Func<IDmtpActor, IRpcServerProvider, IRpcDispatcher<IDmtpActor, IDmtpRpcCallContext>, DmtpRpcActor> CreateDmtpRpcActor { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ushort ReserveProtocolSize => 5;
|
||||
|
||||
@@ -73,6 +73,19 @@ public class DmtpRpcFeature : PluginBase, IDmtpFeature, IDmtpHandshakingPlugin,
|
||||
/// <inheritdoc/>
|
||||
public ushort StartProtocol { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 配置默认的序列化选择器。
|
||||
/// </summary>
|
||||
/// <param name="selector">用于配置默认序列化选择器的操作。</param>
|
||||
/// <returns>返回当前的 <see cref="DmtpRpcFeature"/> 实例,以支持链式调用。</returns>
|
||||
public DmtpRpcFeature ConfigureDefaultSerializationSelector(Action<DefaultSerializationSelector> selector)
|
||||
{
|
||||
var serializationSelector = new DefaultSerializationSelector();
|
||||
selector.Invoke(serializationSelector);
|
||||
this.SerializationSelector = serializationSelector;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置创建DmtpRpc实例
|
||||
/// </summary>
|
||||
@@ -84,6 +97,9 @@ public class DmtpRpcFeature : PluginBase, IDmtpFeature, IDmtpHandshakingPlugin,
|
||||
return this;
|
||||
}
|
||||
|
||||
#region Dispatcher
|
||||
|
||||
private readonly GlobalQueueRpcDispatcher m_globalQueueRpcDispatcher = new();
|
||||
|
||||
/// <summary>
|
||||
/// 使用并发调度器处理请求
|
||||
@@ -97,6 +113,17 @@ public class DmtpRpcFeature : PluginBase, IDmtpFeature, IDmtpHandshakingPlugin,
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用全局队列RPC调度器配置RPC特性。
|
||||
/// </summary>
|
||||
/// <returns>返回配置了全局队列RPC调度器的DmtpRpcFeature实例。</returns>
|
||||
public DmtpRpcFeature UseGlobalQueueRpcDispatcher()
|
||||
{
|
||||
// 设置创建调度器的委托,使用GlobalQueueRpcDispatcher实现
|
||||
this.CreateDispatcher = (actor) => this.m_globalQueueRpcDispatcher;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用即时RPC调度器配置RPC特性
|
||||
/// </summary>
|
||||
@@ -119,6 +146,22 @@ public class DmtpRpcFeature : PluginBase, IDmtpFeature, IDmtpHandshakingPlugin,
|
||||
return this;
|
||||
}
|
||||
|
||||
private class GlobalQueueRpcDispatcher : QueueRpcDispatcher<IDmtpActor, IDmtpRpcCallContext>
|
||||
{
|
||||
public bool Pin { get; set; } = true;
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (this.Pin)
|
||||
{
|
||||
return;
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Dispatcher
|
||||
|
||||
/// <summary>
|
||||
/// 设置<see cref="DmtpRpcFeature"/>的起始协议。
|
||||
/// <para>
|
||||
@@ -144,17 +187,15 @@ public class DmtpRpcFeature : PluginBase, IDmtpFeature, IDmtpHandshakingPlugin,
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 配置默认的序列化选择器。
|
||||
/// </summary>
|
||||
/// <param name="selector">用于配置默认序列化选择器的操作。</param>
|
||||
/// <returns>返回当前的 <see cref="DmtpRpcFeature"/> 实例,以支持链式调用。</returns>
|
||||
public DmtpRpcFeature ConfigureDefaultSerializationSelector(Action<DefaultSerializationSelector> selector)
|
||||
/// <inheritdoc/>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
var serializationSelector = new DefaultSerializationSelector();
|
||||
selector.Invoke(serializationSelector);
|
||||
this.SerializationSelector = serializationSelector;
|
||||
return this;
|
||||
if (disposing)
|
||||
{
|
||||
this.m_globalQueueRpcDispatcher.Pin = false;
|
||||
this.m_globalQueueRpcDispatcher.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private static DmtpRpcActor PrivateCreateDmtpRpcActor(IDmtpActor dmtpActor, IRpcServerProvider rpcServerProvider, IRpcDispatcher<IDmtpActor, IDmtpRpcCallContext> dispatcher)
|
||||
@@ -189,7 +230,7 @@ public class DmtpRpcFeature : PluginBase, IDmtpFeature, IDmtpHandshakingPlugin,
|
||||
dmtpRpcActor.GetInvokeMethod = this.GetInvokeMethod;
|
||||
|
||||
dmtpRpcActor.SetProtocolFlags(this.StartProtocol);
|
||||
client.DmtpActor.SetDmtpRpcActor(dmtpRpcActor);
|
||||
client.DmtpActor.AddActor<DmtpRpcActor>(dmtpRpcActor);
|
||||
|
||||
await e.InvokeNext().ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
@@ -197,7 +238,8 @@ public class DmtpRpcFeature : PluginBase, IDmtpFeature, IDmtpHandshakingPlugin,
|
||||
/// <inheritdoc/>
|
||||
public async Task OnDmtpReceived(IDmtpActorObject client, DmtpMessageEventArgs e)
|
||||
{
|
||||
if (client.DmtpActor.GetDmtpRpcActor() is DmtpRpcActor dmtpRpcActor)
|
||||
var dmtpRpcActor = client.DmtpActor.GetActor<DmtpRpcActor>();
|
||||
if (dmtpRpcActor != null)
|
||||
{
|
||||
if (await dmtpRpcActor.InputReceivedData(e.DmtpMessage).ConfigureAwait(EasyTask.ContinueOnCapturedContext))
|
||||
{
|
||||
|
||||
@@ -11,13 +11,14 @@
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace TouchSocket.Dmtp;
|
||||
|
||||
/// <summary>
|
||||
/// 定义了Actor接口,用于规范Actor的行为。
|
||||
/// </summary>
|
||||
public interface IActor
|
||||
public interface IActor:IDisposableObject
|
||||
{
|
||||
/// <summary>
|
||||
/// 包含当前Actor的父容器。
|
||||
|
||||
@@ -19,6 +19,6 @@ namespace TouchSocket.Dmtp;
|
||||
/// 定义了IDmtpClient接口,它继承了多个与DMTP客户端行为相关的接口。
|
||||
/// 这些接口共同定义了客户端在系统中的行为和职责,包括但不限于客户端的连接、配置、状态管理等。
|
||||
/// </summary>
|
||||
public interface IDmtpClient : IDmtpActorObject, IClient, IClosableClient, ISetupConfigObject, IConnectableClient, IIdClient, IOnlineClient
|
||||
public interface IDmtpClient : IDmtpActorObject, IDependencyClient, IClosableClient, ISetupConfigObject, IConnectableClient, IIdClient, IOnlineClient
|
||||
{
|
||||
}
|
||||
@@ -26,7 +26,7 @@ public class DmtpHeartbeatPlugin : HeartbeatPlugin, IDmtpHandshakedPlugin
|
||||
/// <inheritdoc/>
|
||||
public async Task OnDmtpHandshaked(IDmtpActorObject client, DmtpVerifyEventArgs e)
|
||||
{
|
||||
_ = Task.Factory.StartNew(async () =>
|
||||
_ = EasyTask.Run(async () =>
|
||||
{
|
||||
var failedCount = 0;
|
||||
while (true)
|
||||
@@ -40,7 +40,7 @@ public class DmtpHeartbeatPlugin : HeartbeatPlugin, IDmtpHandshakedPlugin
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (DateTime.UtcNow - client.DmtpActor.LastActiveTime < this.Tick)
|
||||
if (DateTimeOffset.UtcNow - client.DmtpActor.LastActiveTime < this.Tick)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -58,7 +58,7 @@ public class DmtpHeartbeatPlugin : HeartbeatPlugin, IDmtpHandshakedPlugin
|
||||
}
|
||||
}
|
||||
}
|
||||
}, TaskCreationOptions.LongRunning);
|
||||
});
|
||||
|
||||
await e.InvokeNext();
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ internal class DmtpReconnectionPlugin<TClient> : ReconnectionPlugin<TClient>, ID
|
||||
return false;
|
||||
}
|
||||
|
||||
if (DateTime.UtcNow - client.GetLastActiveTime() < this.Tick)
|
||||
if (DateTimeOffset.UtcNow - client.GetLastActiveTime() < this.Tick)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -60,6 +60,15 @@ namespace TouchSocket.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 An Actor of type "{0}" already exists. 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string ActorAlreadyExists {
|
||||
get {
|
||||
return ResourceManager.GetString("ActorAlreadyExists", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 The channel with ID {0} already exists. 的本地化字符串。
|
||||
/// </summary>
|
||||
|
||||
@@ -166,4 +166,7 @@
|
||||
<data name="LengthErrorWhenRead" xml:space="preserve">
|
||||
<value>Error reading file length.</value>
|
||||
</data>
|
||||
<data name="ActorAlreadyExists" xml:space="preserve">
|
||||
<value>An Actor of type "{0}" already exists.</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -189,4 +189,7 @@
|
||||
<value>读取文件长度错误。</value>
|
||||
<comment>读取文件长度错误。</comment>
|
||||
</data>
|
||||
<data name="ActorAlreadyExists" xml:space="preserve">
|
||||
<value>已经存在类型为{0}的Actor。</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1,292 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:https://touchsocket.net/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//using System.ComponentModel;
|
||||
|
||||
//namespace TouchSocket.Resources
|
||||
//{
|
||||
// /// <summary>
|
||||
// /// TouchSocketDmtp资源枚举
|
||||
// /// </summary>
|
||||
// public enum TouchSocketDmtpResource
|
||||
// {
|
||||
// /// <summary>
|
||||
// /// 未知错误
|
||||
// /// </summary>
|
||||
// [Description("未知错误")]
|
||||
// UnknownError,
|
||||
|
||||
// /// <summary>
|
||||
// /// 操作成功
|
||||
// /// </summary>
|
||||
// [Description("操作成功")]
|
||||
// Success,
|
||||
|
||||
// /// <summary>
|
||||
// /// 操作超时
|
||||
// /// </summary>
|
||||
// [Description("操作超时")]
|
||||
// Overtime,
|
||||
|
||||
// /// <summary>
|
||||
// /// 用户主动取消操作。
|
||||
// /// </summary>
|
||||
// [Description("用户主动取消操作。")]
|
||||
// Canceled,
|
||||
|
||||
// /// <summary>
|
||||
// /// 参数‘{0}’为空。
|
||||
// /// </summary>
|
||||
// [Description("参数‘{0}’为空。")]
|
||||
// ArgumentNull,
|
||||
|
||||
// /// <summary>
|
||||
// ///发生异常,信息:{0}。
|
||||
// /// </summary>
|
||||
// [Description("发生异常,信息:{0}。")]
|
||||
// Exception,
|
||||
|
||||
// /// <summary>
|
||||
// /// DmtpRpcActor为空,可能需要启用DmtpRpc插件。
|
||||
// /// </summary>
|
||||
// [Description("DmtpRpcActor为空,可能需要启用DmtpRpc插件。")]
|
||||
// DmtpRpcActorArgumentNull,
|
||||
|
||||
// /// <summary>
|
||||
// /// DmtpFileTransferActor为空,可能需要启用DmtpFileTransfer插件。
|
||||
// /// </summary>
|
||||
// [Description("DmtpFileTransferActor为空,可能需要启用DmtpFileTransfer插件。")]
|
||||
// DmtpFileTransferActorNull,
|
||||
|
||||
// /// <summary>
|
||||
// /// RedisActor为空,可能需要启用RedisActor插件。
|
||||
// /// </summary>
|
||||
// [Description("RedisActor为空,可能需要启用RedisActor插件。")]
|
||||
// RedisActorNull,
|
||||
|
||||
// /// <summary>
|
||||
// /// RemoteAccessActor为空,可能需要启用RemoteAccess插件。
|
||||
// /// </summary>
|
||||
// [Description("RemoteAccessActor为空,可能需要启用RemoteAccess插件。")]
|
||||
// RemoteAccessActorNull,
|
||||
|
||||
// /// <summary>
|
||||
// /// RemoteStreamActor为空,可能需要启用RemoteStream插件。
|
||||
// /// </summary>
|
||||
// [Description("RemoteStreamActor为空,可能需要启用RemoteStream插件。")]
|
||||
// RemoteStreamActorNull,
|
||||
|
||||
// #region DmtpRpc
|
||||
|
||||
// /// <summary>
|
||||
// /// 不允许路由该包,信息:{0}。
|
||||
// /// </summary>
|
||||
// [Description("不允许路由该包,信息:{0}。")]
|
||||
// RoutingNotAllowed,
|
||||
|
||||
// /// <summary>
|
||||
// /// 未找到该公共方法,或该方法未标记为Rpc
|
||||
// /// </summary>
|
||||
// [Description("未找到该公共方法,或该方法未标记为Rpc")]
|
||||
// RpcMethodNotFind,
|
||||
|
||||
// /// <summary>
|
||||
// /// 方法已被禁用
|
||||
// /// </summary>
|
||||
// [Description("方法已被禁用")]
|
||||
// RpcMethodDisable,
|
||||
|
||||
// /// <summary>
|
||||
// /// 函数执行异常,详细信息:{0}
|
||||
// /// </summary>
|
||||
// [Description("函数执行异常,详细信息:{0}")]
|
||||
// RpcInvokeException,
|
||||
|
||||
// /// <summary>
|
||||
// /// 事件操作器异常
|
||||
// /// </summary>
|
||||
// [Description("事件操作器异常。")]
|
||||
// GetEventArgsFail,
|
||||
|
||||
// /// <summary>
|
||||
// /// 通道设置失败。
|
||||
// /// </summary>
|
||||
// [Description("通道设置失败。")]
|
||||
// SetChannelFail,
|
||||
|
||||
// /// <summary>
|
||||
// /// Id为{0}的通道已存在。
|
||||
// /// </summary>
|
||||
// [Description("Id为{0}的通道已存在。")]
|
||||
// ChannelExisted,
|
||||
|
||||
// /// <summary>
|
||||
// /// 远程终端拒绝该操作,反馈信息:{0}。
|
||||
// /// </summary>
|
||||
// [Description("远程终端拒绝该操作,反馈信息:{0}。")]
|
||||
// RemoteRefuse,
|
||||
|
||||
// /// <summary>
|
||||
// /// 从‘{0}’创建写入流失败,信息:{1}。"
|
||||
// /// </summary>
|
||||
// [Description("从‘{0}’创建写入流失败,信息:{1}。")]
|
||||
// CreateWriteStreamFail,
|
||||
|
||||
// /// <summary>
|
||||
// ///没有找到路径‘{0}’对应的流文件。
|
||||
// /// </summary>
|
||||
// [Description("没有找到路径‘{0}’对应的流文件。")]
|
||||
// StreamNotFind,
|
||||
|
||||
// /// <summary>
|
||||
// /// 没有找到Id为{0}的客户端。
|
||||
// /// </summary>
|
||||
// [Description("没有找到Id为{0}的客户端。")]
|
||||
// ClientNotFind,
|
||||
|
||||
// /// <summary>
|
||||
// /// 路径‘{0}’对应的流文件,仍然被‘{1}’对象应用。
|
||||
// /// </summary>
|
||||
// [Description("路径‘{0}’对应的流文件,仍然被‘{1}’对象应用。")]
|
||||
// StreamReferencing,
|
||||
|
||||
// /// <summary>
|
||||
// /// 接收流容器为空
|
||||
// /// </summary>
|
||||
// [Description("流容器为空。")]
|
||||
// StreamBucketNull,
|
||||
|
||||
// /// <summary>
|
||||
// /// 从‘{0}’路径加载流异常,信息:‘{1}’。
|
||||
// /// </summary>
|
||||
// [Description("从‘{0}’路径加载流异常,信息:‘{1}’。")]
|
||||
// LoadStreamFail,
|
||||
|
||||
// /// <summary>
|
||||
// /// 目录‘{0}’已存在。
|
||||
// /// </summary>
|
||||
// [Description("目录‘{0}’已存在。")]
|
||||
// DirectoryExisted,
|
||||
|
||||
// /// <summary>
|
||||
// /// 文件‘{0}’已存在。
|
||||
// /// </summary>
|
||||
// [Description("文件‘{0}’已存在。")]
|
||||
// FileExisted,
|
||||
|
||||
// /// <summary>
|
||||
// /// 文件‘{0}’不存在。
|
||||
// /// </summary>
|
||||
// [Description("文件‘{0}’不存在。")]
|
||||
// FileNotExists,
|
||||
|
||||
// /// <summary>
|
||||
// /// 目录‘{0}’不存在。
|
||||
// /// </summary>
|
||||
// [Description("目录‘{0}’不存在。")]
|
||||
// DirectoryNotExists,
|
||||
|
||||
// /// <summary>
|
||||
// /// 名称为“{0}”的事件已存在
|
||||
// /// </summary>
|
||||
// [Description("名称为“{0}”的事件已存在。")]
|
||||
// EventExisted,
|
||||
|
||||
// /// <summary>
|
||||
// /// 名称为“{0}”的事件不存在
|
||||
// /// </summary>
|
||||
// [Description("名称为“{0}”的事件不存在。")]
|
||||
// EventNotExist,
|
||||
|
||||
// /// <summary>
|
||||
// /// 资源句柄{0}对应的资源没有找到,可能操作已超时。
|
||||
// /// </summary>
|
||||
// [Description("资源句柄{0}对应的资源没有找到,可能操作已超时。")]
|
||||
// ResourceHandleNotFind,
|
||||
|
||||
// /// <summary>
|
||||
// /// 还有{0}个资源没有完成。
|
||||
// /// </summary>
|
||||
// [Description("还有{0}个资源没有完成。")]
|
||||
// HasUnFinished,
|
||||
|
||||
// /// <summary>
|
||||
// /// 文件长度太长。
|
||||
// /// </summary>
|
||||
// [Description("文件长度太长。")]
|
||||
// FileLengthTooLong,
|
||||
|
||||
// /// <summary>
|
||||
// /// 读取文件长度错误。
|
||||
// /// </summary>
|
||||
// [Description("读取文件长度错误。")]
|
||||
// LengthErrorWhenRead,
|
||||
|
||||
// /// <summary>
|
||||
// /// 没有找到任何可用的目标Id。
|
||||
// /// </summary>
|
||||
// [Description("没有找到任何可用的目标Id。")]
|
||||
// NotFindAnyTargetId,
|
||||
|
||||
// #endregion DmtpRpc
|
||||
|
||||
// #region Core
|
||||
|
||||
// /// <summary>
|
||||
// /// Token消息为‘{0}’的已注册。
|
||||
// /// </summary>
|
||||
// [Description("Token消息为‘{0}’的已注册。")]
|
||||
// TokenExisted,
|
||||
|
||||
// /// <summary>
|
||||
// /// Token消息为‘{0}’的未注册。
|
||||
// /// </summary>
|
||||
// [Description("Token消息为‘{0}’的未注册。")]
|
||||
// MessageNotFound,
|
||||
|
||||
// /// <summary>
|
||||
// /// 无法创建未被注册的类型{0}的实例。
|
||||
// /// </summary>
|
||||
// [Description("无法创建未被注册的类型{0}的实例。")]
|
||||
// UnregisteredType,
|
||||
|
||||
// /// <summary>
|
||||
// /// 没有找到类型{0}的公共构造函数。
|
||||
// /// </summary>
|
||||
// [Description("没有找到类型{0}的公共构造函数。")]
|
||||
// NotFindPublicConstructor,
|
||||
|
||||
// #endregion Core
|
||||
|
||||
// #region Client
|
||||
|
||||
// /// <summary>
|
||||
// /// 数据处理适配器为空,可能客户端已掉线。
|
||||
// /// </summary>
|
||||
// [Description("数据处理适配器为空,可能客户端已掉线。")]
|
||||
// NullDataAdapter,
|
||||
|
||||
// /// <summary>
|
||||
// /// 客户端没有连接
|
||||
// /// </summary>
|
||||
// [Description("客户端没有连接。")]
|
||||
// NotConnected,
|
||||
|
||||
// /// <summary>
|
||||
// /// 授权密钥无效,程序将在5秒后退出。请检查密钥,或者不使用企业版功能。
|
||||
// /// </summary>
|
||||
// [Description("授权密钥无效,程序将在5秒后退出。请检查密钥,或者不使用企业版功能。")]
|
||||
// LicenceKeyInvalid,
|
||||
|
||||
// #endregion Client
|
||||
// }
|
||||
//}
|
||||
@@ -23,12 +23,12 @@ namespace TouchSocket.Hosting.Sockets.HostService;
|
||||
|
||||
internal class ServiceHost<TService> : SetupConfigObjectHostedService<TService> where TService : ISetupConfigObject, IServiceBase
|
||||
{
|
||||
private ILogger<ServiceHost<TService>> m_logger;
|
||||
private ILogger<TService> m_logger;
|
||||
|
||||
protected override void OnSetResolver(IResolver resolver)
|
||||
{
|
||||
base.OnSetResolver(resolver);
|
||||
this.m_logger = resolver.GetService<ILogger<ServiceHost<TService>>>();
|
||||
this.m_logger = resolver.GetService<ILogger<TService>>();
|
||||
}
|
||||
|
||||
public override async Task StartAsync(CancellationToken cancellationToken)
|
||||
|
||||
75
src/TouchSocket.Http/BlockSegment/HttpBlockSegment.cs
Normal file
75
src/TouchSocket.Http/BlockSegment/HttpBlockSegment.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:https://touchsocket.net/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace TouchSocket.Http;
|
||||
class HttpBlockSegment : BlockSegment<IReadOnlyMemoryBlockResult>
|
||||
{
|
||||
HttpReadOnlyMemoryBlockResult m_blockResult;
|
||||
protected override IReadOnlyMemoryBlockResult CreateResult(Action actionForDispose)
|
||||
{
|
||||
m_blockResult = new HttpReadOnlyMemoryBlockResult(actionForDispose);
|
||||
return m_blockResult;
|
||||
}
|
||||
|
||||
internal async Task InternalComplete(string msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.m_blockResult.IsCompleted = true;
|
||||
this.m_blockResult.Message = msg;
|
||||
await this.TriggerAsync().ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
internal Task InternalInputAsync(in ReadOnlyMemory<byte> memory)
|
||||
{
|
||||
this.m_blockResult.Memory = memory;
|
||||
return base.TriggerAsync();
|
||||
}
|
||||
|
||||
protected override void CompleteRead()
|
||||
{
|
||||
// 清除结果中的内存数据
|
||||
this.m_blockResult.Memory = default;
|
||||
// 清除结果中的消息
|
||||
this.m_blockResult.Message = default;
|
||||
base.CompleteRead();
|
||||
}
|
||||
|
||||
|
||||
internal void InternalReset()
|
||||
{
|
||||
// 将块结果标记为未完成
|
||||
this.m_blockResult.IsCompleted = false;
|
||||
// 清除结果中的内存数据
|
||||
this.m_blockResult.Memory = default;
|
||||
// 清除结果中的消息
|
||||
this.m_blockResult.Message = default;
|
||||
}
|
||||
|
||||
internal ValueTask<IReadOnlyMemoryBlockResult> InternalValueWaitAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return base.ProtectedReadAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
|
||||
// CSDN博客:https://blog.csdn.net/qq_40374647
|
||||
// 哔哩哔哩视频:https://space.bilibili.com/94253567
|
||||
// Gitee源代码仓库:https://gitee.com/RRQM_Home
|
||||
// Github源代码仓库:https://github.com/RRQM
|
||||
// API首页:https://touchsocket.net/
|
||||
// 交流QQ群:234762506
|
||||
// 感谢您的下载和使用
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace TouchSocket.Http;
|
||||
class HttpReadOnlyMemoryBlockResult : IReadOnlyMemoryBlockResult
|
||||
{
|
||||
|
||||
public static readonly IReadOnlyMemoryBlockResult Completed = new HttpReadOnlyMemoryBlockResult(() => { }) { IsCompleted = true };
|
||||
|
||||
public static IReadOnlyMemoryBlockResult FromResult(ReadOnlyMemory<byte> memory)
|
||||
{
|
||||
return new HttpReadOnlyMemoryBlockResult(() => { }) { IsCompleted = true, Memory = memory };
|
||||
}
|
||||
|
||||
private readonly Action m_actionForDispose;
|
||||
|
||||
public HttpReadOnlyMemoryBlockResult(Action actionForDispose)
|
||||
{
|
||||
this.m_actionForDispose = actionForDispose;
|
||||
}
|
||||
public ReadOnlyMemory<byte> Memory { get; set; }
|
||||
|
||||
public bool IsCompleted { get; set; }
|
||||
|
||||
public string Message { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
m_actionForDispose.Invoke();
|
||||
}
|
||||
}
|
||||
@@ -35,17 +35,13 @@ public abstract class HttpBase : IRequestInfo
|
||||
/// </summary>
|
||||
public const int MaxCacheSize = 1024 * 1024 * 100;
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置HTTP内容。
|
||||
/// </summary>
|
||||
public virtual HttpContent Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 服务器版本
|
||||
/// </summary>
|
||||
public static readonly string ServerVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
||||
|
||||
private readonly InternalHttpHeader m_headers = new InternalHttpHeader();
|
||||
|
||||
private readonly HttpBlockSegment m_httpBlockSegment = new HttpBlockSegment();
|
||||
|
||||
/// <summary>
|
||||
@@ -57,11 +53,6 @@ public abstract class HttpBase : IRequestInfo
|
||||
set => this.m_headers.Add(HttpHeaders.Accept, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否在Server端工作
|
||||
/// </summary>
|
||||
public abstract bool IsServer { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 允许编码
|
||||
/// </summary>
|
||||
@@ -71,6 +62,11 @@ public abstract class HttpBase : IRequestInfo
|
||||
set => this.m_headers.Add(HttpHeaders.AcceptEncoding, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取或设置HTTP内容。
|
||||
/// </summary>
|
||||
public virtual HttpContent Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 内容填充完成
|
||||
/// </summary>
|
||||
@@ -103,6 +99,11 @@ public abstract class HttpBase : IRequestInfo
|
||||
/// </summary>
|
||||
public IHttpHeader Headers => this.m_headers;
|
||||
|
||||
/// <summary>
|
||||
/// 是否在Server端工作
|
||||
/// </summary>
|
||||
public abstract bool IsServer { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 保持连接。
|
||||
/// <para>
|
||||
@@ -158,7 +159,7 @@ public abstract class HttpBase : IRequestInfo
|
||||
|
||||
internal Task CompleteInput()
|
||||
{
|
||||
return this.m_httpBlockSegment.InternalComplete();
|
||||
return this.m_httpBlockSegment.InternalComplete(string.Empty);
|
||||
}
|
||||
|
||||
internal Task InternalInputAsync(ReadOnlyMemory<byte> memory)
|
||||
@@ -192,32 +193,32 @@ public abstract class HttpBase : IRequestInfo
|
||||
this.m_httpBlockSegment.InternalReset();
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// 读取信息
|
||||
///// </summary>
|
||||
//protected abstract void LoadHeaderProperties();
|
||||
|
||||
//private void GetRequestHeaders(string[] rows)
|
||||
//{
|
||||
// this.m_headers.Clear();
|
||||
// if (rows == null || rows.Length <= 0)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// foreach (var item in rows)
|
||||
// {
|
||||
// var kv = item.SplitFirst(':');
|
||||
// if (kv.Length == 2)
|
||||
// {
|
||||
// var key = kv[0].ToLower();
|
||||
// this.m_headers.Add(key, kv[1]);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 读取请求行。
|
||||
/// </summary>
|
||||
/// <param name="requestLineSpan">包含请求行的只读字节跨度。</param>
|
||||
protected abstract void ReadRequestLine(ReadOnlySpan<byte> requestLineSpan);
|
||||
|
||||
private void ParseHeaderLine(ReadOnlySpan<byte> line)
|
||||
{
|
||||
var colonIndex = line.IndexOf((byte)':');
|
||||
if (colonIndex <= 0)
|
||||
{
|
||||
return; // 无效格式
|
||||
}
|
||||
|
||||
// 分割键值
|
||||
var keySpan = line.Slice(0, colonIndex).Trim();
|
||||
var valueSpan = line.Slice(colonIndex + 1).Trim();
|
||||
|
||||
if (!keySpan.IsEmpty && !valueSpan.IsEmpty)
|
||||
{
|
||||
string key = keySpan.ToString(Encoding.UTF8).ToLower();
|
||||
string value = valueSpan.ToString(Encoding.UTF8);
|
||||
this.m_headers[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadHeaders(ReadOnlySpan<byte> span)
|
||||
{
|
||||
//string ss=span.ToString(Encoding.UTF8);
|
||||
@@ -266,56 +267,8 @@ public abstract class HttpBase : IRequestInfo
|
||||
//this.LoadHeaderProperties();
|
||||
}
|
||||
|
||||
private void ParseHeaderLine(ReadOnlySpan<byte> line)
|
||||
{
|
||||
var colonIndex = line.IndexOf((byte)':');
|
||||
if (colonIndex <= 0)
|
||||
{
|
||||
return; // 无效格式
|
||||
}
|
||||
|
||||
// 分割键值
|
||||
var keySpan = line.Slice(0, colonIndex).Trim();
|
||||
var valueSpan = line.Slice(colonIndex + 1).Trim();
|
||||
|
||||
if (!keySpan.IsEmpty && !valueSpan.IsEmpty)
|
||||
{
|
||||
string key = keySpan.ToString(Encoding.UTF8).ToLower();
|
||||
string value = valueSpan.ToString(Encoding.UTF8);
|
||||
this.m_headers[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
//private void ReadHeaders(ReadOnlySpan<byte> span)
|
||||
//{
|
||||
// var data = span.ToString(Encoding.UTF8);
|
||||
// var rows = Regex.Split(data, "\r\n");
|
||||
|
||||
// //Request URL & Method & Version
|
||||
// this.RequestLine = rows[0];
|
||||
|
||||
// this.m_headers.Clear();
|
||||
// if (rows == null || rows.Length <= 0)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// foreach (var item in rows)
|
||||
// {
|
||||
// var kv = item.SplitFirst(':');
|
||||
// if (kv.Length == 2)
|
||||
// {
|
||||
// var key = kv[0].ToLower();
|
||||
// this.m_headers.Add(key, kv[1]);
|
||||
// }
|
||||
// }
|
||||
|
||||
// this.LoadHeaderProperties();
|
||||
//}
|
||||
|
||||
#region Content
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取一次性内容。
|
||||
/// </summary>
|
||||
@@ -333,8 +286,8 @@ public abstract class HttpBase : IRequestInfo
|
||||
/// 异步读取HTTP块段的内容。
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">用于取消异步操作的令牌。</param>
|
||||
/// <returns>返回一个<see cref="IBlockResult{T}"/>,表示异步读取操作的结果。</returns>
|
||||
public virtual ValueTask<IBlockResult<byte>> ReadAsync(CancellationToken cancellationToken)
|
||||
/// <returns>返回一个<see cref="IReadOnlyMemoryBlockResult"/>,表示异步读取操作的结果。</returns>
|
||||
public virtual ValueTask<IReadOnlyMemoryBlockResult> ReadAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
// 调用m_httpBlockSegment的InternalValueWaitAsync方法,等待HTTP块段的内部值。
|
||||
return this.m_httpBlockSegment.InternalValueWaitAsync(cancellationToken);
|
||||
@@ -366,7 +319,6 @@ public abstract class HttpBase : IRequestInfo
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 异步读取并复制流数据
|
||||
/// </summary>
|
||||
@@ -410,31 +362,4 @@ public abstract class HttpBase : IRequestInfo
|
||||
}
|
||||
|
||||
#endregion Read
|
||||
|
||||
#region Class
|
||||
|
||||
private class HttpBlockSegment : BlockSegment<byte>
|
||||
{
|
||||
internal Task InternalComplete()
|
||||
{
|
||||
return base.Complete(string.Empty);
|
||||
}
|
||||
|
||||
internal Task InternalInputAsync(in ReadOnlyMemory<byte> memory)
|
||||
{
|
||||
return base.InputAsync(memory);
|
||||
}
|
||||
|
||||
internal void InternalReset()
|
||||
{
|
||||
base.Reset();
|
||||
}
|
||||
|
||||
internal ValueTask<IBlockResult<byte>> InternalValueWaitAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return base.ValueWaitAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Class
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user