计划任务

This commit is contained in:
snltty
2025-03-27 17:21:16 +08:00
parent 5a80a753f7
commit c403bac74b
18 changed files with 702 additions and 64 deletions

View File

@@ -24,6 +24,16 @@
</div> </div>
## Platforms
| | amd64 | x86 | arm64 | arm |
|-------|-------|-------|-------|-------|
| Windows | ✔ | ✔ |✔ |✔ |
| Linux | ✔ | |✔ |✔ |
| Linux Musl | ✔ | |✔ |✔ |
| Openwrt | ✔ | |✔ |✔ |
| Android(soon) | ✔ | | | |
## Overview ## Overview
Using P2P or server relay, connect multiple LANs to enable communication between any networked devices across these LANs. Using P2P or server relay, connect multiple LANs to enable communication between any networked devices across these LANs.

View File

@@ -24,6 +24,16 @@
</div> </div>
## 支持平台
| | amd64 | x86 | arm64 | arm |
|-------|-------|-------|-------|-------|
| Windows | ✔ | ✔ |✔ |✔ |
| Linux | ✔ | |✔ |✔ |
| Linux Musl | ✔ | |✔ |✔ |
| Openwrt | ✔ | |✔ |✔ |
| Android(soon) | ✔ | | | |
## 大概意思 ## 大概意思
使用p2p或者服务器转发让你的各个局域网连通起来让各个局域网内的任意联网设备都可以相互连通 使用p2p或者服务器转发让你的各个局域网连通起来让各个局域网内的任意联网设备都可以相互连通
@@ -50,7 +60,7 @@
- [x] 异地组网,使用虚拟网卡,将各个客户端组建为局域网络,`点对点``点对网``网对网` - [x] 异地组网,使用虚拟网卡,将各个客户端组建为局域网络,`点对点``点对网``网对网`
- [x] 网卡类库,你可以使用`linker.tun` tun网卡库到你的项目中 - [x] 网卡类库,你可以使用`linker.tun` tun网卡库到你的项目中
- [x] 端口转发,将客户端的端口转发到其它客户端的端口 - [x] 端口转发,将客户端的端口转发到其它客户端的端口
- [x] 服务器穿透,在服务器注册端口或域名,通过访问服务器端口或域名,访问内网服务 - [x] 服务器穿透,在服务器注册端口或域名,通过访问服务器端口或域名,访问内网服务(支持计划任务,定时定长自动开启关闭)
- [x] 权限管理,主客户端拥有完全权限,可导出、配置子客户端配置,分配其管理权限 - [x] 权限管理,主客户端拥有完全权限,可导出、配置子客户端配置,分配其管理权限
- [x] 自定义验证,通过`HTTP POST`让你可以自定义认证是否允许`连接信标``中继``内网穿透` - [x] 自定义验证,通过`HTTP POST`让你可以自定义认证是否允许`连接信标``中继``内网穿透`
- [x] 流量统计,统计服务器`信标``中继``内网穿透` 的流量情况 - [x] 流量统计,统计服务器`信标``中继``内网穿透` 的流量情况

View File

@@ -26,9 +26,9 @@ namespace linker.messenger.entry
{ {
private static ServiceCollection serviceCollection; private static ServiceCollection serviceCollection;
private static ServiceProvider serviceProvider; private static ServiceProvider serviceProvider;
private static OperatingManager inited = new OperatingManager(); private static readonly OperatingManager inited = new OperatingManager();
private static OperatingManager builded = new OperatingManager(); private static readonly OperatingManager builded = new OperatingManager();
private static OperatingManager setuped = new OperatingManager(); private static readonly OperatingManager setuped = new OperatingManager();
/// <summary> /// <summary>
/// 开始初始化 /// 开始初始化
@@ -90,7 +90,7 @@ namespace linker.messenger.entry
.AddSerializerMemoryPack() .AddSerializerMemoryPack()
//计划任务 //计划任务
.AddPlan(); .AddPlanClient().AddPlanServer();
} }
/// <summary> /// <summary>
/// 注入 /// 注入
@@ -145,7 +145,7 @@ namespace linker.messenger.entry
if (modules.HasFlag(ExcludeModule.Logger) == false) if (modules.HasFlag(ExcludeModule.Logger) == false)
serviceProvider.UseLogger(); serviceProvider.UseLogger();
serviceProvider.UseMessenger().UsePlan(); serviceProvider.UseMessenger();
if ((modules & ExcludeModule.StoreFile) != ExcludeModule.StoreFile) if ((modules & ExcludeModule.StoreFile) != ExcludeModule.StoreFile)
serviceProvider.UseStoreFile(configDic); serviceProvider.UseStoreFile(configDic);
@@ -171,6 +171,8 @@ namespace linker.messenger.entry
.UseSignInServer().UseSyncServer().UseTunnelServer().UseFlowServer(); .UseSignInServer().UseSyncServer().UseTunnelServer().UseFlowServer();
serviceProvider.UseListen(); serviceProvider.UseListen();
serviceProvider.UsePlanServer();
} }
if ((commonStore.Modes & CommonModes.Client) == CommonModes.Client) if ((commonStore.Modes & CommonModes.Client) == CommonModes.Client)
@@ -193,6 +195,8 @@ namespace linker.messenger.entry
serviceProvider.UseExRoute().UseAccessClient().UseDecenterClient().UsePcpClient().UseRelayClient().UseSyncClient().UseTunnelClient().UseFlowClient(); serviceProvider.UseExRoute().UseAccessClient().UseDecenterClient().UsePcpClient().UseRelayClient().UseSyncClient().UseTunnelClient().UseFlowClient();
serviceProvider.UseSignInClient(); serviceProvider.UseSignInClient();
serviceProvider.UsePlanClient();
} }
} }

View File

@@ -1,19 +1,41 @@
 
