mirror of
https://github.com/RRQM/TouchSocket.git
synced 2025-12-17 08:56:43 +08:00
发布:4.0.3
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<BaseVersion>4.0.2</BaseVersion>
|
||||
<BaseVersion>4.0.3</BaseVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'">
|
||||
|
||||
@@ -182,7 +182,7 @@ public class WebSocketDmtpService : ConnectableService<WebSocketDmtpSessionClien
|
||||
/// </summary>
|
||||
/// <returns>异步任务。</returns>
|
||||
/// <exception cref="NotSupportedException">抛出不支持异常。</exception>
|
||||
public override Task StartAsync()
|
||||
public override Task StartAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
throw new NotSupportedException("此服务的生命周期跟随主Host");
|
||||
}
|
||||
|
||||
@@ -169,14 +169,22 @@ public abstract class HttpDmtpSessionClient : HttpSessionClient, IHttpDmtpSessio
|
||||
/// <inheritdoc/>
|
||||
protected override async Task OnTcpClosed(ClosedEventArgs e)
|
||||
{
|
||||
await this.OnDmtpClosed(e).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
if (this.m_dmtpActor!=null)
|
||||
{
|
||||
await this.OnDmtpClosed(e).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
await base.OnTcpClosed(e).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override async Task OnTcpClosing(ClosingEventArgs e)
|
||||
{
|
||||
await this.PluginManager.RaiseAsync(typeof(IDmtpClosingPlugin), this.Resolver, this, e).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
if (this.m_dmtpActor != null)
|
||||
{
|
||||
await this.PluginManager.RaiseIDmtpClosingPluginAsync(this.Resolver, this, e).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
await base.OnTcpClosing(e).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
@@ -187,7 +195,7 @@ public abstract class HttpDmtpSessionClient : HttpSessionClient, IHttpDmtpSessio
|
||||
{
|
||||
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);
|
||||
await this.PluginManager.RaiseIDmtpReceivedPluginAsync(this.Resolver, this, new DmtpMessageEventArgs(message)).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
}
|
||||
await base.OnTcpReceived(e).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using TouchSocket.Core.AspNetCore;
|
||||
using TouchSocket.Hosting;
|
||||
using TouchSocket.Hosting.Sockets.HostService;
|
||||
using TouchSocket.Hosting.HostedServices;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection;
|
||||
@@ -143,6 +143,20 @@ public static class ServiceCollectionExtensions
|
||||
return AddSetupConfigObjectHostedService<ServiceHost<TObjectService>, TObjectService, TObjectImpService>(services, actionConfig);
|
||||
}
|
||||
|
||||
public static IServiceCollection AddClientHostedService<TObjectClient, [DynamicallyAccessedMembers(AOT.Container)] TClientImpService>(this IServiceCollection services, Action<TouchSocketConfig> actionConfig)
|
||||
where TObjectClient : class, ISetupConfigObject, IConnectableClient,IClosableClient
|
||||
where TClientImpService : class, TObjectClient
|
||||
{
|
||||
return AddSetupConfigObjectHostedService<ClientHost<TObjectClient>, TObjectClient, TClientImpService>(services, actionConfig);
|
||||
}
|
||||
|
||||
public static IServiceCollection AddSetupConfigObjectHostedService<TObject, [DynamicallyAccessedMembers(AOT.Container)] TObjectImp>(this IServiceCollection services, Action<TouchSocketConfig> actionConfig)
|
||||
where TObject : class, ISetupConfigObject
|
||||
where TObjectImp : class, TObject
|
||||
{
|
||||
return AddSetupConfigObjectHostedService<SetupConfigObjectHostedService<TObject>, TObject, TObjectImp>(services, actionConfig);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加配置对象托管服务
|
||||
/// </summary>
|
||||
|
||||
50
src/TouchSocket.Hosting/HostedServices/ClientHost.cs
Normal file
50
src/TouchSocket.Hosting/HostedServices/ClientHost.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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 Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using TouchSocket.Resources;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace TouchSocket.Hosting.HostedServices;
|
||||
|
||||
internal class ClientHost<TService> : SetupConfigObjectHostedService<TService> where TService : ISetupConfigObject, IConnectableClient,IClosableClient
|
||||
{
|
||||
private ILogger<TService> m_logger;
|
||||
|
||||
protected override void OnSetResolver(IResolver resolver)
|
||||
{
|
||||
base.OnSetResolver(resolver);
|
||||
this.m_logger = resolver.GetService<ILogger<TService>>();
|
||||
}
|
||||
|
||||
public override async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
await base.StartAsync(cancellationToken).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
await this.ConfigObject.ConnectAsync(cancellationToken).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
|
||||
this.m_logger.LogInformation("{Message}", TouchSocketHostingResource.HostServerStarted);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.m_logger.LogError(ex, "{Message}", ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
await this.ConfigObject.CloseAsync("服务停止",cancellationToken).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
await base.StopAsync(cancellationToken).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ using Microsoft.Extensions.Logging;
|
||||
using TouchSocket.Resources;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace TouchSocket.Hosting.Sockets.HostService;
|
||||
namespace TouchSocket.Hosting.HostedServices;
|
||||
|
||||
internal class ServiceHost<TService> : SetupConfigObjectHostedService<TService> where TService : ISetupConfigObject, IServiceBase
|
||||
{
|
||||
@@ -32,7 +32,7 @@ internal class ServiceHost<TService> : SetupConfigObjectHostedService<TService>
|
||||
try
|
||||
{
|
||||
await base.StartAsync(cancellationToken).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
await this.ConfigObject.StartAsync().ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
await this.ConfigObject.StartAsync(cancellationToken).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
|
||||
this.m_logger.LogInformation("{Message}", TouchSocketHostingResource.HostServerStarted);
|
||||
}
|
||||
@@ -44,6 +44,7 @@ internal class ServiceHost<TService> : SetupConfigObjectHostedService<TService>
|
||||
|
||||
public override async Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
await this.ConfigObject.StopAsync();
|
||||
await this.ConfigObject.StopAsync(cancellationToken).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
await base.StopAsync(cancellationToken).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ namespace TouchSocket.Hosting;
|
||||
/// <summary>
|
||||
/// SetupObjectHostedService
|
||||
/// </summary>
|
||||
public abstract class SetupConfigObjectHostedService<TConfigObject> : IHostedService where TConfigObject : ISetupConfigObject
|
||||
public class SetupConfigObjectHostedService<TConfigObject> : IHostedService where TConfigObject : ISetupConfigObject
|
||||
{
|
||||
private TouchSocketConfig m_config;
|
||||
private TConfigObject m_configObject;
|
||||
@@ -81,5 +81,9 @@ public abstract class SetupConfigObjectHostedService<TConfigObject> : IHostedSer
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public abstract Task StopAsync(CancellationToken cancellationToken);
|
||||
public virtual Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
this.m_configObject.Dispose();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// 此代码版权(除特别声明或在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.Net.Mail;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
namespace TouchSocket.Http.WebSockets;
|
||||
|
||||
/// <summary>
|
||||
/// 提供用于为 <see cref="ReconnectionOption{TClient}"/> 配置 WebSocket 心跳检查的扩展方法。
|
||||
/// </summary>
|
||||
public static class ReconnectionOptionsExtension
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 为 <see cref="ReconnectionOption{TClient}"/> 设置一个基于活动时间与 Ping 的检查动作。
|
||||
/// </summary>
|
||||
/// <typeparam name="TClient">实现了 <see cref="IConnectableClient"/>, <see cref="IOnlineClient"/>, <see cref="IDependencyClient"/>, <see cref="IWebSocketClient"/> 的客户端类型。</typeparam>
|
||||
/// <param name="reconnectionOption">要配置的 <see cref="ReconnectionOption{TClient}"/> 实例。</param>
|
||||
/// <param name="activeTimeSpan">在此时间范围内若有活动则跳过心跳检测,默认 3 秒。</param>
|
||||
/// <param name="pingTimeout">执行 Ping 与 Close 操作时的超时时间,默认 5 秒。</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException">当 <paramref name="activeTimeSpan"/> 或 <paramref name="pingTimeout"/> 小于或等于零时抛出。</exception>
|
||||
public static void UseWebSocketCheckAction<TClient>(
|
||||
this ReconnectionOption<TClient> reconnectionOption,
|
||||
TimeSpan? activeTimeSpan = null,
|
||||
TimeSpan? pingTimeout = null)
|
||||
where TClient : IConnectableClient, IOnlineClient, IDependencyClient, IWebSocketClient
|
||||
{
|
||||
ThrowHelper.ThrowIfNull(reconnectionOption, nameof(reconnectionOption));
|
||||
var span = activeTimeSpan ?? TimeSpan.FromSeconds(3);
|
||||
var timeout = pingTimeout ?? TimeSpan.FromSeconds(5);
|
||||
|
||||
// 验证时间参数的有效性
|
||||
if (span <= TimeSpan.Zero)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(activeTimeSpan), "活动时间间隔必须大于零");
|
||||
}
|
||||
|
||||
if (timeout <= TimeSpan.Zero)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(pingTimeout), "Ping超时时间必须大于零");
|
||||
}
|
||||
|
||||
reconnectionOption.CheckAction = async (client) =>
|
||||
{
|
||||
// 第1步:快速在线状态检查
|
||||
// 如果客户端已经离线,无需进一步检查,直接返回Dead状态
|
||||
if (!client.Online)
|
||||
{
|
||||
return ConnectionCheckResult.Dead;
|
||||
}
|
||||
|
||||
// 第2步:活动时间检查
|
||||
// 如果客户端在指定时间内有活动,说明连接正常,跳过本次心跳检查
|
||||
var lastActiveTime = client.GetLastActiveTime();
|
||||
var timeSinceLastActivity = DateTimeOffset.UtcNow - lastActiveTime;
|
||||
|
||||
if (timeSinceLastActivity < span)
|
||||
{
|
||||
return ConnectionCheckResult.Skip;
|
||||
}
|
||||
|
||||
// 第3步:主动心跳检查
|
||||
// 通过Ping操作验证连接的实际可用性
|
||||
try
|
||||
{
|
||||
using var pingCts = new CancellationTokenSource(timeout);
|
||||
var pingResult = await client.PingAsync(pingCts.Token).ConfigureAwait(false);
|
||||
|
||||
if (pingResult.IsSuccess)
|
||||
{
|
||||
return ConnectionCheckResult.Alive;
|
||||
}
|
||||
|
||||
using var closeCts = new CancellationTokenSource(timeout);
|
||||
|
||||
var closeResult = await client.CloseAsync("心跳插件ping失败主动断开连接", closeCts.Token).ConfigureAwait(false);
|
||||
|
||||
return ConnectionCheckResult.Dead;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Ping超时,认为连接已死
|
||||
return ConnectionCheckResult.Dead;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 其他异常也认为连接不可用
|
||||
return ConnectionCheckResult.Dead;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -119,7 +119,7 @@ public abstract class NamedPipeServiceBase<TClient> : ConnectableService<TClient
|
||||
}
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public override async Task StartAsync()
|
||||
public override async Task StartAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
this.ThrowIfConfigIsNull();
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ public abstract class ServiceBase : SetupConfigObject, IServiceBase
|
||||
public abstract ServerState ServerState { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public abstract Task StartAsync();
|
||||
public abstract Task StartAsync(CancellationToken cancellationToken=default);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public abstract Task<Result> StopAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
@@ -139,7 +139,7 @@ public abstract class TcpServiceBase<TClient> : ConnectableService<TClient>, ITc
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task StartAsync()
|
||||
public override async Task StartAsync(CancellationToken cancellationToken=default)
|
||||
{
|
||||
this.ThrowIfDisposed();
|
||||
this.ThrowIfConfigIsNull();
|
||||
|
||||
@@ -140,7 +140,7 @@ public abstract class UdpSessionBase : ServiceBase, IUdpSessionBase
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async Task StartAsync()
|
||||
public override async Task StartAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
this.ThrowIfDisposed();
|
||||
try
|
||||
|
||||
@@ -87,7 +87,7 @@ public static class ServiceExtension
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IServiceBase.StartAsync"/>
|
||||
public static async Task StartAsync<TService>(this TService service, IPHost iPHost) where TService : IUdpSession
|
||||
public static async Task StartAsync<TService>(this TService service, IPHost iPHost, CancellationToken cancellationToken = default) where TService : IUdpSession
|
||||
{
|
||||
TouchSocketConfig config;
|
||||
if (service.Config == null)
|
||||
@@ -101,7 +101,7 @@ public static class ServiceExtension
|
||||
config = service.Config;
|
||||
config.SetBindIPHost(iPHost);
|
||||
}
|
||||
await service.StartAsync().ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
await service.StartAsync(cancellationToken).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
}
|
||||
|
||||
#endregion Udp
|
||||
|
||||
@@ -184,11 +184,11 @@ public static class TouchSocketConfigExtension
|
||||
/// <typeparam name="TClient"></typeparam>
|
||||
/// <param name="config"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<TClient> BuildClientAsync<TClient>(this TouchSocketConfig config) where TClient : ISetupConfigObject, IConnectableClient, new()
|
||||
public static async Task<TClient> BuildClientAsync<TClient>(this TouchSocketConfig config, CancellationToken cancellationToken = default) where TClient : ISetupConfigObject, IConnectableClient, new()
|
||||
{
|
||||
var client = new TClient();
|
||||
await client.SetupAsync(config).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
await client.ConnectAsync().ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
await client.ConnectAsync(cancellationToken).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
return client;
|
||||
}
|
||||
|
||||
@@ -198,11 +198,11 @@ public static class TouchSocketConfigExtension
|
||||
/// <typeparam name="TService"></typeparam>
|
||||
/// <param name="config"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<TService> BuildServiceAsync<TService>(this TouchSocketConfig config) where TService : IServiceBase, new()
|
||||
public static async Task<TService> BuildServiceAsync<TService>(this TouchSocketConfig config, CancellationToken cancellationToken = default) where TService : IServiceBase, new()
|
||||
{
|
||||
var service = new TService();
|
||||
await service.SetupAsync(config).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
await service.StartAsync().ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
await service.StartAsync(cancellationToken).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
|
||||
return service;
|
||||
}
|
||||
#endregion 创建
|
||||
|
||||
@@ -31,7 +31,7 @@ public interface IServiceBase : ISetupConfigObject
|
||||
/// 异步启动
|
||||
/// </summary>
|
||||
/// <exception cref="Exception">可能启动时遇到的异常</exception>
|
||||
Task StartAsync();
|
||||
Task StartAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 异步停止服务器
|
||||
|
||||
Reference in New Issue
Block a user