using linker.messenger.api;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace linker.messenger.plan namespace linker.messenger.plan
{ {
public static class Entry public static class Entry
{ {
public static ServiceCollection AddPlan(this ServiceCollection serviceCollection) public static ServiceCollection AddPlanClient(this ServiceCollection serviceCollection)
{ {
serviceCollection.AddSingleton<PlanTransfer>(); serviceCollection.AddSingleton<PlanTransfer>();
serviceCollection.AddSingleton<PlanApiController>();
serviceCollection.AddSingleton<PlanClientMessenger>();
return serviceCollection; return serviceCollection;
} }
public static ServiceProvider UsePlan(this ServiceProvider serviceProvider) public static ServiceProvider UsePlanClient(this ServiceProvider serviceProvider)
{ {
PlanTransfer planTransfer = serviceProvider.GetService<PlanTransfer>(); PlanTransfer planTransfer = serviceProvider.GetService<PlanTransfer>();
MessengerResolver messengerResolver = serviceProvider.GetService<MessengerResolver>();
messengerResolver.AddMessenger(new List<IMessenger> { serviceProvider.GetService<PlanClientMessenger>() });
IApiServer apiServer = serviceProvider.GetService<IApiServer>();
apiServer.AddPlugins(new List<libs.api.IApiController> { serviceProvider.GetService<PlanApiController>() });
return serviceProvider;
}
public static ServiceCollection AddPlanServer(this ServiceCollection serviceCollection)
{
serviceCollection.AddSingleton<PlanServerMessenger>();
return serviceCollection;
}
public static ServiceProvider UsePlanServer(this ServiceProvider serviceProvider)
{
MessengerResolver messengerResolver = serviceProvider.GetService<MessengerResolver>();
messengerResolver.AddMessenger(new List<IMessenger> { serviceProvider.GetService<PlanClientMessenger>() });
return serviceProvider; return serviceProvider;
} }

View File

@@ -2,10 +2,10 @@
{ {
public interface IPlanStore public interface IPlanStore
{ {
public bool Add(PlanStoreInfo info); public bool Add(PlanInfo info);
public IEnumerable<PlanStoreInfo> Get(); public IEnumerable<PlanInfo> Get();
public IEnumerable<PlanStoreInfo> Get(string category); public IEnumerable<PlanInfo> Get(string category);
public PlanStoreInfo Get(string category, string key); public PlanInfo Get(string category, string key);
public bool Remove(int id); public bool Remove(int id);
} }
@@ -24,7 +24,7 @@
public Task HandleAsync(string handle, string key, string value); public Task HandleAsync(string handle, string key, string value);
} }
public sealed class PlanStoreInfo public sealed class PlanInfo
{ {
public int Id { get; set; } public int Id { get; set; }
@@ -39,6 +39,22 @@
public PlanMethod Method { get; set; } public PlanMethod Method { get; set; }
public string Rule { get; set; } public string Rule { get; set; }
} }
public sealed class PlanGetInfo
{
public string MachineId { get; set; }
public string Category { get; set; }
public string Key { get; set; }
}
public sealed class PlanAddInfo
{
public string MachineId { get; set; }
public PlanInfo Plan { get; set; }
}
public sealed class PlanRemoveInfo
{
public string MachineId { get; set; }
public int PlanId { get; set; }
}
/// <summary> /// <summary>
/// 计划任务方法 /// 计划任务方法

View File

@@ -0,0 +1,79 @@
using linker.libs;
using linker.libs.api;
using linker.libs.extends;
using linker.messenger.signin;
namespace linker.messenger.plan
{
/// <summary>
/// 中继管理接口
/// </summary>
public sealed class PlanApiController : IApiController
{
private readonly SignInClientState signInClientState;
private readonly IMessengerSender messengerSender;
private readonly ISerializer serializer;
private readonly ISignInClientStore signInClientStore;
private readonly PlanTransfer planTransfer;
public PlanApiController( SignInClientState signInClientState, IMessengerSender messengerSender, ISerializer serializer, ISignInClientStore signInClientStore, PlanTransfer planTransfer)
{
this.signInClientState = signInClientState;
this.messengerSender = messengerSender;
this.serializer = serializer;
this.signInClientStore = signInClientStore;
this.planTransfer = planTransfer;
}
public async Task<List<PlanInfo>> Get(ApiControllerParamsInfo param)
{
PlanGetInfo info = param.Content.DeJson<PlanGetInfo>();
if (info.MachineId == signInClientStore.Id)
{
return planTransfer.Get(info.Category).ToList();
}
MessageResponeInfo resp = await messengerSender.SendReply(new MessageRequestWrap
{
Connection = signInClientState.Connection,
MessengerId = (ushort)PlanMessengerIds.GetForward,
Payload = serializer.Serialize(info)
}).ConfigureAwait(false);
if(resp.Code == MessageResponeCodes.OK)
{
return serializer.Deserialize<List<PlanInfo>>(resp.Data.Span);
}
return new List<PlanInfo>();
}
public async Task<bool> Add(ApiControllerParamsInfo param)
{
PlanAddInfo info = param.Content.DeJson<PlanAddInfo>();
if (info.MachineId == signInClientStore.Id)
{
return planTransfer.Add(info.Plan);
}
MessageResponeInfo resp = await messengerSender.SendReply(new MessageRequestWrap
{
Connection = signInClientState.Connection,
MessengerId = (ushort)PlanMessengerIds.AddForward,
Payload = serializer.Serialize(info)
}).ConfigureAwait(false);
return resp.Code == MessageResponeCodes.OK && resp.Data.Span.SequenceEqual(Helper.TrueArray);
}
public async Task<bool> Remove(ApiControllerParamsInfo param)
{
PlanRemoveInfo info = param.Content.DeJson<PlanRemoveInfo>();
if (info.MachineId == signInClientStore.Id)
{
return planTransfer.Remove(info.PlanId);
}
MessageResponeInfo resp = await messengerSender.SendReply(new MessageRequestWrap
{
Connection = signInClientState.Connection,
MessengerId = (ushort)PlanMessengerIds.RemoveForward,
Payload = serializer.Serialize(info)
}).ConfigureAwait(false);
return resp.Code == MessageResponeCodes.OK && resp.Data.Span.SequenceEqual(Helper.TrueArray);
}
}
}

View File

@@ -0,0 +1,151 @@
using linker.libs;
using linker.messenger.signin;
namespace linker.messenger.plan
{
/// <summary>
/// 计划任务客户端
/// </summary>
public class PlanClientMessenger : IMessenger
{
private readonly PlanTransfer planTransfer;
private readonly ISerializer serializer;
public PlanClientMessenger(PlanTransfer planTransfer, ISerializer serializer)
{
this.planTransfer = planTransfer;
this.serializer = serializer;
}
/// <summary>
/// 获取
/// </summary>
/// <param name="connection"></param>
[MessengerId((ushort)PlanMessengerIds.Get)]
public void Get(IConnection connection)
{
PlanGetInfo info = serializer.Deserialize<PlanGetInfo>(connection.ReceiveRequestWrap.Payload.Span);
List<PlanInfo> result = planTransfer.Get(info.Category).ToList();
connection.Write(serializer.Serialize(result));
}
/// <summary>
/// 添加
/// </summary>
/// <param name="connection"></param>
[MessengerId((ushort)PlanMessengerIds.Add)]
public void AddClient(IConnection connection)
{
PlanAddInfo info = serializer.Deserialize<PlanAddInfo>(connection.ReceiveRequestWrap.Payload.Span);
planTransfer.Add(info.Plan);
connection.Write(Helper.TrueArray);
}
// <summary>
/// 删除
/// </summary>
/// <param name="connection"></param>
[MessengerId((ushort)PlanMessengerIds.Remove)]
public void RemoveClient(IConnection connection)
{
PlanRemoveInfo info = serializer.Deserialize<PlanRemoveInfo>(connection.ReceiveRequestWrap.Payload.Span);
planTransfer.Remove(info.PlanId);
connection.Write(Helper.TrueArray);
}
}
/// <summary>
/// 计划任务服务端
/// </summary>
public class PlanServerMessenger : IMessenger
{
private readonly IMessengerSender messengerSender;
private readonly SignInServerCaching signCaching;
private readonly ISerializer serializer;
public PlanServerMessenger(IMessengerSender messengerSender, SignInServerCaching signCaching, ISerializer serializer)
{
this.messengerSender = messengerSender;
this.signCaching = signCaching;
this.serializer = serializer;
}
[MessengerId((ushort)PlanMessengerIds.GetForward)]
public async Task GetForward(IConnection connection)
{
PlanGetInfo info = serializer.Deserialize<PlanGetInfo>(connection.ReceiveRequestWrap.Payload.Span);
if (signCaching.TryGet(info.MachineId, out SignCacheInfo cacheTo) && signCaching.TryGet(connection.Id, out SignCacheInfo cacheFrom) && cacheFrom.GroupId == cacheTo.GroupId)
{
uint requestid = connection.ReceiveRequestWrap.RequestId;
await messengerSender.SendReply(new MessageRequestWrap
{
Connection = cacheTo.Connection,
MessengerId = (ushort)PlanMessengerIds.Get,
Payload = connection.ReceiveRequestWrap.Payload
}).ContinueWith(async (result) =>
{
if (result.Result.Code == MessageResponeCodes.OK)
{
await messengerSender.ReplyOnly(new MessageResponseWrap
{
Connection = connection,
Code = MessageResponeCodes.OK,
Payload = result.Result.Data,
RequestId = requestid
}, (ushort)PlanMessengerIds.GetForward).ConfigureAwait(false);
}
}).ConfigureAwait(false);
}
}
[MessengerId((ushort)PlanMessengerIds.AddForward)]
public async Task AddForward(IConnection connection)
{
PlanAddInfo info = serializer.Deserialize<PlanAddInfo>(connection.ReceiveRequestWrap.Payload.Span);
if (signCaching.TryGet(info.MachineId, out SignCacheInfo cacheTo) && signCaching.TryGet(connection.Id, out SignCacheInfo cacheFrom) && cacheFrom.GroupId == cacheTo.GroupId)
{
uint requestid = connection.ReceiveRequestWrap.RequestId;
await messengerSender.SendReply(new MessageRequestWrap
{
Connection = cacheTo.Connection,
MessengerId = (ushort)PlanMessengerIds.Add,
Payload = connection.ReceiveRequestWrap.Payload
}).ContinueWith(async (result) =>
{
if (result.Result.Code == MessageResponeCodes.OK)
{
await messengerSender.ReplyOnly(new MessageResponseWrap
{
Connection = connection,
Code = MessageResponeCodes.OK,
Payload = result.Result.Data,
RequestId = requestid
}, (ushort)PlanMessengerIds.AddForward).ConfigureAwait(false);
}
}).ConfigureAwait(false);
}
}
[MessengerId((ushort)PlanMessengerIds.RemoveForward)]
public async Task RemoveForward(IConnection connection)
{
PlanRemoveInfo info = serializer.Deserialize<PlanRemoveInfo>(connection.ReceiveRequestWrap.Payload.Span);
if (signCaching.TryGet(info.MachineId, out SignCacheInfo cacheTo) && signCaching.TryGet(connection.Id, out SignCacheInfo cacheFrom) && cacheFrom.GroupId == cacheTo.GroupId)
{
uint requestid = connection.ReceiveRequestWrap.RequestId;
await messengerSender.SendReply(new MessageRequestWrap
{
Connection = cacheTo.Connection,
MessengerId = (ushort)PlanMessengerIds.Remove,
Payload = connection.ReceiveRequestWrap.Payload
}).ContinueWith(async (result) =>
{
if (result.Result.Code == MessageResponeCodes.OK)
{
await messengerSender.ReplyOnly(new MessageResponseWrap
{
Connection = connection,
Code = MessageResponeCodes.OK,
Payload = result.Result.Data,
RequestId = requestid
}, (ushort)PlanMessengerIds.RemoveForward).ConfigureAwait(false);
}
}).ConfigureAwait(false);
}
}
}
}

View File

@@ -0,0 +1,18 @@
namespace linker.messenger.plan
{
public enum PlanMessengerIds : ushort
{
Min = 3000,
Add = 3001,
AddForward = 3002,
Remove = 3003,
RemoveForward = 3004,
Get = 3005,
GetForward = 3006,
Max = 3099
}
}

View File

@@ -27,16 +27,16 @@ namespace linker.messenger.plan
handles.AddOrUpdate(handle.CategoryName, handle, (a, b) => handle); handles.AddOrUpdate(handle.CategoryName, handle, (a, b) => handle);
} }
public IEnumerable<PlanStoreInfo> Get(string category) public IEnumerable<PlanInfo> Get(string category)
{ {
return planStore.Get(category); return planStore.Get(category);
} }
public bool Add(PlanStoreInfo info) public bool Add(PlanInfo info)
{ {
bool result = planStore.Add(info); bool result = planStore.Add(info);
caches.TryRemove(info.Id, out _); caches.TryRemove(info.Id, out _);
PlanExecCacheInfo cache = new PlanExecCacheInfo { Store = info }; PlanExecCacheInfo cache = new PlanExecCacheInfo { Plan = info };
cache.Active = UpdateNextTime(cache) && string.IsNullOrWhiteSpace(info.TriggerHandle); cache.Active = UpdateNextTime(cache) && string.IsNullOrWhiteSpace(info.TriggerHandle);
caches.TryAdd(info.Id, cache); caches.TryAdd(info.Id, cache);
return result; return result;
@@ -56,12 +56,12 @@ namespace linker.messenger.plan
} }
private void Load() private void Load()
{ {
foreach (PlanStoreInfo info in planStore.Get()) foreach (PlanInfo info in planStore.Get())
{ {
try try
{ {
PlanExecCacheInfo cache = new PlanExecCacheInfo { Store = info }; PlanExecCacheInfo cache = new PlanExecCacheInfo { Plan = info };
cache.Active = (cache.Store.Method < PlanMethod.At) || (UpdateNextTime(cache) && cache.Store.Method != PlanMethod.Trigger); cache.Active = (cache.Plan.Method < PlanMethod.At) || (UpdateNextTime(cache) && cache.Plan.Method != PlanMethod.Trigger);
caches.TryAdd(info.Id, cache); caches.TryAdd(info.Id, cache);
} }
catch (Exception) catch (Exception)
@@ -71,33 +71,33 @@ namespace linker.messenger.plan
} }
private void RunSetup() private void RunSetup()
{ {
foreach (PlanExecCacheInfo item in caches.Values.Where(c => c.Store.Method == PlanMethod.Setup && c.Store.Disabled == false && c.Running == false)) foreach (PlanExecCacheInfo item in caches.Values.Where(c => c.Plan.Method == PlanMethod.Setup && c.Plan.Disabled == false && c.Running == false))
{ {
Run(item); Run(item);
} }
} }
private void RunLoop() private void RunLoop()
{ {
foreach (PlanExecCacheInfo item in caches.Values.Where(c => c.NextTime <= DateTime.Now && c.Store.Method >= PlanMethod.At)) foreach (PlanExecCacheInfo item in caches.Values.Where(c => c.NextTime <= DateTime.Now && c.Plan.Method >= PlanMethod.At))
{ {
Run(item); Run(item);
} }
} }
private void Run(PlanExecCacheInfo item) private void Run(PlanExecCacheInfo item)
{ {
if(item.Store.Disabled || item.Active == false || item.Running || handles.TryGetValue(item.Store.Category, out IPlanHandle handle) == false) if(item.Plan.Disabled || item.Active == false || item.Running || handles.TryGetValue(item.Plan.Category, out IPlanHandle handle) == false)
{ {
return; return;
} }
item.Running = true; item.Running = true;
item.Active = (item.Store.Method < PlanMethod.At) || (UpdateNextTime(item) && item.Store.Method != PlanMethod.Trigger); item.Active = (item.Plan.Method < PlanMethod.At) || (UpdateNextTime(item) && item.Plan.Method != PlanMethod.Trigger);
item.LastTime = DateTime.Now; item.LastTime = DateTime.Now;
handle.HandleAsync(item.Store.Handle, item.Store.Key, item.Store.Value).ContinueWith((result) => handle.HandleAsync(item.Plan.Handle, item.Plan.Key, item.Plan.Value).ContinueWith((result) =>
{ {
item.Running = false; item.Running = false;
PlanExecCacheInfo trigger = caches.Values.FirstOrDefault(c => c.Store.Category == item.Store.Category && c.Store.Key == item.Store.Key && c.Store.TriggerHandle == item.Store.Handle && c.Store.TriggerHandle != c.Store.Handle && c.Store.Method == PlanMethod.Trigger); PlanExecCacheInfo trigger = caches.Values.FirstOrDefault(c => c.Plan.Category == item.Plan.Category && c.Plan.Key == item.Plan.Key && c.Plan.TriggerHandle == item.Plan.Handle && c.Plan.TriggerHandle != c.Plan.Handle && c.Plan.Method == PlanMethod.Trigger);
if (trigger != null) if (trigger != null)
{ {
trigger.Active = UpdateNextTime(trigger); trigger.Active = UpdateNextTime(trigger);
@@ -109,19 +109,19 @@ namespace linker.messenger.plan
{ {
try try
{ {
if (cache.Store.Method == PlanMethod.At) if (cache.Plan.Method == PlanMethod.At)
{ {
return NextTimeAt(cache); return NextTimeAt(cache);
} }
else if (cache.Store.Method == PlanMethod.Timer) else if (cache.Plan.Method == PlanMethod.Timer)
{ {
return NextTimeTimer(cache); return NextTimeTimer(cache);
} }
else if (cache.Store.Method == PlanMethod.Cron) else if (cache.Plan.Method == PlanMethod.Cron)
{ {
return NextTimeCorn(cache); return NextTimeCorn(cache);
} }
else if (cache.Store.Method == PlanMethod.Trigger) else if (cache.Plan.Method == PlanMethod.Trigger)
{ {
return NextTimeAfter(cache); return NextTimeAfter(cache);
} }
@@ -136,7 +136,7 @@ namespace linker.messenger.plan
{ {
try try
{ {
CronExpression cron = CronExpression.Parse(cache.Store.Rule, CronFormat.IncludeSeconds); CronExpression cron = CronExpression.Parse(cache.Plan.Rule, CronFormat.IncludeSeconds);
DateTimeOffset? nextOccurrence = cron.GetNextOccurrence(DateTimeOffset.Now, TimeZoneInfo.Local); DateTimeOffset? nextOccurrence = cron.GetNextOccurrence(DateTimeOffset.Now, TimeZoneInfo.Local);
if (nextOccurrence.HasValue) if (nextOccurrence.HasValue)
{ {
@@ -153,15 +153,15 @@ namespace linker.messenger.plan
} }
private bool NextTimeAt(PlanExecCacheInfo cache) private bool NextTimeAt(PlanExecCacheInfo cache)
{ {
if (Regex.IsMatch(cache.Store.Rule, regex) == false) if (Regex.IsMatch(cache.Plan.Rule, regex) == false)
{ {
cache.Error = $"{cache.Store.Rule} format error"; cache.Error = $"{cache.Plan.Rule} format error";
return false; return false;
} }
DateTime from = DateTime.Now; DateTime from = DateTime.Now;
GroupCollection groups = Regex.Match(cache.Store.Rule, regex).Groups; GroupCollection groups = Regex.Match(cache.Plan.Rule, regex).Groups;
int year = groups[1].Value == "?" ? from.Year : int.Parse(groups[1].Value); int year = groups[1].Value == "?" ? from.Year : int.Parse(groups[1].Value);
int month = groups[2].Value == "?" ? from.Month : int.Parse(groups[2].Value); int month = groups[2].Value == "?" ? from.Month : int.Parse(groups[2].Value);
int day = groups[3].Value == "?" ? from.Day : int.Parse(groups[3].Value); int day = groups[3].Value == "?" ? from.Day : int.Parse(groups[3].Value);
@@ -183,13 +183,13 @@ namespace linker.messenger.plan
} }
private bool NextTimeTimer(PlanExecCacheInfo cache) private bool NextTimeTimer(PlanExecCacheInfo cache)
{ {
if (Regex.IsMatch(cache.Store.Rule, regex) == false) if (Regex.IsMatch(cache.Plan.Rule, regex) == false)
{ {
cache.Error = $"{cache.Store.Rule} format error"; cache.Error = $"{cache.Plan.Rule} format error";
return false; return false;
} }
GroupCollection groups = Regex.Match(cache.Store.Rule, regex).Groups; GroupCollection groups = Regex.Match(cache.Plan.Rule, regex).Groups;
int year = groups[1].Value == "?" ? 0 : int.Parse(groups[1].Value); int year = groups[1].Value == "?" ? 0 : int.Parse(groups[1].Value);
int month = groups[2].Value == "?" ? 0 : int.Parse(groups[2].Value); int month = groups[2].Value == "?" ? 0 : int.Parse(groups[2].Value);
int day = groups[3].Value == "?" ? 0 : int.Parse(groups[3].Value); int day = groups[3].Value == "?" ? 0 : int.Parse(groups[3].Value);
@@ -202,13 +202,13 @@ namespace linker.messenger.plan
} }
private bool NextTimeAfter(PlanExecCacheInfo cache) private bool NextTimeAfter(PlanExecCacheInfo cache)
{ {
if (Regex.IsMatch(cache.Store.Rule, regex) == false) if (Regex.IsMatch(cache.Plan.Rule, regex) == false)
{ {
cache.Error = $"{cache.Store.Rule} format error"; cache.Error = $"{cache.Plan.Rule} format error";
return false; return false;
} }
GroupCollection groups = Regex.Match(cache.Store.Rule, regex).Groups; GroupCollection groups = Regex.Match(cache.Plan.Rule, regex).Groups;
int year = groups[1].Value == "?" ? 0 : int.Parse(groups[1].Value); int year = groups[1].Value == "?" ? 0 : int.Parse(groups[1].Value);
int month = groups[2].Value == "?" ? 0 : int.Parse(groups[2].Value); int month = groups[2].Value == "?" ? 0 : int.Parse(groups[2].Value);
int day = groups[3].Value == "?" ? 0 : int.Parse(groups[3].Value); int day = groups[3].Value == "?" ? 0 : int.Parse(groups[3].Value);
@@ -224,7 +224,7 @@ namespace linker.messenger.plan
public sealed class PlanExecCacheInfo public sealed class PlanExecCacheInfo
{ {
public PlanStoreInfo Store { get; set; } public PlanInfo Plan { get; set; }
public DateTime LastTime { get; set; } public DateTime LastTime { get; set; }
public DateTime NextTime { get; set; } public DateTime NextTime { get; set; }

View File

@@ -127,6 +127,12 @@ namespace linker.messenger.serializer.memorypack
MemoryPackFormatterProvider.Register(new TuntapLanInfoFormatter()); MemoryPackFormatterProvider.Register(new TuntapLanInfoFormatter());
MemoryPackFormatterProvider.Register(new LeaseInfoFormatter()); MemoryPackFormatterProvider.Register(new LeaseInfoFormatter());
MemoryPackFormatterProvider.Register(new PlanInfoFormatter());
MemoryPackFormatterProvider.Register(new PlanGetInfoFormatter());
MemoryPackFormatterProvider.Register(new PlanAddInfoFormatter());
MemoryPackFormatterProvider.Register(new PlanRemoveInfoFormatter());
return serviceCollection; return serviceCollection;
} }
public static ServiceProvider UseSerializerMemoryPack(this ServiceProvider serviceProvider) public static ServiceProvider UseSerializerMemoryPack(this ServiceProvider serviceProvider)

View File

@@ -0,0 +1,251 @@
using linker.messenger.plan;
using MemoryPack;
namespace linker.messenger.serializer.memorypack
{
[MemoryPackable]
public readonly partial struct SerializablePlanInfo
{
[MemoryPackIgnore]
public readonly PlanInfo info;
[MemoryPackInclude]
int Id => info.Id;
[MemoryPackInclude]
string Category => info.Category;
[MemoryPackInclude]
string Key => info.Key;
[MemoryPackInclude]
string Handle => info.Handle;
[MemoryPackInclude]
string Value => info.Value;
[MemoryPackInclude]
bool Disabled => info.Disabled;
[MemoryPackInclude]
string TriggerHandle => info.TriggerHandle;
[MemoryPackInclude]
PlanMethod Method => info.Method;
[MemoryPackInclude]
string Rule => info.Rule;
[MemoryPackConstructor]
SerializablePlanInfo(int id, string category, string key, string handle, string value, bool disabled, string triggerHandle, PlanMethod method, string rule)
{
var info = new PlanInfo
{
Id = id,
Category = category,
Key = key,
Handle = handle,
Value = value,
Disabled = disabled,
TriggerHandle = triggerHandle,
Method = method,
Rule = rule
};
this.info = info;
}
public SerializablePlanInfo(PlanInfo info)
{
this.info = info;
}
}
public class PlanInfoFormatter : MemoryPackFormatter<PlanInfo>
{
public override void Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter> writer, scoped ref PlanInfo value)
{
if (value == null)
{
writer.WriteNullObjectHeader();
return;
}
writer.WritePackable(new SerializablePlanInfo(value));
}
public override void Deserialize(ref MemoryPackReader reader, scoped ref PlanInfo value)
{
if (reader.PeekIsNull())
{
reader.Advance(1); // skip null block
value = null;
return;
}
var wrapped = reader.ReadPackable<SerializablePlanInfo>();
value = wrapped.info;
}
}
[MemoryPackable]
public readonly partial struct SerializablePlanGetInfo
{
[MemoryPackIgnore]
public readonly PlanGetInfo info;
[MemoryPackInclude]
string MachineId => info.MachineId;
[MemoryPackInclude]
string Category => info.Category;
[MemoryPackInclude]
string Key => info.Key;
[MemoryPackConstructor]
SerializablePlanGetInfo(string machineId, string category, string key)
{
var info = new PlanGetInfo
{
MachineId = machineId,
Category = category,
Key = key
};
this.info = info;
}
public SerializablePlanGetInfo(PlanGetInfo info)
{
this.info = info;
}
}
public class PlanGetInfoFormatter : MemoryPackFormatter<PlanGetInfo>
{
public override void Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter> writer, scoped ref PlanGetInfo value)
{
if (value == null)
{
writer.WriteNullObjectHeader();
return;
}
writer.WritePackable(new SerializablePlanGetInfo(value));
}
public override void Deserialize(ref MemoryPackReader reader, scoped ref PlanGetInfo value)
{
if (reader.PeekIsNull())
{
reader.Advance(1); // skip null block
value = null;
return;
}
var wrapped = reader.ReadPackable<SerializablePlanGetInfo>();
value = wrapped.info;
}
}
[MemoryPackable]
public readonly partial struct SerializablePlanAddInfo
{
[MemoryPackIgnore]
public readonly PlanAddInfo info;
[MemoryPackInclude]
string MachineId => info.MachineId;
[MemoryPackInclude, MemoryPackAllowSerialize]
PlanInfo Plan => info.Plan;
[MemoryPackConstructor]
SerializablePlanAddInfo(string machineId, PlanInfo plan)
{
var info = new PlanAddInfo
{
MachineId = machineId,
Plan = plan
};
this.info = info;
}
public SerializablePlanAddInfo(PlanAddInfo info)
{
this.info = info;
}
}
public class PlanAddInfoFormatter : MemoryPackFormatter<PlanAddInfo>
{
public override void Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter> writer, scoped ref PlanAddInfo value)
{
if (value == null)
{
writer.WriteNullObjectHeader();
return;
}
writer.WritePackable(new SerializablePlanAddInfo(value));
}
public override void Deserialize(ref MemoryPackReader reader, scoped ref PlanAddInfo value)
{
if (reader.PeekIsNull())
{
reader.Advance(1); // skip null block
value = null;
return;
}
var wrapped = reader.ReadPackable<SerializablePlanAddInfo>();
value = wrapped.info;
}
}
[MemoryPackable]
public readonly partial struct SerializablePlanRemoveInfo
{
[MemoryPackIgnore]
public readonly PlanRemoveInfo info;
[MemoryPackInclude]
string MachineId => info.MachineId;
[MemoryPackInclude, MemoryPackAllowSerialize]
int PlanId => info.PlanId;
[MemoryPackConstructor]
SerializablePlanRemoveInfo(string machineId, int planid)
{
var info = new PlanRemoveInfo
{
MachineId = machineId,
PlanId = planid
};
this.info = info;
}
public SerializablePlanRemoveInfo(PlanRemoveInfo info)
{
this.info = info;
}
}
public class PlanRemoveInfoFormatter : MemoryPackFormatter<PlanRemoveInfo>
{
public override void Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter> writer, scoped ref PlanRemoveInfo value)
{
if (value == null)
{
writer.WriteNullObjectHeader();
return;
}
writer.WritePackable(new SerializablePlanRemoveInfo(value));
}
public override void Deserialize(ref MemoryPackReader reader, scoped ref PlanRemoveInfo value)
{
if (reader.PeekIsNull())
{
reader.Advance(1); // skip null block
value = null;
return;
}
var wrapped = reader.ReadPackable<SerializablePlanRemoveInfo>();
value = wrapped.info;
}
}
}

View File

@@ -115,12 +115,13 @@ namespace linker.messenger.sforward.client
return forwardTransfer.Add(info.Data); return forwardTransfer.Add(info.Data);
} }
if (accessStore.HasAccess(AccessValue.ForwardOther) == false) return false; if (accessStore.HasAccess(AccessValue.ForwardOther) == false) return false;
return await messengerSender.SendOnly(new MessageRequestWrap MessageResponeInfo resp = await messengerSender.SendReply(new MessageRequestWrap
{ {
Connection = signInClientState.Connection, Connection = signInClientState.Connection,
MessengerId = (ushort)SForwardMessengerIds.AddClientForward, MessengerId = (ushort)SForwardMessengerIds.AddClientForward,
Payload = serializer.Serialize(info) Payload = serializer.Serialize(info)
}).ConfigureAwait(false); }).ConfigureAwait(false);
return resp.Code == MessageResponeCodes.OK && resp.Data.Span.SequenceEqual(Helper.TrueArray);
} }
/// <summary> /// <summary>
@@ -137,12 +138,13 @@ namespace linker.messenger.sforward.client
return forwardTransfer.Remove(info.Id); return forwardTransfer.Remove(info.Id);
} }
if (accessStore.HasAccess(AccessValue.ForwardOther) == false) return false; if (accessStore.HasAccess(AccessValue.ForwardOther) == false) return false;
return await messengerSender.SendOnly(new MessageRequestWrap MessageResponeInfo resp = await messengerSender.SendReply(new MessageRequestWrap
{ {
Connection = signInClientState.Connection, Connection = signInClientState.Connection,
MessengerId = (ushort)SForwardMessengerIds.RemoveClientForward, MessengerId = (ushort)SForwardMessengerIds.RemoveClientForward,
Payload = serializer.Serialize(info) Payload = serializer.Serialize(info)
}).ConfigureAwait(false); }).ConfigureAwait(false);
return resp.Code == MessageResponeCodes.OK && resp.Data.Span.SequenceEqual(Helper.TrueArray);
} }
/// <summary> /// <summary>

View File

@@ -30,6 +30,22 @@ namespace linker.messenger.sforward.client
this.serializer = serializer; this.serializer = serializer;
} }
public void Start(int id)
{
SForwardInfo forwardInfo = sForwardClientStore.Get(id);
if(forwardInfo != null)
{
Start(forwardInfo);
}
}
public void Stop(int id)
{
SForwardInfo forwardInfo = sForwardClientStore.Get(id);
if (forwardInfo != null)
{
Stop(forwardInfo);
}
}
private void Start() private void Start()
{ {
foreach (var item in sForwardClientStore.Get()) foreach (var item in sForwardClientStore.Get())
@@ -70,7 +86,7 @@ namespace linker.messenger.sforward.client
forwardInfo.BufferSize = sForwardAddResultInfo.BufferSize; forwardInfo.BufferSize = sForwardAddResultInfo.BufferSize;
if (sForwardAddResultInfo.Success) if (sForwardAddResultInfo.Success)
{ {
sForwardClientStore.Update(forwardInfo.Id, forwardInfo.Started,true, string.Empty); sForwardClientStore.Update(forwardInfo.Id, forwardInfo.Started, true, string.Empty);
LoggerHelper.Instance.Debug(sForwardAddResultInfo.Message); LoggerHelper.Instance.Debug(sForwardAddResultInfo.Message);
OnOpen(forwardInfo.Id); OnOpen(forwardInfo.Id);
} }
@@ -117,7 +133,7 @@ namespace linker.messenger.sforward.client
} }
else else
{ {
sForwardClientStore.Update(forwardInfo.Id, true, forwardInfo.Proxy, string.Empty); sForwardClientStore.Update(forwardInfo.Id, forwardInfo.Started, forwardInfo.Proxy, string.Empty);
LoggerHelper.Instance.Error(sForwardAddResultInfo.Message); LoggerHelper.Instance.Error(sForwardAddResultInfo.Message);
} }
} }

View File

@@ -7,12 +7,27 @@ namespace linker.messenger.sforward.client
public string CategoryName => "sforward"; public string CategoryName => "sforward";
public SForwardPlanHandle() private readonly SForwardClientTransfer sForwardClientTransfer;
public SForwardPlanHandle(SForwardClientTransfer sForwardClientTransfer)
{ {
this.sForwardClientTransfer = sForwardClientTransfer;
} }
public async Task HandleAsync(string handle, string key, string value) public async Task HandleAsync(string handle, string key, string value)
{ {
Console.WriteLine($"[{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}] exec plan {CategoryName} {handle} {key}->{value}"); if (int.TryParse(key, out int id) == false) return;
switch (handle)
{
case "start":
sForwardClientTransfer.Start(id);
break;
case "stop":
sForwardClientTransfer.Stop(id);
break;
default:
break;
}
await Task.CompletedTask; await Task.CompletedTask;
} }
} }

View File

@@ -237,7 +237,7 @@ namespace linker.plugins.sforward.messenger
RequestId = requestid RequestId = requestid
}, (ushort)SForwardMessengerIds.GetForward).ConfigureAwait(false); }, (ushort)SForwardMessengerIds.GetForward).ConfigureAwait(false);
} }
}); }).ConfigureAwait(false);
} }
} }
/// <summary> /// <summary>
@@ -251,11 +251,23 @@ namespace linker.plugins.sforward.messenger
if (signCaching.TryGet(info.MachineId, out SignCacheInfo cacheTo) && signCaching.TryGet(connection.Id, out SignCacheInfo cacheFrom) && cacheFrom.GroupId == cacheTo.GroupId) if (signCaching.TryGet(info.MachineId, out SignCacheInfo cacheTo) && signCaching.TryGet(connection.Id, out SignCacheInfo cacheFrom) && cacheFrom.GroupId == cacheTo.GroupId)
{ {
uint requestid = connection.ReceiveRequestWrap.RequestId; uint requestid = connection.ReceiveRequestWrap.RequestId;
await sender.SendOnly(new MessageRequestWrap await sender.SendReply(new MessageRequestWrap
{ {
Connection = cacheTo.Connection, Connection = cacheTo.Connection,
MessengerId = (ushort)SForwardMessengerIds.AddClient, MessengerId = (ushort)SForwardMessengerIds.AddClient,
Payload = serializer.Serialize(info.Data) Payload = serializer.Serialize(info.Data)
}).ContinueWith(async (result) =>
{
if (result.Result.Code == MessageResponeCodes.OK)
{
await sender.ReplyOnly(new MessageResponseWrap
{
Connection = connection,
Code = MessageResponeCodes.OK,
Payload = result.Result.Data,
RequestId = requestid
}, (ushort)SForwardMessengerIds.AddClientForward).ConfigureAwait(false);
}
}).ConfigureAwait(false); }).ConfigureAwait(false);
} }
} }
@@ -270,11 +282,23 @@ namespace linker.plugins.sforward.messenger
if (signCaching.TryGet(info.MachineId, out SignCacheInfo cacheTo) && signCaching.TryGet(connection.Id, out SignCacheInfo cacheFrom) && cacheFrom.GroupId == cacheTo.GroupId) if (signCaching.TryGet(info.MachineId, out SignCacheInfo cacheTo) && signCaching.TryGet(connection.Id, out SignCacheInfo cacheFrom) && cacheFrom.GroupId == cacheTo.GroupId)
{ {
uint requestid = connection.ReceiveRequestWrap.RequestId; uint requestid = connection.ReceiveRequestWrap.RequestId;
await sender.SendOnly(new MessageRequestWrap await sender.SendReply(new MessageRequestWrap
{ {
Connection = cacheTo.Connection, Connection = cacheTo.Connection,
MessengerId = (ushort)SForwardMessengerIds.RemoveClient, MessengerId = (ushort)SForwardMessengerIds.RemoveClient,
Payload = serializer.Serialize(info.Id) Payload = serializer.Serialize(info.Id)
}).ContinueWith(async (result) =>
{
if (result.Result.Code == MessageResponeCodes.OK)
{
await sender.ReplyOnly(new MessageResponseWrap
{
Connection = connection,
Code = MessageResponeCodes.OK,
Payload = result.Result.Data,
RequestId = requestid
}, (ushort)SForwardMessengerIds.RemoveClientForward).ConfigureAwait(false);
}
}).ConfigureAwait(false); }).ConfigureAwait(false);
} }
} }
@@ -404,6 +428,7 @@ namespace linker.plugins.sforward.messenger
{ {
SForwardInfo sForwardInfo = serializer.Deserialize<SForwardInfo>(connection.ReceiveRequestWrap.Payload.Span); SForwardInfo sForwardInfo = serializer.Deserialize<SForwardInfo>(connection.ReceiveRequestWrap.Payload.Span);
sForwardTransfer.Add(sForwardInfo); sForwardTransfer.Add(sForwardInfo);
connection.Write(Helper.TrueArray);
} }
// <summary> // <summary>
/// 删除 /// 删除
@@ -414,6 +439,7 @@ namespace linker.plugins.sforward.messenger
{ {
int id = serializer.Deserialize<int>(connection.ReceiveRequestWrap.Payload.Span); int id = serializer.Deserialize<int>(connection.ReceiveRequestWrap.Payload.Span);
sForwardTransfer.Remove(id); sForwardTransfer.Remove(id);
connection.Write(Helper.TrueArray);
} }
// <summary> // <summary>
/// 测试 /// 测试

View File

@@ -6,13 +6,13 @@ namespace linker.messenger.store.file.plan
public sealed class PlanStore : IPlanStore public sealed class PlanStore : IPlanStore
{ {
private readonly Storefactory dBfactory; private readonly Storefactory dBfactory;
private readonly ILiteCollection<PlanStoreInfo> liteCollection; private readonly ILiteCollection<PlanInfo> liteCollection;
public PlanStore(Storefactory dBfactory) public PlanStore(Storefactory dBfactory)
{ {
this.dBfactory = dBfactory; this.dBfactory = dBfactory;
liteCollection = dBfactory.GetCollection<PlanStoreInfo>("plan"); liteCollection = dBfactory.GetCollection<PlanInfo>("plan");
} }
public bool Add(PlanStoreInfo info) public bool Add(PlanInfo info)
{ {
if (info.Id == 0) if (info.Id == 0)
{ {
@@ -27,16 +27,16 @@ namespace linker.messenger.store.file.plan
return liteCollection.Update(info); return liteCollection.Update(info);
} }
public IEnumerable<PlanStoreInfo> Get() public IEnumerable<PlanInfo> Get()
{ {
return liteCollection.FindAll(); return liteCollection.FindAll();
} }
public IEnumerable<PlanStoreInfo> Get(string category) public IEnumerable<PlanInfo> Get(string category)
{ {
return liteCollection.Find(c => c.Category == category); return liteCollection.Find(c => c.Category == category);
} }
public PlanStoreInfo Get(string category, string key) public PlanInfo Get(string category, string key)
{ {
return liteCollection.FindOne(c => c.Category == category && c.Key == key); return liteCollection.FindOne(c => c.Category == category && c.Key == key);
} }

View File

@@ -2,7 +2,7 @@
<el-dialog v-model="state.show" @open="handleOnShowList" append-to=".app-wrap" :title="`【${machineName}】的内网穿透`" top="1vh" width="700"> <el-dialog v-model="state.show" @open="handleOnShowList" append-to=".app-wrap" :title="`【${machineName}】的内网穿透`" top="1vh" width="700">
<div> <div>
<div class="t-c head"> <div class="t-c head">
<el-button type="success" size="small" @click="handleAdd">添加</el-button> <el-button type="success" size="small" @click="handleAdd" :loading="state.loading">添加</el-button>
<el-button size="small" @click="handleRefresh">刷新</el-button> <el-button size="small" @click="handleRefresh">刷新</el-button>
</div> </div>
<el-table :data="state.data" size="small" border height="500" @cell-dblclick="handleCellClick"> <el-table :data="state.data" size="small" border height="500" @cell-dblclick="handleCellClick">
@@ -100,6 +100,7 @@ export default {
timer:0, timer:0,
timer1:0, timer1:0,
editing:false, editing:false,
loading:false,
}); });
watch(() => state.show, (val) => { watch(() => state.show, (val) => {
if (!val) { if (!val) {
@@ -147,12 +148,15 @@ export default {
ElMessage.success('已刷新') ElMessage.success('已刷新')
} }
const handleAdd = () => { const handleAdd = () => {
state.loading = true;
const row = { Id: 0, Name: '', RemotePort: 0, LocalEP: '127.0.0.1:80',Domain:'',Temp:'' }; const row = { Id: 0, Name: '', RemotePort: 0, LocalEP: '127.0.0.1:80',Domain:'',Temp:'' };
addSForwardInfo({machineid:sforward.value.machineid,data:row}).then(() => { addSForwardInfo({machineid:sforward.value.machineid,data:row}).then(() => {
state.loading = false;
setTimeout(()=>{ setTimeout(()=>{
_getSForwardInfo(); _getSForwardInfo();
},100) },100)
}).catch((err) => { }).catch((err) => {
state.loading = false;
ElMessage.error(err); ElMessage.error(err);
}); });
} }
@@ -181,9 +185,15 @@ export default {
saveRow(row); saveRow(row);
} }
const handleDel = (id) => { const handleDel = (id) => {
removeSForwardInfo({machineid:sforward.value.machineid,id:id}).then(() => { state.loading = true;
removeSForwardInfo({machineid:sforward.value.machineid,id:id})
.then(() => {
state.loading = false;
_getSForwardInfo(); _getSForwardInfo();
}) }).catch((err) => {
state.loading = false;
ElMessage.error(err);
});
} }
const handleStartChange = (row) => { const handleStartChange = (row) => {
saveRow(row); saveRow(row);
@@ -195,13 +205,15 @@ export default {
}else{ }else{
row.Domain = row.Temp; row.Domain = row.Temp;
} }
state.loading = true;
addSForwardInfo({machineid:sforward.value.machineid,data:row}).then((res) => { addSForwardInfo({machineid:sforward.value.machineid,data:row}).then((res) => {
state.loading = false;
if(res == false){ if(res == false){
ElMessage.error('操作失败,可能存在相同值'); ElMessage.error('操作失败,可能存在相同值');
} }
_getSForwardInfo(); _getSForwardInfo();
}).catch((err) => { }).catch((err) => {
state.loading = false;
ElMessage.error(err); ElMessage.error(err);
}); });
} }

View File

@@ -1,5 +1,5 @@
v1.7.1 v1.7.1
2025-03-26 23:10:35 2025-03-27 17:21:16
1. 优化数据同步 1. 优化数据同步
2. 优化linux的tun网卡网卡读写分离提高性能 2. 优化linux的tun网卡网卡读写分离提高性能
3. 优化windows网卡的禁用自动启用 3. 优化windows网卡的禁用自动启用