This commit is contained in:
snltty
2025-03-06 21:15:19 +08:00
parent 303581a8b3
commit c6eeab74b9
86 changed files with 1091 additions and 557 deletions

View File

@@ -35,7 +35,7 @@ jobs:
release_name: v1.6.9.${{ steps.date.outputs.today }}
draft: false
prerelease: false
body: "1. 优化linux下路由跟踪问题\r\n2. 优化linux下获取本机IP问题\r\n3. 增加ICS让win7+、win server2008+支持NAT\r\n4. 增加中继卡密\r\n5. 增加内外穿透定时开关功能\r\n6. 优化管理页面连接接口的体验\r\n7. 优化一些UI体验去除同步页面将同步功能放各个实际的位置\r\n8. 其它一些修复优化"
body: "1. 优化linux下路由跟踪问题\r\n2. 优化linux下获取本机IP问题\r\n3. 增加ICS让win7+、win server2008+支持NAT\r\n4. 增加中继cdkey使用cdkey解锁公开中继节点的带宽、流量、连接数限制\r\n5. 增加内外穿透定时开关功能\r\n6. 优化管理页面连接接口的体验\r\n7. 优化一些UI体验去除同步页面将同步功能放各个实际的位置\r\n8. 优化端口转发,让不同分组间可以使用相同端口\r\n9. 其它一些修复优化"
- name: publish projects
run: ./publish.bat
- name: upload-win-x86-oss

View File

@@ -58,6 +58,7 @@ For user convenience, this project provides **public messenger servers** and **p
- [x] Distributed architecture: Multiple relay nodes for massive scalability
- [x] SOCKS5 proxy: Dynamic port forwarding without specifying ports
- [x] Easy integration: Use `linker.messenger.entry` to embed into your projects
- [x] CDKEYIt can temporarily lift certain restrictions
## Quick Integration
In a .NET 8+ project, install the NuGet package `linker.messenger.entry`

View File

@@ -58,6 +58,7 @@
- [x] 分布式,多中继服务器节点,承载海量设备
- [x] socks5代理端口转发需要指定端口而socks5代理可以代理所有端口
- [x] 集成linker使用`linker.messenger.entry`入口库,轻松集成到你的项目中
- [x] CDKEY可以临时解锁一些限制中继内外穿透什么的
## 轻松集成
在.NET8+项目中nuget 安装 `linker.messenger.entry`

View File

@@ -44,7 +44,7 @@ sidebar_position: 1
- [x] 分布式,多中继服务器节点,承载海量设备
- [x] socks5代理端口转发需要指定端口而socks5代理可以代理所有端口
- [x] 集成linker使用`linker.messenger.entry`入口库,轻松集成到你的项目中
- [x] CDKEY可以临时解锁一些限制中继内外穿透什么的
#### 1.4、加入组织
<a href="https://jq.qq.com/?_wv=1027&k=ucoIVfz4" target="_blank">你可以加入QQ群1121552990</a>

View File

@@ -13,48 +13,87 @@ namespace linker.libs
action();
});
}
public static void SetInterval(Func<bool> action, int delayMs)
{
Task.Run(async () =>
{
while (true)
while (action())
{
if (action() == false)
{
break;
}
await Task.Delay(delayMs).ConfigureAwait(false);
}
});
}
public static void SetIntervalLong(Func<bool> action, int delayMs)
{
Task.Factory.StartNew(async () =>
{
while (action())
{
await Task.Delay(delayMs).ConfigureAwait(false);
}
}, TaskCreationOptions.LongRunning);
}
public static void SetInterval(Func<Task<bool>> action, int delayMs)
{
Task.Run(async () =>
{
while (await action())
{
await Task.Delay(delayMs).ConfigureAwait(false);
}
});
}
public static void SetIntervalLong(Func<Task<bool>> action, int delay)
{
Task.Factory.StartNew(async () =>
{
while (await action())
{
await Task.Delay(delay).ConfigureAwait(false);
}
}, TaskCreationOptions.LongRunning);
}
public static void SetInterval(Func<bool> action, Func<int> delay)
{
Task.Run(async () =>
{
while (true)
while (action())
{
if (action() == false)
{
break;
}
await Task.Delay(delay()).ConfigureAwait(false);
}
});
}
public static void SetIntervalLong(Func<bool> action, Func<int> delay)
{
Task.Factory.StartNew(async () =>
{
while (action())
{
await Task.Delay(delay()).ConfigureAwait(false);
}
}, TaskCreationOptions.LongRunning);
}
public static void SetInterval(Func<Task<bool>> action, Func<int> delay)
{
Task.Run(async () =>
{
while (true)
while (await action())
{
if (await action() == false)
{
break;
}
await Task.Delay(delay()).ConfigureAwait(false);
}
});
}
public static void SetIntervalLong(Func<Task<bool>> action, Func<int> delay)
{
Task.Factory.StartNew(async () =>
{
while (await action())
{
await Task.Delay(delay()).ConfigureAwait(false);
}
}, TaskCreationOptions.LongRunning);
}
public static void Async(Action action)
{
@@ -68,5 +107,6 @@ namespace linker.libs
{
Task.Factory.StartNew(action,TaskCreationOptions.LongRunning);
}
}
}

View File

@@ -53,7 +53,7 @@ namespace linker.messenger.decenter
private void SyncTask()
{
TimerHelper.SetInterval(async () =>
TimerHelper.SetIntervalLong(async () =>
{
try
{
@@ -100,7 +100,7 @@ namespace linker.messenger.decenter
}
}
return true;
}, () => 300);
}, 300);
}
class DecenterSyncTaskInfo

View File

@@ -4,8 +4,8 @@ namespace linker.messenger.flow
{
public sealed class ExternalFlow : IFlow
{
public ulong ReceiveBytes { get; private set; }
public ulong SendtBytes { get; private set; }
public long ReceiveBytes { get; private set; }
public long SendtBytes { get; private set; }
public string FlowName => "External";
public VersionManager Version { get; } = new VersionManager();
@@ -16,11 +16,11 @@ namespace linker.messenger.flow
public string GetItems() => string.Empty;
public void SetItems(string json) { }
public void SetBytes(ulong receiveBytes, ulong sendtBytes) { ReceiveBytes = receiveBytes; SendtBytes = sendtBytes; }
public void SetBytes(long receiveBytes, long sendtBytes) { ReceiveBytes = receiveBytes; SendtBytes = sendtBytes; }
public void Clear() { ReceiveBytes = 0; SendtBytes = 0;}
public void AddReceive(ulong bytes) { ReceiveBytes += bytes; Version.Add(); }
public void AddSendt(ulong bytes) { SendtBytes += bytes; Version.Add(); }
public void AddReceive(long bytes) { ReceiveBytes += bytes; Version.Add(); }
public void AddSendt(long bytes) { SendtBytes += bytes; Version.Add(); }
}
@@ -35,8 +35,8 @@ namespace linker.messenger.flow
this.externalFlow = externalFlow;
}
public override void AddReceive(ulong bytes) { externalFlow.AddReceive(bytes); }
public override void AddSendt(ulong bytes) { externalFlow.AddSendt(bytes); }
public override void AddReceive(long bytes) { externalFlow.AddReceive(bytes); }
public override void AddSendt(long bytes) { externalFlow.AddSendt(bytes); }
}

View File

@@ -18,11 +18,11 @@ namespace linker.messenger.flow
/// <summary>
/// 在线 | 总数
/// </summary>
public ulong ReceiveBytes { get; private set; }
public long ReceiveBytes { get; private set; }
/// <summary>
/// 服务器数
/// </summary>
public ulong SendtBytes { get; private set; }
public long SendtBytes { get; private set; }
private ConcurrentDictionary<IPAddress, OnlineFlowInfo> servers = new ConcurrentDictionary<IPAddress, OnlineFlowInfo>(new IPAddressComparer());
@@ -69,7 +69,7 @@ namespace linker.messenger.flow
public string GetItems() => string.Empty;
public void SetItems(string json) { }
public void SetBytes(ulong receiveBytes, ulong sendtBytes) { }
public void SetBytes(long receiveBytes, long sendtBytes) { }
public void Clear() { }
@@ -86,7 +86,7 @@ namespace linker.messenger.flow
private void OnlineTask()
{
TimerHelper.SetInterval(() =>
TimerHelper.SetIntervalLong(() =>
{
try
{
@@ -110,11 +110,11 @@ namespace linker.messenger.flow
servers.TryRemove(key, out _);
}
ulong online = (ulong)servers.Sum(c => c.Value.Online) << 32;
ulong total = (ulong)servers.Sum(c => c.Value.Total);
long online = servers.Sum(c => c.Value.Online) << 32;
long total = servers.Sum(c => c.Value.Total);
ReceiveBytes = online | total;
SendtBytes = (ulong)servers.Count;
SendtBytes = servers.Count;
Version.Add();
}
private void Report()

View File

@@ -5,21 +5,21 @@ namespace linker.messenger.flow
{
public interface IFlow
{
public ulong ReceiveBytes { get; }
public ulong SendtBytes { get; }
public long ReceiveBytes { get; }
public long SendtBytes { get; }
public string FlowName { get; }
public VersionManager Version { get; }
public string GetItems();
public void SetItems(string json);
public void SetBytes(ulong receiveBytes,ulong sendtBytes);
public void SetBytes(long receiveBytes,long sendtBytes);
public void Clear();
}
public partial class FlowItemInfo
{
public ulong ReceiveBytes { get; set; }
public ulong SendtBytes { get; set; }
public long ReceiveBytes { get; set; }
public long SendtBytes { get; set; }
[JsonIgnore]
public string FlowName { get; set; }

View File

@@ -1,13 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace linker.messenger.flow
{
public interface IFlowStore
{
}
}

View File

@@ -12,11 +12,11 @@ namespace linker.messenger.flow
this.messengerFlow = messengerFlow;
}
public override void AddReceive(ushort id, ulong bytes)
public override void AddReceive(ushort id, long bytes)
{
messengerFlow.AddReceive(id, bytes);
}
public override void AddSendt(ushort id, ulong bytes)
public override void AddSendt(ushort id, long bytes)
{
messengerFlow.AddSendt(id, bytes);
}
@@ -29,11 +29,11 @@ namespace linker.messenger.flow
this.messengerFlow = messengerFlow;
}
public override void AddReceive(ushort id, ulong bytes)
public override void AddReceive(ushort id, long bytes)
{
messengerFlow.AddReceive(id, bytes);
}
public override void AddSendt(ushort id, ulong bytes)
public override void AddSendt(ushort id, long bytes)
{
messengerFlow.AddSendt(id, bytes);
}
@@ -43,8 +43,8 @@ namespace linker.messenger.flow
public sealed class MessengerFlow : IFlow
{
public ulong ReceiveBytes { get; private set; }
public ulong SendtBytes { get; private set; }
public long ReceiveBytes { get; private set; }
public long SendtBytes { get; private set; }
public string FlowName => "Messenger";
public VersionManager Version { get; } = new VersionManager();
@@ -56,10 +56,10 @@ namespace linker.messenger.flow
public string GetItems() => flows.ToJson();
public void SetItems(string json) { flows = json.DeJson<Dictionary<ushort, FlowItemInfo>>(); }
public void SetBytes(ulong receiveBytes, ulong sendtBytes) { ReceiveBytes = receiveBytes; SendtBytes = sendtBytes; }
public void SetBytes(long receiveBytes, long sendtBytes) { ReceiveBytes = receiveBytes; SendtBytes = sendtBytes; }
public void Clear() { ReceiveBytes = 0; SendtBytes = 0;flows.Clear(); }
public void AddReceive(ushort id, ulong bytes)
public void AddReceive(ushort id, long bytes)
{
if (flows.TryGetValue(id, out FlowItemInfo messengerFlowItemInfo) == false)
{
@@ -70,7 +70,7 @@ namespace linker.messenger.flow
messengerFlowItemInfo.ReceiveBytes += bytes;
Version.Add();
}
public void AddSendt(ushort id, ulong bytes)
public void AddSendt(ushort id, long bytes)
{
if (flows.TryGetValue(id, out FlowItemInfo messengerFlowItemInfo) == false)
{

View File

@@ -7,8 +7,8 @@ namespace linker.messenger.flow
{
public sealed class RelayReportFlow : IFlow
{
public ulong ReceiveBytes { get; private set; }
public ulong SendtBytes { get; private set; }
public long ReceiveBytes { get; private set; }
public long SendtBytes { get; private set; }
public string FlowName => "RelayReport";
public VersionManager Version { get; } = new VersionManager();
public RelayReportFlow()
@@ -16,11 +16,11 @@ namespace linker.messenger.flow
}
public string GetItems() => string.Empty;
public void SetItems(string json) { }
public void SetBytes(ulong receiveBytes, ulong sendtBytes) { ReceiveBytes = receiveBytes; SendtBytes = sendtBytes; }
public void SetBytes(long receiveBytes, long sendtBytes) { ReceiveBytes = receiveBytes; SendtBytes = sendtBytes; }
public void Clear() { ReceiveBytes = 0; SendtBytes = 0; }
public void AddReceive(ulong bytes) { ReceiveBytes += bytes; Version.Add(); }
public void AddSendt(ulong bytes) { SendtBytes += bytes; Version.Add(); }
public void AddReceive(long bytes) { ReceiveBytes += bytes; Version.Add(); }
public void AddSendt(long bytes) { SendtBytes += bytes; Version.Add(); }
}
@@ -32,8 +32,8 @@ namespace linker.messenger.flow
this.relayReportFlow = relayReportFlow;
}
public override void AddReceive(ulong bytes) { relayReportFlow.AddReceive(bytes); }
public override void AddSendt(ulong bytes) { relayReportFlow.AddSendt(bytes); }
public override void AddReceive(long bytes) { relayReportFlow.AddReceive(bytes); }
public override void AddSendt(long bytes) { relayReportFlow.AddSendt(bytes); }
}
@@ -47,19 +47,19 @@ namespace linker.messenger.flow
this.relayFlow = relayFlow;
}
public override void AddReceive(string key, string from, string to, string groupid, ulong bytes)
public override void AddReceive(string key, string from, string to, string groupid, long bytes)
{
relayFlow.AddReceive(key, from, to, groupid, bytes);
}
public override void AddSendt(string key, string from, string to, string groupid, ulong bytes)
public override void AddSendt(string key, string from, string to, string groupid, long bytes)
{
relayFlow.AddSendt(key, from, to, groupid, bytes);
}
public override void AddReceive(string key, ulong bytes)
public override void AddReceive(string key, long bytes)
{
relayFlow.AddReceive(key, bytes);
}
public override void AddSendt(string key, ulong bytes)
public override void AddSendt(string key, long bytes)
{
relayFlow.AddSendt(key, bytes);
}
@@ -68,8 +68,8 @@ namespace linker.messenger.flow
public sealed class RelayFlow : IFlow
{
public ulong ReceiveBytes { get; private set; }
public ulong SendtBytes { get; private set; }
public long ReceiveBytes { get; private set; }
public long SendtBytes { get; private set; }
public string FlowName => "Relay";
public VersionManager Version { get; } = new VersionManager();
@@ -79,7 +79,7 @@ namespace linker.messenger.flow
public RelayFlow()
{
TimerHelper.SetInterval(() =>
TimerHelper.SetIntervalLong(() =>
{
if (lastTicksManager.DiffLessEqual(5000))
{
@@ -103,10 +103,10 @@ namespace linker.messenger.flow
public string GetItems() => flows.ToJson();
public void SetItems(string json) { flows = json.DeJson<Dictionary<string, RelayFlowItemInfo>>(); }
public void SetBytes(ulong receiveBytes, ulong sendtBytes) { ReceiveBytes = receiveBytes; SendtBytes = sendtBytes; }
public void SetBytes(long receiveBytes, long sendtBytes) { ReceiveBytes = receiveBytes; SendtBytes = sendtBytes; }
public void Clear() { ReceiveBytes = 0; SendtBytes = 0;flows.Clear(); }
public void AddReceive(string key, string from, string to, string groupid, ulong bytes)
public void AddReceive(string key, string from, string to, string groupid, long bytes)
{
if (flows.TryGetValue(key, out RelayFlowItemInfo messengerFlowItemInfo) == false)
{
@@ -117,7 +117,7 @@ namespace linker.messenger.flow
messengerFlowItemInfo.ReceiveBytes += bytes;
Version.Add();
}
public void AddSendt(string key, string from, string to, string groupid, ulong bytes)
public void AddSendt(string key, string from, string to, string groupid, long bytes)
{
if (flows.TryGetValue(key, out RelayFlowItemInfo messengerFlowItemInfo) == false)
{
@@ -129,7 +129,7 @@ namespace linker.messenger.flow
Version.Add();
}
public void AddReceive(string key, ulong bytes)
public void AddReceive(string key, long bytes)
{
if (flows.TryGetValue(key, out RelayFlowItemInfo messengerFlowItemInfo))
{
@@ -139,7 +139,7 @@ namespace linker.messenger.flow
}
}
public void AddSendt(string key, ulong bytes)
public void AddSendt(string key, long bytes)
{
if (flows.TryGetValue(key, out RelayFlowItemInfo messengerFlowItemInfo))
{
@@ -201,17 +201,17 @@ namespace linker.messenger.flow
public sealed partial class RelayFlowItemInfo : FlowItemInfo
{
public ulong DiffReceiveBytes { get; set; }
public ulong DiffSendtBytes { get; set; }
public long DiffReceiveBytes { get; set; }
public long DiffSendtBytes { get; set; }
public string FromName { get; set; }
public string ToName { get; set; }
public string GroupId { get; set; }
[JsonIgnore]
public ulong OldReceiveBytes { get; set; }
public long OldReceiveBytes { get; set; }
[JsonIgnore]
public ulong OldSendtBytes { get; set; }
public long OldSendtBytes { get; set; }
}
public sealed partial class RelayFlowRequestInfo

View File

@@ -12,11 +12,11 @@ namespace linker.messenger.flow
{
this.sForwardFlow = sForwardFlow;
}
public override void AddReceive(string key, string groupid, ulong bytes)
public override void AddReceive(string key, string groupid, long bytes)
{
sForwardFlow.AddReceive(key, groupid, bytes);
}
public override void AddSendt(string key, string groupid, ulong bytes)
public override void AddSendt(string key, string groupid, long bytes)
{
sForwardFlow.AddSendt(key, groupid, bytes);
}
@@ -24,8 +24,8 @@ namespace linker.messenger.flow
public sealed class SForwardFlow : IFlow
{
public ulong ReceiveBytes { get; private set; }
public ulong SendtBytes { get; private set; }
public long ReceiveBytes { get; private set; }
public long SendtBytes { get; private set; }
public string FlowName => "SForward";
public VersionManager Version { get; } = new VersionManager();
@@ -35,7 +35,7 @@ namespace linker.messenger.flow
public SForwardFlow()
{
TimerHelper.SetInterval(() =>
TimerHelper.SetIntervalLong(() =>
{
if (lastTicksManager.DiffLessEqual(5000))
{
@@ -54,7 +54,7 @@ namespace linker.messenger.flow
public string GetItems() => flows.ToJson();
public void SetItems(string json) { flows = json.DeJson<Dictionary<string, SForwardFlowItemInfo>>(); }
public void SetBytes(ulong receiveBytes, ulong sendtBytes) { ReceiveBytes = receiveBytes; SendtBytes = sendtBytes; }
public void SetBytes(long receiveBytes, long sendtBytes) { ReceiveBytes = receiveBytes; SendtBytes = sendtBytes; }
public void Clear() { ReceiveBytes = 0; SendtBytes = 0;flows.Clear(); }
public void Update()
@@ -62,7 +62,7 @@ namespace linker.messenger.flow
lastTicksManager.Update();
}
public void AddReceive(string key, string groupid, ulong bytes)
public void AddReceive(string key, string groupid, long bytes)
{
if (flows.TryGetValue(key, out SForwardFlowItemInfo messengerFlowItemInfo) == false)
{
@@ -73,7 +73,7 @@ namespace linker.messenger.flow
messengerFlowItemInfo.ReceiveBytes += bytes;
Version.Add();
}
public void AddSendt(string key, string groupid, ulong bytes)
public void AddSendt(string key, string groupid, long bytes)
{
if (flows.TryGetValue(key, out SForwardFlowItemInfo messengerFlowItemInfo) == false)
{
@@ -134,16 +134,16 @@ namespace linker.messenger.flow
public sealed partial class SForwardFlowItemInfo : FlowItemInfo
{
public ulong DiffReceiveBytes { get; set; }
public ulong DiffSendtBytes { get; set; }
public long DiffReceiveBytes { get; set; }
public long DiffSendtBytes { get; set; }
public string Key { get; set; }
public string GroupId { get; set; }
[JsonIgnore]
public ulong OldReceiveBytes { get; set; }
public long OldReceiveBytes { get; set; }
[JsonIgnore]
public ulong OldSendtBytes { get; set; }
public long OldSendtBytes { get; set; }
}
public sealed partial class SForwardFlowRequestInfo

View File

@@ -38,7 +38,7 @@ namespace linker.messenger.flow.messenger
Dictionary<string, FlowItemInfo> dic = flowTransfer.GetFlows();
signCaching.GetOnline(out int all, out int online);
dic.TryAdd("_", new FlowItemInfo { FlowName = "_", ReceiveBytes = (ulong)all, SendtBytes = (ulong)online });
dic.TryAdd("_", new FlowItemInfo { FlowName = "_", ReceiveBytes = all, SendtBytes = online });
FlowInfo serverFlowInfo = new FlowInfo
{

View File

@@ -7,7 +7,7 @@ namespace linker.messenger.forward
public sealed partial class ForwardInfo
{
public ForwardInfo() { }
public uint Id { get; set; }
public long Id { get; set; }
/// <summary>
/// 名称
/// </summary>
@@ -60,7 +60,7 @@ namespace linker.messenger.forward
public sealed partial class ForwardRemoveForwardInfo
{
public string MachineId { get; set; }
public uint Id { get; set; }
public long Id { get; set; }
}
public sealed partial class ForwardCountInfo
{

View File

@@ -18,9 +18,6 @@ namespace linker.messenger.forward
private readonly IMessengerSender messengerSender;
private readonly ISignInClientStore signInClientStore;
private readonly ISerializer serializer;
private readonly NumberSpaceUInt32 ns = new NumberSpaceUInt32();
public ForwardTransfer(IForwardClientStore forwardClientStore, ForwardProxy forwardProxy, SignInClientState signInClientState, IMessengerSender messengerSender, ISignInClientStore signInClientStore, ISerializer serializer)
{
this.forwardClientStore = forwardClientStore;
@@ -54,9 +51,6 @@ namespace linker.messenger.forward
{
lock (this)
{
uint maxid = forwardClientStore.Count() > 0 ? forwardClientStore.Get().Max(c => c.Id) : 1;
ns.Reset(maxid);
foreach (var item in forwardClientStore.Get(signInClientStore.Group.Id))
{
if (item.Started)
@@ -149,7 +143,7 @@ namespace linker.messenger.forward
public bool Add(ForwardInfo forwardInfo)
{
//同名或者同端口但是ID不一样
ForwardInfo old = forwardClientStore.Get().FirstOrDefault(c => (c.Port == forwardInfo.Port && c.Port != 0) && c.MachineId == forwardInfo.MachineId);
ForwardInfo old = forwardClientStore.Get().FirstOrDefault(c => (c.Port == forwardInfo.Port && c.Port != 0) && c.GroupId == signInClientStore.Group.Id && c.MachineId == forwardInfo.MachineId);
if (old != null && old.Id != forwardInfo.Id) return false;
if (forwardInfo.Id != 0)
@@ -169,10 +163,10 @@ namespace linker.messenger.forward
old.Started = forwardInfo.Started;
old.BufferSize = forwardInfo.BufferSize;
old.GroupId = signInClientStore.Group.Id;
forwardClientStore.Update(forwardInfo);
}
else
{
forwardInfo.Id = ns.Increment();
forwardInfo.GroupId = signInClientStore.Group.Id;
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
@@ -186,7 +180,7 @@ namespace linker.messenger.forward
return true;
}
public bool Remove(uint id)
public bool Remove(long id)
{
//同名或者同端口但是ID不一样
ForwardInfo old = forwardClientStore.Get(id);

View File

@@ -18,7 +18,7 @@
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public ForwardInfo Get(uint id);
public ForwardInfo Get(long id);
/// <summary>
/// 根据分组获取
/// </summary>
@@ -42,7 +42,7 @@
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public bool Remove(uint id);
public bool Remove(long id);
/// <summary>
/// 提交更新

View File

@@ -270,7 +270,7 @@ namespace linker.messenger.forward.proxy
/// </summary>
private void TaskUdp()
{
TimerHelper.SetInterval(() =>
TimerHelper.SetIntervalLong(() =>
{
if(udpConnections.Count > 0)
{

View File

@@ -69,7 +69,7 @@ namespace linker.messenger.logger
{
}
};
TimerHelper.SetInterval(() =>
TimerHelper.SetIntervalLong(() =>
{
string[] files = Directory.GetFiles("logs").OrderBy(c => c).ToArray();
for (int i = 0; i < files.Length - 180; i++)

View File

@@ -72,7 +72,7 @@ namespace linker.messenger.relay
[Access(AccessValue.RelayCdkey)]
public async Task<bool> AddCdkey(ApiControllerParamsInfo param)
{
RelayServerCdkeyInfo info = param.Content.DeJson<RelayServerCdkeyInfo>();
RelayServerCdkeyStoreInfo info = param.Content.DeJson<RelayServerCdkeyStoreInfo>();
var resp = await messengerSender.SendReply(new MessageRequestWrap
{
Connection = signInClientState.Connection,
@@ -96,7 +96,7 @@ namespace linker.messenger.relay
MessengerId = (ushort)RelayMessengerIds.DelCdkey,
Payload = serializer.Serialize(new RelayServerCdkeyDelInfo
{
Id = param.Content,
CdkeyId = long.Parse(param.Content),
SecretKey = relayClientStore.Server.SecretKey
})
});

View File

@@ -66,14 +66,14 @@ namespace linker.messenger.relay
}
private void TestTask()
{
TimerHelper.SetInterval(async () =>
TimerHelper.SetIntervalLong(async () =>
{
if (lastTicksManager.DiffLessEqual(3000))
{
await TaskRelay();
}
return true;
}, () => 3000);
}, 3000);
}
}

View File

@@ -5,6 +5,7 @@ using linker.messenger.relay.client;
using linker.messenger.relay.server;
using linker.messenger.signin;
using linker.messenger.relay.server.validator;
using linker.libs.extends;
namespace linker.messenger.relay.messenger
{
@@ -81,7 +82,7 @@ namespace linker.messenger.relay.messenger
TransportName = "test",
}, cache, null);
var nodes = relayServerTransfer.GetNodes(string.IsNullOrWhiteSpace(result), info.UserId);
var nodes = relayServerTransfer.GetNodes(string.IsNullOrWhiteSpace(result));
connection.Write(serializer.Serialize(nodes));
}
@@ -110,11 +111,11 @@ namespace linker.messenger.relay.messenger
bool validated = string.IsNullOrWhiteSpace(error);
result.Nodes = relayServerTransfer.GetNodes(validated);
List<RelayServerCdkeyInfo> cdkeys = await relayServerCdkeyStore.Get(info.UserId);
List<RelayServerCdkeyStoreInfo> cdkeys = await relayServerCdkeyStore.GetAvailable(info.UserId);
if (result.Nodes.Count > 0)
{
result.FlowingId = relayServerTransfer.AddRelay(cacheFrom.MachineId, cacheFrom.MachineName, cacheTo.MachineId, cacheTo.MachineName, cacheFrom.GroupId, validated, cdkeys);
result.FlowingId = relayServerTransfer.AddRelay(cacheFrom.MachineId, cacheFrom.MachineName, cacheTo.MachineId, cacheTo.MachineName, cacheFrom.GroupId, validated, cdkeys.Select(c => new RelayServerCdkeyInfo { Bandwidth = c.Bandwidth, CdkeyId = c.CdkeyId, LastBytes = c.LastBytes }).ToList());
}
connection.Write(serializer.Serialize(result));
@@ -230,7 +231,7 @@ namespace linker.messenger.relay.messenger
return;
}
await relayServerCdkeyStore.Del(info.Id);
await relayServerCdkeyStore.Del(info.CdkeyId);
connection.Write(Helper.TrueArray);
}
@@ -266,9 +267,17 @@ namespace linker.messenger.relay.messenger
/// <param name="connection"></param>
/// <returns></returns>
[MessengerId((ushort)RelayMessengerIds.NodeGetCache)]
public async Task NodeGetCache(IConnection connection)
public void NodeGetCache(IConnection connection)
{
string key = serializer.Deserialize<string>(connection.ReceiveRequestWrap.Payload.Span);
if (relayServerTransfer.TryGetRelayCache(key, out RelayCacheInfo cache))
{
connection.Write(serializer.Serialize(cache));
}
else
{
connection.Write(Helper.EmptyArray);
}
}
/// <summary>
/// 获取缓存
@@ -276,9 +285,14 @@ namespace linker.messenger.relay.messenger
/// <param name="connection"></param>
/// <returns></returns>
[MessengerId((ushort)RelayMessengerIds.NodeReport)]
public async Task NodeReport(IConnection connection)
public void NodeReport(IConnection connection)
{
RelayServerNodeReportInfo info = serializer.Deserialize<RelayServerNodeReportInfo>(connection.ReceiveRequestWrap.Payload.Span);
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
LoggerHelper.Instance.Debug($"relay node report : {info.ToJson()}");
}
relayServerTransfer.SetNodeReport(connection, info);
}
/// <summary>
/// 消耗流量报告
@@ -288,12 +302,14 @@ namespace linker.messenger.relay.messenger
[MessengerId((ushort)RelayMessengerIds.TrafficReport)]
public async Task TrafficReport(IConnection connection)
{
RelayTrafficReportInfo info = serializer.Deserialize<RelayTrafficReportInfo>(connection.ReceiveRequestWrap.Payload.Span);
if (relayServerStore.SecretKey != info.SecretKey )
RelayTrafficUpdateInfo info = serializer.Deserialize<RelayTrafficUpdateInfo>(connection.ReceiveRequestWrap.Payload.Span);
if (relayServerStore.SecretKey != info.SecretKey)
{
connection.Write(serializer.Serialize(new Dictionary<string,ulong>()));
connection.Write(serializer.Serialize(new Dictionary<string, long>()));
return;
}
Dictionary<long, long> result = await relayServerTransfer.AddTraffic(info);
connection.Write(serializer.Serialize(result));
}
}
}

View File

@@ -2,18 +2,41 @@
{
public interface IRelayServerCdkeyStore
{
public Task<bool> Add(RelayServerCdkeyInfo info);
public Task<bool> Del(string id);
public Task<bool> Add(RelayServerCdkeyStoreInfo info);
public Task<bool> Del(long id);
/// <summary>
/// 获取有效的CDKEY
/// </summary>
/// <param name="userid"></param>
/// <returns></returns>
public Task<List<RelayServerCdkeyInfo>> Get(string userid);
public Task<List<RelayServerCdkeyStoreInfo>> GetAvailable(string userid);
public Task<List<RelayServerCdkeyStoreInfo>> Get(List<long> ids);
public Task<bool> Traffic(Dictionary<long, long> dic);
public Task<RelayServerCdkeyPageResultInfo> Get(RelayServerCdkeyPageRequestInfo relayServerCdkeyPageRequestInfo);
}
public sealed class RelayServerCdkeyConfigInfo
{
/// <summary>
/// 获取可用的CDKEY
/// </summary>
public string CdkeyAvailablePostUrl { get; set; } = string.Empty;
/// <summary>
/// 分页获取CDKEY
/// </summary>
public string CdkeyPagePostUrl { get; set; } = string.Empty;
/// <summary>
/// id列表获取CDKEY
/// </summary>
public string CdkeyListPostUrl { get; set; } = string.Empty;
/// <summary>
/// 报告流量websocket
/// </summary>
public string CdkeyTrafficWsUrl { get; set; } = string.Empty;
}
public sealed partial class RelayServerCdkeyPageRequestInfo
{
public int Page { get; set; }
@@ -29,24 +52,24 @@
public int Page { get; set; }
public int Size { get; set; }
public int Count { get; set; }
public List<RelayServerCdkeyInfo> List { get; set; }
public List<RelayServerCdkeyStoreInfo> List { get; set; }
}
public sealed partial class RelayServerCdkeyAddInfo
{
public string SecretKey { get; set; }
public RelayServerCdkeyInfo Data { get; set; }
public RelayServerCdkeyStoreInfo Data { get; set; }
}
public sealed partial class RelayServerCdkeyDelInfo
{
public string SecretKey { get; set; }
public string Id { get; set; }
public long CdkeyId { get; set; }
}
/// <summary>
/// 中继CDKEY
/// 中继CDKEY存储
/// </summary>
public sealed partial class RelayServerCdkeyInfo
public sealed partial class RelayServerCdkeyStoreInfo : RelayServerCdkeyInfo
{
public string Id { get; set; }
@@ -72,21 +95,17 @@
/// </summary>
public DateTime EndTime { get; set; }
/// <summary>
/// 最后使用时间
/// </summary>
public DateTime UseTime { get; set; }
/// <summary>
/// 允许节点
/// </summary>
public List<string> Nodes { get; set; }
/// <summary>
/// 带宽Mbps
/// </summary>
public double Bandwidth { get; set; }
/// <summary>
/// 流量
/// </summary>
public ulong MaxBytes { get; set; }
/// <summary>
/// 剩余流量
/// </summary>
public ulong LastBytes { get; set; }
public long MaxBytes { get; set; }
/// <summary>
/// 原价

View File

@@ -33,7 +33,7 @@ namespace linker.messenger.relay.server
/// 设置剩余流量
/// </summary>
/// <param name="value"></param>
public void SetMaxGbTotalLastBytes(ulong value);
public void SetMaxGbTotalLastBytes(long value);
/// <summary>
/// 提交保存
/// </summary>
@@ -68,7 +68,7 @@ namespace linker.messenger.relay.server
public double MaxBandwidth { get; set; }
public double MaxBandwidthTotal { get; set; }
public double MaxGbTotal { get; set; }
public ulong MaxGbTotalLastBytes { get; set; }
public long MaxGbTotalLastBytes { get; set; }
public int MaxGbTotalMonth { get; set; }
public bool Public { get; set; }
@@ -79,8 +79,6 @@ namespace linker.messenger.relay.server
#else
public string MasterSecretKey { get; set; } = string.Empty;
#endif
public List<string> UserIds { get; set; } = new List<string>();
}
public sealed partial class RelayServerNodeReportInfo
@@ -92,7 +90,7 @@ namespace linker.messenger.relay.server
public double MaxBandwidth { get; set; }
public double MaxBandwidthTotal { get; set; }
public double MaxGbTotal { get; set; }
public ulong MaxGbTotalLastBytes { get; set; }
public long MaxGbTotalLastBytes { get; set; }
public double ConnectionRatio { get; set; }
public double BandwidthRatio { get; set; }
@@ -104,8 +102,6 @@ namespace linker.messenger.relay.server
public IPEndPoint EndPoint { get; set; }
public long LastTicks { get; set; }
public List<string> UserIds { get; set; } = new List<string>();
}

View File

@@ -13,14 +13,17 @@ namespace linker.messenger.relay.server
private ulong relayFlowingId = 0;
private readonly ConcurrentDictionary<string, RelayServerNodeReportInfo> reports = new ConcurrentDictionary<string, RelayServerNodeReportInfo>();
private readonly ConcurrentQueue<Dictionary<long, long>> trafficQueue = new ConcurrentQueue<Dictionary<long, long>>();
private readonly IRelayServerCaching relayCaching;
private readonly ISerializer serializer;
public RelayServerMasterTransfer(IRelayServerCaching relayCaching, ISerializer serializer, IRelayServerMasterStore relayServerMasterStore)
private readonly IRelayServerCdkeyStore relayServerCdkeyStore;
public RelayServerMasterTransfer(IRelayServerCaching relayCaching, ISerializer serializer, IRelayServerMasterStore relayServerMasterStore, IRelayServerCdkeyStore relayServerCdkeyStore)
{
this.relayCaching = relayCaching;
this.serializer = serializer;
this.relayServerCdkeyStore = relayServerCdkeyStore;
TrafficTask();
}
@@ -82,10 +85,10 @@ namespace linker.messenger.relay.server
/// </summary>
/// <param name="validated">是否已认证</param>
/// <returns></returns>
public List<RelayServerNodeReportInfo> GetNodes(bool validated, string userid)
public List<RelayServerNodeReportInfo> GetNodes(bool validated)
{
var result = reports.Values
.Where(c => c.Public || (c.Public == false && validated) || c.UserIds.Contains(userid))
.Where(c => c.Public || validated)
.Where(c => Environment.TickCount64 - c.LastTicks < 15000)
.Where(c => c.ConnectionRatio < 100 && (c.MaxGbTotal == 0 || (c.MaxGbTotal > 0 && c.MaxGbTotalLastBytes > 0)))
.OrderByDescending(c => c.LastTicks);
@@ -96,7 +99,7 @@ namespace linker.messenger.relay.server
.ThenByDescending(x => x.MaxBandwidth == 0 ? double.MaxValue : x.MaxBandwidth)
.ThenByDescending(x => x.MaxBandwidthTotal == 0 ? double.MaxValue : x.MaxBandwidthTotal)
.ThenByDescending(x => x.MaxGbTotal == 0 ? double.MaxValue : x.MaxGbTotal)
.ThenByDescending(x => x.MaxGbTotalLastBytes == 0 ? ulong.MaxValue : x.MaxGbTotalLastBytes)
.ThenByDescending(x => x.MaxGbTotalLastBytes == 0 ? long.MaxValue : x.MaxGbTotalLastBytes)
.ToList();
}
@@ -109,7 +112,29 @@ namespace linker.messenger.relay.server
{
return reports.TryGetValue(nodeId, out RelayServerNodeReportInfo relayNodeReportInfo) && relayNodeReportInfo.Public == false;
}
public async Task<Dictionary<long, long>> AddTraffic(RelayTrafficUpdateInfo relayTrafficUpdateInfo)
{
if (relayTrafficUpdateInfo.Dic.Count > 0)
trafficQueue.Enqueue(relayTrafficUpdateInfo.Dic);
if (relayTrafficUpdateInfo.Ids == null || relayTrafficUpdateInfo.Ids.Count == 0)
{
return new Dictionary<long, long>();
}
return (await relayServerCdkeyStore.Get(relayTrafficUpdateInfo.Ids)).ToDictionary(c => c.CdkeyId, c => c.LastBytes);
}
private void TrafficTask()
{
TimerHelper.SetIntervalLong(async () =>
{
while (trafficQueue.TryDequeue(out Dictionary<long, long> dic))
{
await relayServerCdkeyStore.Traffic(dic).ConfigureAwait(false);
}
return true;
},3000);
}
}
}

View File

@@ -5,7 +5,6 @@ using System.Buffers;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
namespace linker.messenger.relay.server
{
@@ -18,11 +17,11 @@ namespace linker.messenger.relay.server
private IConnection localConnection;
private IConnection remoteConnection;
private ulong bytes = 0;
private ulong lastBytes = 0;
RelaySpeedLimit limitTotal = new RelaySpeedLimit();
private long bytes = 0;
private long lastBytes = 0;
private RelaySpeedLimit limitTotal = new RelaySpeedLimit();
private readonly ConcurrentDictionary<ulong, RelayTrafficCacheInfo> trafficDict = new ConcurrentDictionary<ulong, RelayTrafficCacheInfo>();
private readonly ConcurrentDictionary<string, ulong> cdkeyLastBytes = new ConcurrentDictionary<string, ulong>();
private readonly ConcurrentDictionary<long, long> cdkeyLastBytes = new ConcurrentDictionary<long, long>();
private readonly ISerializer serializer;
private readonly IRelayServerNodeStore relayServerNodeStore;
@@ -50,7 +49,7 @@ namespace linker.messenger.relay.server
{
try
{
IConnection connection = nodeid == RelayServerNodeInfo.MASTER_NODE_ID ? localConnection : remoteConnection;
IConnection connection = relayServerNodeStore.Node.Id == RelayServerNodeInfo.MASTER_NODE_ID ? localConnection : remoteConnection;
MessageResponeInfo resp = await messengerSender.SendReply(new MessageRequestWrap
{
@@ -133,6 +132,7 @@ namespace linker.messenger.relay.server
public bool NeedLimit(RelayTrafficCacheInfo relayCache)
{
if (relayCache.Cache.Validated) return false;
//if (relayCache.CurrentCdkey != null) return false;
return limitTotal.NeedLimit();
}
/// <summary>
@@ -164,7 +164,7 @@ namespace linker.messenger.relay.server
trafficDict.TryRemove(relayCache.Cache.FlowId, out _);
foreach (var item in relayCache.Cache.Cdkey)
{
cdkeyLastBytes.TryRemove(item.Id, out _);
cdkeyLastBytes.TryRemove(item.CdkeyId, out _);
}
}
/// <summary>
@@ -172,7 +172,7 @@ namespace linker.messenger.relay.server
/// </summary>
/// <param name="length"></param>
/// <returns></returns>
public bool AddBytes(RelayTrafficCacheInfo cache, ulong length)
public bool AddBytes(RelayTrafficCacheInfo cache, long length)
{
Interlocked.Add(ref bytes, length);
@@ -194,20 +194,13 @@ namespace linker.messenger.relay.server
/// <param name="relayCache"></param>
private void SetLimit(RelayTrafficCacheInfo relayCache)
{
//验证过的,无限制
if (relayCache.Cache.Validated)
//无限制
if (relayCache.Cache.Validated || relayServerNodeStore.Node.MaxBandwidth == 0)
{
relayCache.Limit.SetLimit(0);
return;
}
//节点无限制
if (relayServerNodeStore.Node.MaxBandwidth == 0)
{
relayCache.Limit.SetLimit((uint)Math.Ceiling((relayServerNodeStore.Node.MaxBandwidth * 1024 * 1024) / 8.0));
return;
}
RelayServerCdkeyInfo currentCdkey = relayCache.Cache.Cdkey.Where(c => c.LastBytes > 0).OrderByDescending(c => c.Bandwidth).FirstOrDefault();
//有cdkey且带宽大于节点带宽就用cdkey的带宽
if (currentCdkey != null && (currentCdkey.Bandwidth == 0 || currentCdkey.Bandwidth > relayServerNodeStore.Node.MaxBandwidth))
@@ -220,91 +213,129 @@ namespace linker.messenger.relay.server
relayCache.Limit.SetLimit((uint)Math.Ceiling((relayServerNodeStore.Node.MaxBandwidth * 1024 * 1024) / 8.0));
}
private void ResetNodeBytes(ulong length)
private void ResetNodeBytes()
{
if (relayServerNodeStore.Node.MaxGbTotalLastBytes >= length)
relayServerNodeStore.SetMaxGbTotalLastBytes(relayServerNodeStore.Node.MaxGbTotalLastBytes - length);
else relayServerNodeStore.SetMaxGbTotalLastBytes(0);
foreach (var cache in trafficDict.Values.Where(c => c.CurrentCdkey == null))
{
long length = cache.Sendt;
Interlocked.Exchange(ref cache.Sendt, 0);
if (relayServerNodeStore.Node.MaxGbTotalLastBytes >= length)
relayServerNodeStore.SetMaxGbTotalLastBytes(relayServerNodeStore.Node.MaxGbTotalLastBytes - length);
else relayServerNodeStore.SetMaxGbTotalLastBytes(0);
}
if (relayServerNodeStore.Node.MaxGbTotalMonth != DateTime.Now.Month)
{
relayServerNodeStore.SetMaxGbTotalMonth(DateTime.Now.Month);
relayServerNodeStore.SetMaxGbTotalLastBytes((ulong)(relayServerNodeStore.Node.MaxGbTotal * 1024 * 1024 * 1024));
relayServerNodeStore.SetMaxGbTotalLastBytes((long)(relayServerNodeStore.Node.MaxGbTotal * 1024 * 1024 * 1024));
}
relayServerNodeStore.Confirm();
}
private void TrafficTask()
private void DownloadBytes()
{
TimerHelper.SetInterval(async () =>
TimerHelper.Async(async () =>
{
//需要报告Cdkey的流量
_ = messengerSender.SendReply(new MessageRequestWrap
List<long> ids = trafficDict.Values.SelectMany(c => c.Cache.Cdkey).Select(c => c.CdkeyId).Distinct().ToList();
while (ids.Count > 0)
{
//分批更新,可能数量很大
List<long> id = ids.Take(100).ToList();
ids.RemoveRange(0, id.Count);
MessageResponeInfo resp = await messengerSender.SendReply(new MessageRequestWrap
{
Connection = relayServerNodeStore.Node.Id == RelayServerNodeInfo.MASTER_NODE_ID ? localConnection : remoteConnection,
MessengerId = (ushort)RelayMessengerIds.TrafficReport,
Payload = serializer.Serialize(new RelayTrafficUpdateInfo
{
Dic = [],
Ids = id,
SecretKey = relayServerNodeStore.Node.Id == RelayServerNodeInfo.MASTER_NODE_ID
? relayServerMasterStore.Master.SecretKey
: relayServerNodeStore.Node.MasterSecretKey
}),
Timeout = 4000
});
if (resp.Code == MessageResponeCodes.OK && resp.Data.Length > 0)
{
Dictionary<long, long> dic = serializer.Deserialize<Dictionary<long, long>>(resp.Data.Span);
//更新剩余流量
foreach (KeyValuePair<long, long> item in dic)
{
cdkeyLastBytes.AddOrUpdate(item.Key, item.Value, (a, b) => item.Value);
}
//查不到的,归零
foreach (long item in id.Except(dic.Keys))
{
cdkeyLastBytes.AddOrUpdate(item, 0, (a, b) => 0);
}
}
}
});
}
private void UploadBytes()
{
TimerHelper.Async(async () =>
{
MessageResponeInfo resp = await messengerSender.SendReply(new MessageRequestWrap
{
Connection = relayServerNodeStore.Node.Id == RelayServerNodeInfo.MASTER_NODE_ID ? localConnection : remoteConnection,
MessengerId = (ushort)RelayMessengerIds.TrafficReport,
Payload = serializer.Serialize(new RelayTrafficReportInfo
Payload = serializer.Serialize(new RelayTrafficUpdateInfo
{
Id2Bytes = trafficDict.Values
.Where(c => c.CurrentCdkey != null && c.Sendt > 0)
.GroupBy(c => c.CurrentCdkey.Id)
.ToDictionary(c => c.Key, d => (ulong)d.Sum(d => (decimal)d.Sendt)),
UpdateIds = trafficDict.Values.SelectMany(c => c.Cache.Cdkey).Select(c => c.Id).Distinct().ToList(),
Dic = trafficDict.Values.Where(c => c.CurrentCdkey != null && c.Sendt > 0).GroupBy(c => c.CurrentCdkey.CdkeyId).ToDictionary(c => c.Key, d => d.Sum(d => d.Sendt)),
Ids = [],
SecretKey = relayServerNodeStore.Node.Id == RelayServerNodeInfo.MASTER_NODE_ID
? relayServerMasterStore.Master.SecretKey
: relayServerNodeStore.Node.MasterSecretKey
? relayServerMasterStore.Master.SecretKey
: relayServerNodeStore.Node.MasterSecretKey
}),
Timeout = 4000,
}).ContinueWith((result) =>
{
//更新cdkey的剩余流量
if (result.Result.Code != MessageResponeCodes.OK || result.Result.Data.Length == 0)
{
return;
}
Dictionary<string, ulong> dic = serializer.Deserialize<Dictionary<string, ulong>>(result.Result.Data.Span);
foreach (var item in dic)
{
cdkeyLastBytes.AddOrUpdate(item.Key, item.Value, (a, b) => item.Value);
}
Timeout = 4000
});
foreach (var cache in trafficDict.Values.Where(c => c.CurrentCdkey != null))
if (resp.Code == MessageResponeCodes.OK)
{
ulong length = cache.Sendt;
Interlocked.Add(ref Unsafe.As<ulong, long>(ref cache.Sendt), -(long)length);
if (cdkeyLastBytes.TryGetValue(cache.CurrentCdkey.Id, out ulong value))
try
{
cache.CurrentCdkey.LastBytes = value;
serializer.Deserialize<Dictionary<string, ulong>>(resp.Data.Span);
//成功报告了流量,就重新计数
foreach (var cache in trafficDict.Values.Where(c => c.CurrentCdkey != null))
{
Interlocked.Exchange(ref cache.Sendt, 0);
//检查一下是不是需要更新剩余流量
if (cdkeyLastBytes.TryGetValue(cache.CurrentCdkey.CdkeyId, out long value))
{
cache.CurrentCdkey.LastBytes = value;
}
//当前cdkey流量用完了就重新找找新的cdkey
if (cache.CurrentCdkey.LastBytes <= 0)
{
SetLimit(cache);
}
}
}
if (cache.CurrentCdkey.LastBytes <= 0)
catch (Exception)
{
SetLimit(cache);
}
}
});
}
private void TrafficTask()
{
TimerHelper.SetIntervalLong(() =>
{
UploadBytes();
DownloadBytes();
foreach (var cache in trafficDict.Values.Where(c => c.CurrentCdkey == null))
{
ulong length = cache.Sendt;
Interlocked.Add(ref Unsafe.As<ulong, long>(ref cache.Sendt), -(long)length);
ResetNodeBytes(length);
}
await Task.CompletedTask;
ResetNodeBytes();
return true;
}, () => 5000);
}, 5000);
}
private void ReportTask()
{
TimerHelper.SetInterval(async () =>
TimerHelper.SetIntervalLong(async () =>
{
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
LoggerHelper.Instance.Debug($"relay report : {relayServerNodeStore.Node.ToJson()}");
}
IEnumerable<RelayServerNodeInfo> nodes = new List<RelayServerNodeInfo>
{
//默认报告给自己,作为本服务器的一个默认中继节点
@@ -349,8 +380,7 @@ namespace linker.messenger.relay.server
MaxGbTotalLastBytes = node.MaxGbTotalLastBytes,
MaxConnection = node.MaxConnection,
ConnectionRatio = Math.Round(node.MaxConnection == 0 ? 0 : connectionNum / 2.0 / node.MaxConnection, 2),
EndPoint = endPoint,
UserIds = node.UserIds
EndPoint = endPoint
};
await messengerSender.SendOnly(new MessageRequestWrap
@@ -364,17 +394,17 @@ namespace linker.messenger.relay.server
{
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
LoggerHelper.Instance.Debug($"relay report : {ex}");
LoggerHelper.Instance.Error($"relay report : {ex}");
}
}
}
return true;
}, () => 5000);
}, 5000);
}
private void SignInTask()
{
TimerHelper.SetInterval(async () =>
TimerHelper.SetIntervalLong(async () =>
{
if ((remoteConnection == null || remoteConnection.Connected == false) && string.IsNullOrWhiteSpace(relayServerNodeStore.Node.MasterHost) == false)
{
@@ -385,7 +415,7 @@ namespace linker.messenger.relay.server
localConnection = await SignIn(new IPEndPoint(IPAddress.Loopback, relayServerNodeStore.ServicePort).ToString(), relayServerMasterStore.Master.SecretKey).ConfigureAwait(false);
}
return true;
}, () => 3000);
}, 3000);
}
private async Task<IConnection> SignIn(string host, string secretKey)
{
@@ -403,10 +433,14 @@ namespace linker.messenger.relay.server
Socket socket = new Socket(remote.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
socket.KeepAlive();
await socket.ConnectAsync(remote).WaitAsync(TimeSpan.FromMilliseconds(5000)).ConfigureAwait(false);
return await messengerResolver.BeginReceiveClient(socket, true, (byte)ResolverType.RelayReport, bytes).ConfigureAwait(false);
return await messengerResolver.BeginReceiveClient(socket, true, (byte)ResolverType.RelayReport, bytes.AsMemory(0, secretKeyBytes.Length + 1)).ConfigureAwait(false);
}
catch (Exception)
catch (Exception ex)
{
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
LoggerHelper.Instance.Error(ex);
}
}
finally
{
@@ -416,16 +450,16 @@ namespace linker.messenger.relay.server
}
}
public sealed partial class RelayTrafficReportInfo
public sealed partial class RelayTrafficUpdateInfo
{
/// <summary>
/// cdkey id 和 流量
/// </summary>
public Dictionary<string, ulong> Id2Bytes { get; set; }
public Dictionary<long, long> Dic { get; set; }
/// <summary>
/// 需要知道哪些cdkey的剩余流量
/// </summary>
public List<string> UpdateIds { get; set; }
public List<long> Ids { get; set; }
public string SecretKey { get; set; }
}

View File

@@ -24,10 +24,10 @@ namespace linker.messenger.relay.server
this.messengerResolver = messengerResolver;
}
public virtual void AddReceive(ulong bytes)
public virtual void AddReceive(long bytes)
{
}
public virtual void AddSendt(ulong bytes)
public virtual void AddSendt(long bytes)
{
}
@@ -36,19 +36,32 @@ namespace linker.messenger.relay.server
byte[] buffer = ArrayPool<byte>.Shared.Rent(1024);
try
{
AddReceive((ulong)memory.Length);
int length = await socket.ReceiveAsync(buffer.AsMemory(0, 1), SocketFlags.None).ConfigureAwait(false);
AddReceive((ulong)length);
AddReceive(memory.Length);
await socket.ReceiveAsync(buffer.AsMemory(0, 1), SocketFlags.None).ConfigureAwait(false);
int length = buffer[0];
AddReceive(length);
await socket.ReceiveAsync(buffer.AsMemory(0, length), SocketFlags.None).ConfigureAwait(false);
string key = buffer.AsMemory(0, length).GetString();
if (relayServerMasterStore.Master.SecretKey.Md5() == key)
{
await messengerResolver.BeginReceiveServer(socket, Helper.EmptyArray);
}
else
{
socket.SafeClose();
}
}
catch (Exception)
catch (Exception ex)
{
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
LoggerHelper.Instance.Error(ex);
}
socket.SafeClose();
}
finally
{

View File

@@ -26,16 +26,16 @@ namespace linker.messenger.relay.server
public virtual void AddReceive(string key, string from, string to, string groupid, ulong bytes)
public virtual void AddReceive(string key, string from, string to, string groupid, long bytes)
{
}
public virtual void AddSendt(string key, string from, string to, string groupid, ulong bytes)
public virtual void AddSendt(string key, string from, string to, string groupid, long bytes)
{
}
public virtual void AddReceive(string key, ulong bytes)
public virtual void AddReceive(string key, long bytes)
{
}
public virtual void AddSendt(string key, ulong bytes)
public virtual void AddSendt(string key, long bytes)
{
}
@@ -75,7 +75,7 @@ namespace linker.messenger.relay.server
}
//流量统计
AddReceive(relayCache.FromId, relayCache.FromName, relayCache.ToName, relayCache.GroupId, (ulong)length);
AddReceive(relayCache.FromId, relayCache.FromName, relayCache.ToName, relayCache.GroupId, length);
try
{
switch (relayMessage.Type)
@@ -152,7 +152,7 @@ namespace linker.messenger.relay.server
while ((bytesRead = await source.ReceiveAsync(buffer.AsMemory(), SocketFlags.None).ConfigureAwait(false)) != 0)
{
//流量限制
if (relayServerNodeTransfer.AddBytes(trafficCacheInfo, (ulong)bytesRead) == false)
if (relayServerNodeTransfer.AddBytes(trafficCacheInfo, bytesRead) == false)
{
source.SafeClose();
break;
@@ -181,8 +181,8 @@ namespace linker.messenger.relay.server
}
}
AddReceive(cache.FromId, cache.FromName, cache.ToName, cache.GroupId, (ulong)bytesRead);
AddSendt(cache.FromId, cache.FromName, cache.ToName, cache.GroupId, (ulong)bytesRead);
AddReceive(cache.FromId, cache.FromName, cache.ToName, cache.GroupId, bytesRead);
AddSendt(cache.FromId, cache.FromName, cache.ToName, cache.GroupId, bytesRead);
await destination.SendAsync(buffer.AsMemory(0, bytesRead), SocketFlags.None).ConfigureAwait(false);
}
}
@@ -263,11 +263,24 @@ namespace linker.messenger.relay.server
}
public sealed class RelayTrafficCacheInfo
{
public ulong Sendt;
public long Sendt;
public RelaySpeedLimit Limit { get; set; }
public RelayCacheInfo Cache { get; set; }
public RelayServerCdkeyInfo CurrentCdkey { get; set; }
}
public partial class RelayServerCdkeyInfo
{
public long CdkeyId { get; set; }
/// <summary>
/// 带宽Mbps
/// </summary>
public double Bandwidth { get; set; }
/// <summary>
/// 剩余流量
/// </summary>
public long LastBytes { get; set; }
}
public sealed class RelayWrapInfo
{

View File

@@ -56,6 +56,7 @@ namespace linker.messenger.serializer.memorypack
MemoryPackFormatterProvider.Register(new RelayCacheInfoFormatter());
MemoryPackFormatterProvider.Register(new RelayMessageInfoFormatter());
MemoryPackFormatterProvider.Register(new RelayServerCdkeyInfoFormatter());
MemoryPackFormatterProvider.Register(new RelayServerCdkeyStoreInfoFormatter());
MemoryPackFormatterProvider.Register(new RelayServerCdkeyPageRequestInfoFormatter());
MemoryPackFormatterProvider.Register(new RelayServerCdkeyPageResultInfoFormatter());
MemoryPackFormatterProvider.Register(new RelayServerCdkeyAddInfoFormatter());

View File

@@ -11,13 +11,13 @@ namespace linker.messenger.serializer.memorypack
public readonly FlowItemInfo info;
[MemoryPackInclude]
ulong ReceiveBytes => info.ReceiveBytes;
long ReceiveBytes => info.ReceiveBytes;
[MemoryPackInclude]
ulong SendtBytes => info.SendtBytes;
long SendtBytes => info.SendtBytes;
[MemoryPackConstructor]
SerializableFlowItemInfo(ulong receiveBytes, ulong sendtBytes)
SerializableFlowItemInfo(long receiveBytes, long sendtBytes)
{
var info = new FlowItemInfo { ReceiveBytes = receiveBytes, SendtBytes = sendtBytes };
this.info = info;
@@ -181,16 +181,16 @@ namespace linker.messenger.serializer.memorypack
public readonly RelayFlowItemInfo info;
[MemoryPackInclude]
ulong ReceiveBytes => info.ReceiveBytes;
long ReceiveBytes => info.ReceiveBytes;
[MemoryPackInclude]
ulong SendtBytes => info.SendtBytes;
long SendtBytes => info.SendtBytes;
[MemoryPackInclude]
ulong DiffReceiveBytes => info.DiffReceiveBytes;
long DiffReceiveBytes => info.DiffReceiveBytes;
[MemoryPackInclude]
ulong DiffSendtBytes => info.DiffSendtBytes;
long DiffSendtBytes => info.DiffSendtBytes;
[MemoryPackInclude]
string FromName => info.FromName;
@@ -199,7 +199,7 @@ namespace linker.messenger.serializer.memorypack
string ToName => info.ToName;
[MemoryPackConstructor]
SerializableRelayFlowItemInfo(ulong receiveBytes, ulong sendtBytes, ulong diffReceiveBytes, ulong diffSendtBytes, string fromName, string toName)
SerializableRelayFlowItemInfo(long receiveBytes, long sendtBytes, long diffReceiveBytes, long diffSendtBytes, string fromName, string toName)
{
var info = new RelayFlowItemInfo
{
@@ -393,22 +393,22 @@ namespace linker.messenger.serializer.memorypack
public readonly SForwardFlowItemInfo info;
[MemoryPackInclude]
ulong ReceiveBytes => info.ReceiveBytes;
long ReceiveBytes => info.ReceiveBytes;
[MemoryPackInclude]
ulong SendtBytes => info.SendtBytes;
long SendtBytes => info.SendtBytes;
[MemoryPackInclude]
ulong DiffReceiveBytes => info.DiffReceiveBytes;
long DiffReceiveBytes => info.DiffReceiveBytes;
[MemoryPackInclude]
ulong DiffSendtBytes => info.DiffSendtBytes;
long DiffSendtBytes => info.DiffSendtBytes;
[MemoryPackInclude]
string Key => info.Key;
[MemoryPackConstructor]
SerializableSForwardFlowItemInfo(ulong receiveBytes, ulong sendtBytes, ulong diffReceiveBytes, ulong diffSendtBytes, string key)
SerializableSForwardFlowItemInfo(long receiveBytes, long sendtBytes, long diffReceiveBytes, long diffSendtBytes, string key)
{
var info = new SForwardFlowItemInfo
{

View File

@@ -11,7 +11,7 @@ namespace linker.messenger.serializer.memorypack
public readonly ForwardInfo info;
[MemoryPackInclude]
uint Id => info.Id;
long Id => info.Id;
[MemoryPackInclude]
string Name => info.Name;
@@ -47,7 +47,7 @@ namespace linker.messenger.serializer.memorypack
string TargetMsg => info.TargetMsg;
[MemoryPackConstructor]
SerializableForwardInfo(uint id, string name, string machineId, string groupId, string machineName, IPAddress bindIPAddress, int port, IPEndPoint targetEP, bool started, byte bufferSize, string msg, string targetMsg)
SerializableForwardInfo(long id, string name, string machineId, string groupId, string machineName, IPAddress bindIPAddress, int port, IPEndPoint targetEP, bool started, byte bufferSize, string msg, string targetMsg)
{
this.info = new ForwardInfo
{
@@ -167,10 +167,10 @@ namespace linker.messenger.serializer.memorypack
string MachineId => info.MachineId;
[MemoryPackInclude, MemoryPackAllowSerialize]
uint Id => info.Id;
long Id => info.Id;
[MemoryPackConstructor]
SerializableForwardRemoveForwardInfo(string machineId, uint id)
SerializableForwardRemoveForwardInfo(string machineId, long id)
{
this.info = new ForwardRemoveForwardInfo
{

View File

@@ -171,7 +171,7 @@ namespace linker.messenger.serializer.memorypack
[MemoryPackInclude]
double MaxGbTotal => info.MaxGbTotal;
[MemoryPackInclude]
ulong MaxGbTotalLastBytes => info.MaxGbTotalLastBytes;
long MaxGbTotalLastBytes => info.MaxGbTotalLastBytes;
[MemoryPackInclude]
double ConnectionRatio => info.ConnectionRatio;
[MemoryPackInclude]
@@ -185,18 +185,15 @@ namespace linker.messenger.serializer.memorypack
[MemoryPackInclude]
long LastTicks => info.LastTicks;
[MemoryPackInclude]
List<string> UserIds => info.UserIds;
[MemoryPackConstructor]
SerializableRelayServerNodeReportInfo(
string id, string name,
int maxConnection, double maxBandwidth, double maxBandwidthTotal,
double maxGbTotal, ulong maxGbTotalLastBytes,
double maxGbTotal, long maxGbTotalLastBytes,
double connectionRatio, double bandwidthRatio,
bool Public, int delay,
IPEndPoint endPoint, long lastTicks, List<string> userIds)
IPEndPoint endPoint, long lastTicks)
{
var info = new RelayServerNodeReportInfo
{
@@ -212,8 +209,7 @@ namespace linker.messenger.serializer.memorypack
MaxGbTotal = maxGbTotal,
MaxGbTotalLastBytes = maxGbTotalLastBytes,
Name = name,
Public = Public,
UserIds = userIds
Public = Public
};
this.info = info;
}
@@ -447,7 +443,6 @@ namespace linker.messenger.serializer.memorypack
}
}
[MemoryPackable]
public readonly partial struct SerializableRelayServerCdkeyInfo
{
@@ -455,53 +450,21 @@ namespace linker.messenger.serializer.memorypack
public readonly RelayServerCdkeyInfo info;
[MemoryPackInclude]
string Id => info.Id;
[MemoryPackInclude]
string UserId => info.UserId;
long CdkeyId => info.CdkeyId;
[MemoryPackInclude]
string CdKey => info.CdKey;
[MemoryPackInclude]
DateTime AddTime => info.AddTime;
[MemoryPackInclude]
DateTime StartTime => info.StartTime;
[MemoryPackInclude]
DateTime EndTime => info.EndTime;
[MemoryPackInclude]
List<string> Nodes => info.Nodes;
[MemoryPackInclude]
double Bandwidth => info.Bandwidth;
[MemoryPackInclude]
ulong MaxBytes => info.MaxBytes;
[MemoryPackInclude]
ulong LastBytes => info.LastBytes;
[MemoryPackInclude]
double Memory => info.Memory;
[MemoryPackInclude]
double PayMemory => info.PayMemory;
[MemoryPackInclude]
string Remark => info.Remark;
long LastBytes => info.LastBytes;
[MemoryPackConstructor]
SerializableRelayServerCdkeyInfo(string id, string userid, string cdKey, DateTime addTime, DateTime startTime, DateTime endTime,
List<string> nodes, double bandwidth, ulong maxBytes, ulong lastBytes, double memory, double payMemory, string remark)
SerializableRelayServerCdkeyInfo(long cdkeyid, double bandwidth, long lastBytes)
{
var info = new RelayServerCdkeyInfo
{
Id = id,
UserId = userid,
CdKey = cdKey,
AddTime = addTime,
StartTime = startTime,
EndTime = endTime,
Nodes = nodes,
CdkeyId = cdkeyid,
Bandwidth = bandwidth,
MaxBytes = maxBytes,
LastBytes = lastBytes,
Memory = memory,
PayMemory = payMemory,
Remark = remark
LastBytes = lastBytes
};
this.info = info;
}
@@ -539,6 +502,103 @@ namespace linker.messenger.serializer.memorypack
}
[MemoryPackable]
public readonly partial struct SerializableRelayServerCdkeyStoreInfo
{
[MemoryPackIgnore]
public readonly RelayServerCdkeyStoreInfo info;
[MemoryPackInclude]
long CdkeyId => info.CdkeyId;
[MemoryPackInclude]
string Id => info.Id;
[MemoryPackInclude]
string UserId => info.UserId;
[MemoryPackInclude]
string CdKey => info.CdKey;
[MemoryPackInclude]
DateTime AddTime => info.AddTime;
[MemoryPackInclude]
DateTime StartTime => info.StartTime;
[MemoryPackInclude]
DateTime EndTime => info.EndTime;
[MemoryPackInclude]
DateTime UseTime => info.UseTime;
[MemoryPackInclude]
List<string> Nodes => info.Nodes;
[MemoryPackInclude]
double Bandwidth => info.Bandwidth;
[MemoryPackInclude]
long MaxBytes => info.MaxBytes;
[MemoryPackInclude]
long LastBytes => info.LastBytes;
[MemoryPackInclude]
double Memory => info.Memory;
[MemoryPackInclude]
double PayMemory => info.PayMemory;
[MemoryPackInclude]
string Remark => info.Remark;
[MemoryPackConstructor]
SerializableRelayServerCdkeyStoreInfo(long cdkeyid, string id, string userid, string cdKey, DateTime addTime, DateTime startTime, DateTime endTime, DateTime useTime,
List<string> nodes, double bandwidth, long maxBytes, long lastBytes, double memory, double payMemory, string remark)
{
var info = new RelayServerCdkeyStoreInfo
{
CdkeyId = cdkeyid,
Id = id,
UserId = userid,
CdKey = cdKey,
AddTime = addTime,
StartTime = startTime,
EndTime = endTime,
UseTime = useTime,
Nodes = nodes,
Bandwidth = bandwidth,
MaxBytes = maxBytes,
LastBytes = lastBytes,
Memory = memory,
PayMemory = payMemory,
Remark = remark
};
this.info = info;
}
public SerializableRelayServerCdkeyStoreInfo(RelayServerCdkeyStoreInfo info)
{
this.info = info;
}
}
public class RelayServerCdkeyStoreInfoFormatter : MemoryPackFormatter<RelayServerCdkeyStoreInfo>
{
public override void Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter> writer, scoped ref RelayServerCdkeyStoreInfo value)
{
if (value == null)
{
writer.WriteNullObjectHeader();
return;
}
writer.WritePackable(new SerializableRelayServerCdkeyStoreInfo(value));
}
public override void Deserialize(ref MemoryPackReader reader, scoped ref RelayServerCdkeyStoreInfo value)
{
if (reader.PeekIsNull())
{
reader.Advance(1); // skip null block
value = null;
return;
}
var wrapped = reader.ReadPackable<SerializableRelayServerCdkeyStoreInfo>();
value = wrapped.info;
}
}
[MemoryPackable]
public readonly partial struct SerializableRelayServerCdkeyAddInfo
@@ -549,10 +609,10 @@ namespace linker.messenger.serializer.memorypack
[MemoryPackInclude]
string SecretKey => info.SecretKey;
[MemoryPackInclude, MemoryPackAllowSerialize]
RelayServerCdkeyInfo Data => info.Data;
RelayServerCdkeyStoreInfo Data => info.Data;
[MemoryPackConstructor]
SerializableRelayServerCdkeyAddInfo(string secretKey, RelayServerCdkeyInfo data)
SerializableRelayServerCdkeyAddInfo(string secretKey, RelayServerCdkeyStoreInfo data)
{
var info = new RelayServerCdkeyAddInfo
{
@@ -604,15 +664,15 @@ namespace linker.messenger.serializer.memorypack
[MemoryPackInclude]
string SecretKey => info.SecretKey;
[MemoryPackInclude]
string Id => info.Id;
long CdkeyId => info.CdkeyId;
[MemoryPackConstructor]
SerializableRelayServerCdkeyDelInfo(string secretKey, string id)
SerializableRelayServerCdkeyDelInfo(string secretKey, long cdkeyid)
{
var info = new RelayServerCdkeyDelInfo
{
SecretKey = secretKey,
Id = id
CdkeyId = cdkeyid
};
this.info = info;
}
@@ -735,10 +795,10 @@ namespace linker.messenger.serializer.memorypack
[MemoryPackInclude]
int Count => info.Count;
[MemoryPackInclude]
List<RelayServerCdkeyInfo> List => info.List;
List<RelayServerCdkeyStoreInfo> List => info.List;
[MemoryPackConstructor]
SerializableRelayServerCdkeyPageResultInfo(int page, int size, int count, List<RelayServerCdkeyInfo> list)
SerializableRelayServerCdkeyPageResultInfo(int page, int size, int count, List<RelayServerCdkeyStoreInfo> list)
{
var info = new RelayServerCdkeyPageResultInfo
{
@@ -788,35 +848,35 @@ namespace linker.messenger.serializer.memorypack
public readonly partial struct SerializableRelayTrafficReportInfo
{
[MemoryPackIgnore]
public readonly RelayTrafficReportInfo info;
public readonly RelayTrafficUpdateInfo info;
[MemoryPackInclude]
Dictionary<string, ulong> Id2Bytes => info.Id2Bytes;
Dictionary<long, long> Dic => info.Dic;
[MemoryPackInclude]
List<string> UpdateIds => info.UpdateIds;
List<long> Ids => info.Ids;
[MemoryPackInclude]
string SecretKey => info.SecretKey;
[MemoryPackConstructor]
SerializableRelayTrafficReportInfo(Dictionary<string, ulong> id2Bytes, List<string> updateIds, string secretKey)
SerializableRelayTrafficReportInfo(Dictionary<long, long> dic, List<long> ids, string secretKey)
{
var info = new RelayTrafficReportInfo
var info = new RelayTrafficUpdateInfo
{
Id2Bytes = id2Bytes,
UpdateIds = updateIds,
Dic = dic,
Ids = ids,
SecretKey = secretKey
};
this.info = info;
}
public SerializableRelayTrafficReportInfo(RelayTrafficReportInfo info)
public SerializableRelayTrafficReportInfo(RelayTrafficUpdateInfo info)
{
this.info = info;
}
}
public class RelayTrafficReportInfoFormatter : MemoryPackFormatter<RelayTrafficReportInfo>
public class RelayTrafficReportInfoFormatter : MemoryPackFormatter<RelayTrafficUpdateInfo>
{
public override void Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter> writer, scoped ref RelayTrafficReportInfo value)
public override void Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter> writer, scoped ref RelayTrafficUpdateInfo value)
{
if (value == null)
{
@@ -827,7 +887,7 @@ namespace linker.messenger.serializer.memorypack
writer.WritePackable(new SerializableRelayTrafficReportInfo(value));
}
public override void Deserialize(ref MemoryPackReader reader, scoped ref RelayTrafficReportInfo value)
public override void Deserialize(ref MemoryPackReader reader, scoped ref RelayTrafficUpdateInfo value)
{
if (reader.PeekIsNull())
{

View File

@@ -11,7 +11,7 @@ namespace linker.messenger.serializer.memorypack
public readonly SForwardInfo info;
[MemoryPackInclude]
uint Id => info.Id;
long Id => info.Id;
[MemoryPackInclude]
string Name => info.Name;
@@ -44,7 +44,7 @@ namespace linker.messenger.serializer.memorypack
int RemotePortMax => info.RemotePortMax;
[MemoryPackConstructor]
SerializableSForwardInfo(uint id, string name, string domain, int remotePort, byte bufferSize, IPEndPoint localEP, bool started, string msg, string localMsg, int remotePortMin, int remotePortMax)
SerializableSForwardInfo(long id, string name, string domain, int remotePort, byte bufferSize, IPEndPoint localEP, bool started, string msg, string localMsg, int remotePortMin, int remotePortMax)
{
this.info = new SForwardInfo
{
@@ -277,10 +277,10 @@ namespace linker.messenger.serializer.memorypack
string MachineId => info.MachineId;
[MemoryPackInclude, MemoryPackAllowSerialize]
uint Id => info.Id;
long Id => info.Id;
[MemoryPackConstructor]
SerializableSForwardRemoveForwardInfo(string machineId, uint id)
SerializableSForwardRemoveForwardInfo(string machineId, long id)
{
this.info = new SForwardRemoveForwardInfo
{

View File

@@ -10,7 +10,7 @@ namespace linker.messenger.sforward
/// <summary>
/// 穿透id
/// </summary>
public uint Id { get; set; }
public long Id { get; set; }
/// <summary>
/// 名称
/// </summary>
@@ -125,7 +125,7 @@ namespace linker.messenger.sforward
public sealed partial class SForwardRemoveForwardInfo
{
public string MachineId { get; set; }
public uint Id { get; set; }
public long Id { get; set; }
}
/// <summary>

View File

@@ -30,7 +30,7 @@
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public SForwardInfo Get(uint id);
public SForwardInfo Get(long id);
/// <summary>
/// 获取穿透
/// </summary>
@@ -60,7 +60,7 @@
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public bool Remove(uint id);
public bool Remove(long id);
/// <summary>
/// 提交保存
/// </summary>

View File

@@ -16,9 +16,6 @@ namespace linker.messenger.sforward.client
private readonly ISForwardClientStore sForwardClientStore;
private readonly ISerializer serializer;
private readonly NumberSpaceUInt32 ns = new NumberSpaceUInt32();
public SForwardClientTransfer(SignInClientState signInClientState, IMessengerSender messengerSender, ISignInClientStore signInClientStore, ISForwardClientStore sForwardClientStore, ISerializer serializer)
{
this.signInClientState = signInClientState;
@@ -33,8 +30,6 @@ namespace linker.messenger.sforward.client
private void Start()
{
var list = sForwardClientStore.Get();
uint maxid = list.Count > 0 ? list.Max(c => c.Id) : 1;
ns.Reset(maxid);
foreach (var item in list)
{
@@ -167,7 +162,6 @@ namespace linker.messenger.sforward.client
}
else
{
forwardInfo.Id = ns.Increment();
if (PortRange(forwardInfo.Domain, out int min, out int max))
{
forwardInfo.RemotePortMin = min;
@@ -183,7 +177,7 @@ namespace linker.messenger.sforward.client
return true;
}
public bool Remove(uint id)
public bool Remove(long id)
{
//同名或者同端口但是ID不一样
SForwardInfo old = sForwardClientStore.Get(id);

View File

@@ -14,10 +14,10 @@ namespace linker.plugins.sforward.proxy
}
public virtual void AddReceive(string key,string groupid, ulong bytes)
public virtual void AddReceive(string key,string groupid, long bytes)
{
}
public virtual void AddSendt(string key,string groupid, ulong bytes)
public virtual void AddSendt(string key,string groupid, long bytes)
{
}

View File

@@ -281,13 +281,13 @@ namespace linker.plugins.sforward.proxy
{
if (isDomain)
{
AddReceive(domain, groupid, (ulong)bytesRead);
AddSendt(domain, groupid, (ulong)bytesRead);
AddReceive(domain, groupid, bytesRead);
AddSendt(domain, groupid, bytesRead);
}
else
{
AddReceive(portStr, groupid, (ulong)bytesRead);
AddSendt(portStr, groupid, (ulong)bytesRead);
AddReceive(portStr, groupid, bytesRead);
AddSendt(portStr, groupid, bytesRead);
}
await target.SendAsync(buffer.Slice(0, bytesRead), SocketFlags.None).ConfigureAwait(false);
}

View File

@@ -59,13 +59,13 @@ namespace linker.plugins.sforward.proxy
Memory<byte> memory = buffer.AsMemory(0, result.ReceivedBytes);
AddReceive(portStr, token.GroupId, (ulong)memory.Length);
AddReceive(portStr, token.GroupId, memory.Length);
IPEndPoint source = result.RemoteEndPoint as IPEndPoint;
//已经连接
if (udpConnections.TryGetValue(source, out UdpTargetCache cache) && cache != null)
{
AddSendt(portStr, token.GroupId, (ulong)memory.Length);
AddSendt(portStr, token.GroupId, memory.Length);
cache.LastTicks.Update();
await token.SourceSocket.SendToAsync(memory, cache.IPEndPoint).ConfigureAwait(false);
}
@@ -197,8 +197,8 @@ namespace linker.plugins.sforward.proxy
cache.LastTicks.Update();
Memory<byte> memory = buffer.AsMemory(0, result.ReceivedBytes);
AddReceive(portStr, string.Empty, (ulong)memory.Length);
AddSendt(portStr, string.Empty, (ulong)memory.Length);
AddReceive(portStr, string.Empty, memory.Length);
AddSendt(portStr, string.Empty, memory.Length);
//未连接本地服务的,去连接一下
if (serviceUdp == null)
{
@@ -239,8 +239,8 @@ namespace linker.plugins.sforward.proxy
break;
}
Memory<byte> memory = buffer.AsMemory(0, result.ReceivedBytes);
AddReceive(portStr, string.Empty, (ulong)memory.Length);
AddSendt(portStr, string.Empty, (ulong)memory.Length);
AddReceive(portStr, string.Empty, memory.Length);
AddSendt(portStr, string.Empty, memory.Length);
await serverUdp.SendToAsync(memory, server).ConfigureAwait(false);
cache.LastTicks.Update();
@@ -261,7 +261,7 @@ namespace linker.plugins.sforward.proxy
private void UdpTask()
{
TimerHelper.SetInterval(() =>
TimerHelper.SetIntervalLong(() =>
{
var connections = udpConnections.Where(c => c.Value.Timeout).Select(c => c.Key);
foreach (var item in connections)

View File

@@ -34,7 +34,7 @@ namespace linker.messenger.signin
/// </summary>
public void SignInTask()
{
TimerHelper.SetInterval(async () =>
TimerHelper.SetIntervalLong(async () =>
{
if (clientSignInState.Connected == false)
{
@@ -49,7 +49,7 @@ namespace linker.messenger.signin
}
}
return true;
}, () => 10000);
}, 10000);
}
public void ReSignIn()

View File

@@ -121,7 +121,7 @@ namespace linker.messenger.signin
private void ClearTask()
{
TimerHelper.SetInterval(() =>
TimerHelper.SetIntervalLong(() =>
{
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{

View File

@@ -73,7 +73,7 @@ namespace linker.messenger.socks5
}
CloseClientSocket(token);
}
private async Task SendToConnection(AsyncUserUdpToken token)
{
if (token.Connection == null) return;
@@ -96,7 +96,7 @@ namespace linker.messenger.socks5
token.Proxy.Return(connectData);
}
}
private async Task SendToConnections(AsyncUserUdpToken token)
{
byte[] connectData = token.Proxy.ToBytes(out int length);
@@ -242,7 +242,7 @@ namespace linker.messenger.socks5
private void TaskUdp()
{
TimerHelper.SetInterval(() =>
TimerHelper.SetIntervalLong(() =>
{
var connections = udpConnections.Where(c => c.Value.Timeout).Select(c => c.Key).ToList();
foreach (var item in connections)
@@ -259,7 +259,7 @@ namespace linker.messenger.socks5
}
}
return true;
}, () => 3 * 60 * 1000);
}, 3 * 60 * 1000);
}
private void CloseClientSocketUdp(ITunnelConnection connection)

View File

@@ -32,6 +32,8 @@ using linker.messenger.tuntap.lease;
using linker.messenger.updater;
using linker.plugins.tunnel;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Yitter.IdGenerator;
namespace linker.messenger.store.file
{
public static class Entry
@@ -40,6 +42,8 @@ namespace linker.messenger.store.file
{
LoggerHelper.Instance.Info("add store file");
YitIdHelper.SetIdGenerator(new IdGeneratorOptions(1));
serviceCollection.AddSingleton<Storefactory>();
serviceCollection.AddSingleton<FileConfig>();
serviceCollection.AddSingleton<RunningConfig>();

View File

@@ -127,7 +127,7 @@ namespace linker.messenger.store.file
}
private void SaveTask()
{
TimerHelper.SetInterval(() =>
TimerHelper.SetIntervalLong(() =>
{
while (Data.Updated > 0)
{

View File

@@ -45,7 +45,7 @@ namespace linker.messenger.store.file
}
private void SaveTask()
{
TimerHelper.SetInterval(() =>
TimerHelper.SetIntervalLong(() =>
{
while (Data.Updated > 0)
{

View File

@@ -34,7 +34,7 @@ namespace linker.messenger.store.file
private void CheckpointTask()
{
TimerHelper.SetInterval(() =>
TimerHelper.SetIntervalLong(() =>
{
database.Checkpoint();
return true;

View File

@@ -1,4 +1,5 @@
using linker.messenger.forward;
using Yitter.IdGenerator;
namespace linker.messenger.store.file.forward
{
@@ -23,7 +24,7 @@ namespace linker.messenger.store.file.forward
return runningConfig.Data.Forwards;
}
public ForwardInfo Get(uint id)
public ForwardInfo Get(long id)
{
return runningConfig.Data.Forwards.FirstOrDefault(x => x.Id == id);
}
@@ -35,6 +36,11 @@ namespace linker.messenger.store.file.forward
public bool Add(ForwardInfo info)
{
if(info.Id == 0)
{
info.Id = YitIdHelper.NextId();
}
runningConfig.Data.Forwards.Add(info);
return true;
}
@@ -42,7 +48,7 @@ namespace linker.messenger.store.file.forward
{
return true;
}
public bool Remove(uint id)
public bool Remove(long id)
{
runningConfig.Data.Forwards.Remove(Get(id));
return true;

View File

@@ -38,6 +38,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="LiteDB" Version="5.0.17" />
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\linker.libs\linker.libs.csproj" />

View File

@@ -8,6 +8,7 @@ namespace linker.messenger.store.file
public sealed partial class RunningConfigInfo
{
public RelayInfo Relay { get; set; } = new RelayInfo();
}
public sealed class RelayInfo
@@ -43,6 +44,8 @@ namespace linker.messenger.store.file
#else
public string SecretKey { get; set; } = Guid.NewGuid().ToString().ToUpper();
#endif
public RelayServerCdkeyConfigInfo Cdkey { get; set; } = new RelayServerCdkeyConfigInfo();
public DistributedInfo Distributed { get; set; } = new DistributedInfo { };
}

View File

@@ -1,23 +1,29 @@
using linker.messenger.relay.server;
using LiteDB;
using Yitter.IdGenerator;
namespace linker.messenger.store.file.relay
{
public sealed class RelayServerCdkeyStore : IRelayServerCdkeyStore
{
private readonly Storefactory dBfactory;
private readonly ILiteCollection<RelayServerCdkeyInfo> liteCollection;
private readonly ILiteCollection<RelayServerCdkeyStoreInfo> liteCollection;
public RelayServerCdkeyStore(Storefactory dBfactory, FileConfig fileConfig)
{
this.dBfactory = dBfactory;
liteCollection = dBfactory.GetCollection<RelayServerCdkeyInfo>("relayCdkey");
liteCollection = dBfactory.GetCollection<RelayServerCdkeyStoreInfo>("relayCdkey");
}
public async Task<bool> Add(RelayServerCdkeyInfo info)
public async Task<bool> Add(RelayServerCdkeyStoreInfo info)
{
if (string.IsNullOrWhiteSpace(info.Id))
{
info.Id = ObjectId.NewObjectId().ToString();
info.CdKey = Guid.NewGuid().ToString().ToUpper();
info.AddTime = DateTime.Now;
info.UseTime = DateTime.Now;
info.LastBytes = info.MaxBytes;
info.CdkeyId = YitIdHelper.NextId();
liteCollection.Insert(info);
}
else
@@ -26,40 +32,64 @@ namespace linker.messenger.store.file.relay
}
return await Task.FromResult(true);
}
public async Task<bool> Del(string id)
public async Task<bool> Del(long id)
{
return await Task.FromResult(liteCollection.Delete(id));
return await Task.FromResult(liteCollection.DeleteMany(c => c.CdkeyId == id) > 0);
}
public async Task<List<RelayServerCdkeyInfo>> Get(string userid)
public async Task<bool> Traffic(Dictionary<long, long> dic)
{
foreach (var item in dic)
{
var info = liteCollection.FindOne(x => x.CdkeyId == item.Key);
if (info != null)
{
long bytes = info.LastBytes >= item.Value ? info.LastBytes - item.Value : 0;
liteCollection.UpdateMany(x => new RelayServerCdkeyStoreInfo { LastBytes = bytes, UseTime = DateTime.Now }, c => c.CdkeyId == item.Key);
}
}
return await Task.FromResult(true);
}
public async Task<List<RelayServerCdkeyStoreInfo>> GetAvailable(string userid)
{
return await Task.FromResult(liteCollection.Find(x => x.UserId == userid && x.LastBytes > 0 && x.StartTime <= DateTime.Now && x.EndTime < DateTime.Now).ToList());
}
public async Task<RelayServerCdkeyPageResultInfo> Get(RelayServerCdkeyPageRequestInfo relayServerCdkeyPageRequestInfo)
public async Task<List<RelayServerCdkeyStoreInfo>> Get(List<long> ids)
{
ILiteQueryable<RelayServerCdkeyInfo> query = liteCollection.Query();
return await Task.FromResult(liteCollection.Find(x => ids.Contains(x.CdkeyId)).ToList());
}
if (string.IsNullOrWhiteSpace(relayServerCdkeyPageRequestInfo.Order) == false)
public async Task<RelayServerCdkeyPageResultInfo> Get(RelayServerCdkeyPageRequestInfo info)
{
ILiteQueryable<RelayServerCdkeyStoreInfo> query = liteCollection.Query();
if (string.IsNullOrWhiteSpace(info.Order) == false)
{
query = query.OrderBy(relayServerCdkeyPageRequestInfo.Order, relayServerCdkeyPageRequestInfo.Sort == "asc" ? Query.Ascending : Query.Descending);
query = query.OrderBy(info.Order, info.Sort == "asc" ? Query.Ascending : Query.Descending);
}
if (string.IsNullOrWhiteSpace(relayServerCdkeyPageRequestInfo.UserId) == false)
else
{
query = query.Where(x => x.UserId == relayServerCdkeyPageRequestInfo.UserId);
query = query.OrderBy(c => c.CdkeyId, Query.Descending);
}
if (string.IsNullOrWhiteSpace(relayServerCdkeyPageRequestInfo.Remark) == false)
if (string.IsNullOrWhiteSpace(info.UserId) == false)
{
query = query.Where(x => x.Remark.Contains(relayServerCdkeyPageRequestInfo.Remark));
query = query.Where(x => x.UserId == info.UserId);
}
if (string.IsNullOrWhiteSpace(info.Remark) == false)
{
query = query.Where(x => x.Remark.Contains(info.Remark));
}
return await Task.FromResult(new RelayServerCdkeyPageResultInfo
{
Page = relayServerCdkeyPageRequestInfo.Page,
Size = relayServerCdkeyPageRequestInfo.Size,
Page = info.Page,
Size = info.Size,
Count = query.Count(),
List = query.Skip((relayServerCdkeyPageRequestInfo.Page - 1) * relayServerCdkeyPageRequestInfo.Size).Limit(relayServerCdkeyPageRequestInfo.Size).ToList()
List = query.Skip((info.Page - 1) * info.Size).Limit(info.Size).ToList()
});
}
}
}

View File

@@ -22,7 +22,7 @@ namespace linker.messenger.store.file.relay
{
config.Data.Server.Relay.Distributed.Node = node;
}
public void SetMaxGbTotalLastBytes(ulong value)
public void SetMaxGbTotalLastBytes(long value)
{
config.Data.Server.Relay.Distributed.Node.MaxGbTotalLastBytes=value;
}

View File

@@ -1,5 +1,6 @@
using linker.messenger.sforward;
using linker.messenger.sforward.client;
using Yitter.IdGenerator;
namespace linker.messenger.store.file.sforward
{
@@ -36,7 +37,7 @@ namespace linker.messenger.store.file.sforward
return runningConfig.Data.SForwards;
}
public SForwardInfo Get(uint id)
public SForwardInfo Get(long id)
{
return runningConfig.Data.SForwards.FirstOrDefault(x => x.Id == id);
}
@@ -53,6 +54,10 @@ namespace linker.messenger.store.file.sforward
public bool Add(SForwardInfo info)
{
if (info.Id == 0)
{
info.Id = YitIdHelper.NextId();
}
runningConfig.Data.SForwards.Add(info);
return true;
}
@@ -60,7 +65,7 @@ namespace linker.messenger.store.file.sforward
{
return true;
}
public bool Remove(uint id)
public bool Remove(long id)
{
runningConfig.Data.SForwards.Remove(Get(id));
return true;

View File

@@ -14,8 +14,8 @@ namespace linker.messenger.tunnel
{
public byte Type => (byte)ResolverType.External;
public virtual void AddReceive( ulong bytes) { }
public virtual void AddSendt(ulong bytes) { }
public virtual void AddReceive( long bytes) { }
public virtual void AddSendt(long bytes) { }
/// <summary>
/// UDP
@@ -28,12 +28,12 @@ namespace linker.messenger.tunnel
{
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG) LoggerHelper.Instance.Debug($"{ep} get udp external port");
AddReceive((ulong)memory.Length);
AddReceive(memory.Length);
byte[] sendData = ArrayPool<byte>.Shared.Rent(1024);
try
{
var send = BuildSendData(sendData, ep);
AddSendt((ulong)send.Length);
AddSendt(send.Length);
await socket.SendToAsync(send, SocketFlags.None, ep).ConfigureAwait(false);
}
catch (Exception ex)
@@ -57,7 +57,7 @@ namespace linker.messenger.tunnel
try
{
memory = BuildSendData(sendData, socket.RemoteEndPoint as IPEndPoint);
AddSendt((ulong)memory.Length);
AddSendt(memory.Length);
await socket.SendAsync(memory, SocketFlags.None).ConfigureAwait(false);
}
catch (Exception ex)

View File

@@ -80,11 +80,11 @@ namespace linker.messenger.tuntap
private OperatingManager checking = new OperatingManager();
private void CheckDeviceTask()
{
TimerHelper.SetInterval(async () =>
TimerHelper.SetIntervalLong(async () =>
{
await CheckDevice();
return true;
}, () => 30000);
}, 30000);
}
private async Task CheckDevice()
{

View File

@@ -35,7 +35,7 @@ namespace linker.messenger.tuntap
}
private void PingTask()
{
TimerHelper.SetInterval(async () =>
TimerHelper.SetIntervalLong(async () =>
{
if (tuntapTransfer.Status == TuntapStatus.Running)
{

View File

@@ -73,7 +73,7 @@ namespace linker.messenger.tuntap.lease
private void LeaseExpTask()
{
TimerHelper.SetInterval(async () =>
TimerHelper.SetIntervalLong(async () =>
{
await messengerSender.SendReply(new MessageRequestWrap
{
@@ -82,7 +82,7 @@ namespace linker.messenger.tuntap.lease
});
return true;
}, () => 60000);
}, 60000);
}
}
}

View File

@@ -219,7 +219,7 @@ namespace linker.messenger.tuntap.lease
private void ClearTask()
{
TimerHelper.SetInterval(() =>
TimerHelper.SetIntervalLong(() =>
{
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{

View File

@@ -89,7 +89,7 @@ namespace linker.messenger.updater
}
private void UpdateTask()
{
TimerHelper.SetInterval(async () =>
TimerHelper.SetIntervalLong(async () =>
{
await GetUpdateInfo();
if (updateInfo.Updated)

View File

@@ -15,7 +15,7 @@ namespace linker.messenger
public interface IMessengerResolver
{
public Task<IConnection> BeginReceiveClient(Socket socket);
public Task<IConnection> BeginReceiveClient(Socket socket, bool sendFlag, byte flag, byte[] data);
public Task<IConnection> BeginReceiveClient(Socket socket, bool sendFlag, byte flag, Memory<byte> data);
public void AddMessenger(List<IMessenger> list);
public Task BeginReceiveServer(Socket socket, Memory<byte> memory);
public Task BeginReceiveServer(Socket socket, IPEndPoint ep, Memory<byte> memory);
@@ -59,8 +59,8 @@ namespace linker.messenger
}
public virtual void AddReceive(ushort id, ulong bytes) { }
public virtual void AddSendt(ushort id, ulong bytes) { }
public virtual void AddReceive(ushort id, long bytes) { }
public virtual void AddSendt(ushort id, long bytes) { }
/// <summary>
/// 以服务器模式接收数据 TCP
@@ -113,7 +113,7 @@ namespace linker.messenger
/// <param name="sendFlag"></param>
/// <param name="flag"></param>
/// <returns></returns>
public async Task<IConnection> BeginReceiveClient(Socket socket, bool sendFlag, byte flag, byte[] data)
public async Task<IConnection> BeginReceiveClient(Socket socket, bool sendFlag, byte flag, Memory<byte> data)
{
try
{
@@ -230,7 +230,7 @@ namespace linker.messenger
//新的请求
requestWrap.FromArray(data);
AddReceive(requestWrap.MessengerId, (ulong)data.Length);
AddReceive(requestWrap.MessengerId, data.Length);
//404,没这个插件
if (messengers.TryGetValue(requestWrap.MessengerId, out MessengerCacheInfo plugin) == false)
{

View File

@@ -47,8 +47,8 @@ namespace linker.messenger
{
}
public virtual void AddReceive(ushort id, ulong bytes) { }
public virtual void AddSendt(ushort id, ulong bytes) { }
public virtual void AddReceive(ushort id, long bytes) { }
public virtual void AddSendt(ushort id, long bytes) { }
public async Task<MessageResponeInfo> SendReply(MessageRequestWrap msg)
{
if (msg.Connection == null || msg.Connection.Connected == false)
@@ -105,7 +105,7 @@ namespace linker.messenger
byte[] bytes = msg.ToArray(out int length);
AddSendt(msg.MessengerId, (ulong)bytes.Length);
AddSendt(msg.MessengerId, bytes.Length);
bool res = await msg.Connection.SendAsync(bytes.AsMemory(0, length)).ConfigureAwait(false);
msg.Return(bytes);
@@ -130,7 +130,7 @@ namespace linker.messenger
byte[] bytes = msg.ToArray(out int length);
AddSendt(messengerId, (ulong)length);
AddSendt(messengerId, length);
bool res = await msg.Connection.SendAsync(bytes.AsMemory(0, length)).ConfigureAwait(false);
msg.Return(bytes);
@@ -151,7 +151,7 @@ namespace linker.messenger
byte[] bytes = new byte[wrap.Payload.Length];
wrap.Payload.CopyTo(bytes);
AddReceive(info.MessengerId, (ulong)bytes.Length);
AddReceive(info.MessengerId, bytes.Length);
info.Tcs.SetResult(new MessageResponeInfo { Code = wrap.Code, Data = bytes, Connection = wrap.Connection });
}
}

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<project ver="10" name="linker.tray.win" libEmbed="true" icon="..\linker\favicon.ico" ui="win" output="linker.tray.win.exe" CompanyName="snltty" FileDescription="linker.tray.win" LegalCopyright="Copyright (C) snltty 2024" ProductName="linker.tray.win" InternalName="linker.install.win" FileVersion="0.0.0.213" ProductVersion="0.0.0.213" publishDir="/dist/" dstrip="false" local="false" ignored="false">
<project ver="10" name="linker.tray.win" libEmbed="true" icon="..\linker\favicon.ico" ui="win" output="linker.tray.win.exe" CompanyName="snltty" FileDescription="linker.tray.win" LegalCopyright="Copyright (C) snltty 2024" ProductName="linker.tray.win" InternalName="linker.install.win" FileVersion="0.0.0.214" ProductVersion="0.0.0.214" publishDir="/dist/" dstrip="false" local="false" ignored="false">
<file name="main.aardio" path="main.aardio" comment="main.aardio"/>
<folder name="资源文件" path="res" embed="true" local="false" ignored="false">
<file name="favicon.ico" path="res\favicon.ico" comment="res\favicon.ico"/>

Binary file not shown.

View File

@@ -34,7 +34,7 @@ namespace linker.tunnel
private void LoopDiscovery()
{
TimerHelper.SetInterval(() =>
TimerHelper.SetIntervalLong(() =>
{
NatUtility.StopDiscovery();
NatUtility.StartDiscovery();

View File

@@ -164,7 +164,8 @@ namespace linker.tunnel.transport
{
targetSocket.KeepAlive();
targetSocket.ReuseBind(new IPEndPoint(ep.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, tunnelTransportInfo.Local.Local.Port));
targetSocket.IPv6Only(ep.AddressFamily, false);
targetSocket.ReuseBind(new IPEndPoint(ep.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, tunnelTransportInfo.Local.Local.Port));
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
@@ -218,7 +219,8 @@ namespace linker.tunnel.transport
try
{
targetSocket.Ttl = ip.Address.AddressFamily == AddressFamily.InterNetworkV6 ? (short)2 : (short)(tunnelTransportInfo.Local.RouteLevel);
targetSocket.ReuseBind(new IPEndPoint(ip.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, tunnelTransportInfo.Local.Local.Port));
targetSocket.IPv6Only(IPAddress.IPv6Any.AddressFamily, false);
targetSocket.ReuseBind(new IPEndPoint(IPAddress.IPv6Any, tunnelTransportInfo.Local.Local.Port));
_ = targetSocket.ConnectAsync(ip);
return targetSocket;
}
@@ -315,15 +317,10 @@ namespace linker.tunnel.transport
private async Task StartListen(IPEndPoint local, TunnelTransportInfo tunnelTransportInfo)
{
IPAddress localIP = tunnelTransportInfo.Local.LocalIps.Any(c => c.AddressFamily == AddressFamily.InterNetworkV6)
&& tunnelTransportInfo.Remote.LocalIps.Any(c => c.AddressFamily == AddressFamily.InterNetworkV6)
? IPAddress.IPv6Any : IPAddress.Any;
IPAddress localIP = IPAddress.IPv6Any;
Socket socket = new Socket(localIP.AddressFamily, SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
//socket.ReceiveBufferSize = 5 * 1024 * 1024;
//socket.IPv6Only(localIP.AddressFamily, false);
socket.Bind(new IPEndPoint(localIP, local.Port));
socket.IPv6Only(localIP.AddressFamily, false);
socket.ReuseBind(new IPEndPoint(localIP, local.Port));
socket.Listen(int.MaxValue);
try

View File

@@ -119,15 +119,16 @@ namespace linker.tunnel.transport
LoggerHelper.Instance.Warning($"{Name} connect to {tunnelTransportInfo.Remote.MachineId}->{tunnelTransportInfo.Remote.MachineName} {string.Join("\r\n", tunnelTransportInfo.RemoteEndPoints.Select(c => c.ToString()))}");
}
IPEndPoint ep = tunnelTransportInfo.Remote.LocalIps.Any(c=>c.AddressFamily == AddressFamily.InterNetworkV6)
IPEndPoint ep = tunnelTransportInfo.Remote.LocalIps.Any(c => c.AddressFamily == AddressFamily.InterNetworkV6)
&& tunnelTransportInfo.Local.LocalIps.Any(c => c.AddressFamily == AddressFamily.InterNetworkV6)
? new IPEndPoint(tunnelTransportInfo.Remote.LocalIps.FirstOrDefault(c=>c.AddressFamily == AddressFamily.InterNetworkV6), tunnelTransportInfo.Remote.Remote.Port)
: tunnelTransportInfo.Remote.Remote;
? new IPEndPoint(tunnelTransportInfo.Remote.LocalIps.FirstOrDefault(c => c.AddressFamily == AddressFamily.InterNetworkV6), tunnelTransportInfo.Remote.Remote.Port)
: tunnelTransportInfo.Remote.Remote;
Socket targetSocket = new(ep.AddressFamily, SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
try
{
targetSocket.KeepAlive();
targetSocket.ReuseBind(new IPEndPoint(ep.AddressFamily == AddressFamily.InterNetwork ? tunnelTransportInfo.Local.Local.Address : IPAddress.IPv6Any, tunnelTransportInfo.Local.Local.Port));
targetSocket.IPv6Only(ep.AddressFamily, false);
targetSocket.ReuseBind(new IPEndPoint(ep.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, tunnelTransportInfo.Local.Local.Port));
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{

View File

@@ -314,7 +314,8 @@ namespace linker.tunnel.transport
try
{
targetSocket.KeepAlive();
targetSocket.ReuseBind(new IPEndPoint(ep.AddressFamily == AddressFamily.InterNetwork ? tunnelTransportInfo.Local.Local.Address : IPAddress.IPv6Any, tunnelTransportInfo.Local.Local.Port));
targetSocket.IPv6Only(ep.AddressFamily, false);
targetSocket.ReuseBind(new IPEndPoint(ep.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, tunnelTransportInfo.Local.Local.Port));
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{

View File

@@ -147,10 +147,9 @@ namespace linker.tunnel.transport
LoggerHelper.Instance.Warning($"{Name} connect to {tunnelTransportInfo.Remote.MachineId}->{tunnelTransportInfo.Remote.MachineName} {string.Join("\r\n", tunnelTransportInfo.RemoteEndPoints.Select(c => c.ToString()))}");
}
IPEndPoint local = new IPEndPoint(tunnelTransportInfo.Local.Local.Address, tunnelTransportInfo.Local.Local.Port);
TaskCompletionSource<IPEndPoint> taskCompletionSource = new TaskCompletionSource<IPEndPoint>(TaskCreationOptions.RunContinuationsAsynchronously);
//监听连接
Socket remoteUdp = BindListen(local, taskCompletionSource);
Socket remoteUdp = BindListen(tunnelTransportInfo.Local.Local, taskCompletionSource);
//给对方发送简单消息
foreach (IPEndPoint ep in tunnelTransportInfo.RemoteEndPoints)
@@ -216,7 +215,9 @@ namespace linker.tunnel.transport
/// <returns></returns>
private Socket BindListen(IPEndPoint local, TaskCompletionSource<IPEndPoint> tcs)
{
local = new IPEndPoint(IPAddress.IPv6Any, local.Port);
Socket socket = new Socket(local.AddressFamily, SocketType.Dgram, System.Net.Sockets.ProtocolType.Udp);
socket.IPv6Only(local.AddressFamily, false);
socket.WindowsUdpBug();
socket.ReuseBind(local);
@@ -238,9 +239,11 @@ namespace linker.tunnel.transport
/// <returns></returns>
private async Task BindListen(IPEndPoint local, TunnelTransportInfo state)
{
local = new IPEndPoint(IPAddress.IPv6Any,local.Port);
Socket socket = new Socket(local.AddressFamily, SocketType.Dgram, System.Net.Sockets.ProtocolType.Udp);
try
{
socket.IPv6Only(local.AddressFamily,false);
socket.ReuseBind(local);
socket.WindowsUdpBug();
ListenAsyncToken token = new ListenAsyncToken
@@ -267,7 +270,7 @@ namespace linker.tunnel.transport
try
{
byte[] buffer = new byte[8 * 1024];
IPEndPoint ep = new IPEndPoint(IPAddress.Any, 0);
IPEndPoint ep = new IPEndPoint(IPAddress.IPv6Any, 0);
while (true)
{
SocketReceiveFromResult result = await token.LocalUdp.ReceiveFromAsync(buffer, ep).ConfigureAwait(false);
@@ -306,7 +309,7 @@ namespace linker.tunnel.transport
/// <param name="tunnelTransportInfo"></param>
private void BindAndTTL(TunnelTransportInfo tunnelTransportInfo)
{
IPEndPoint local = new IPEndPoint(tunnelTransportInfo.Local.Local.Address, tunnelTransportInfo.Local.Local.Port);
IPEndPoint local = new IPEndPoint(IPAddress.IPv6Any, tunnelTransportInfo.Local.Local.Port);
foreach (var ip in tunnelTransportInfo.RemoteEndPoints)
{
try
@@ -316,15 +319,13 @@ namespace linker.tunnel.transport
LoggerHelper.Instance.Warning($"{Name} ttl to {tunnelTransportInfo.Remote.MachineId}->{tunnelTransportInfo.Remote.MachineName} {ip}");
}
if (ip.AddressFamily == AddressFamily.InterNetwork)
{
Socket socket = new Socket(local.AddressFamily, SocketType.Dgram, System.Net.Sockets.ProtocolType.Udp);
socket.WindowsUdpBug();
socket.ReuseBind(local);
socket.Ttl = (short)(tunnelTransportInfo.Local.RouteLevel);
_ = socket.SendToAsync(new byte[0], SocketFlags.None, ip);
socket.SafeClose();
}
Socket socket = new Socket(local.AddressFamily, SocketType.Dgram, System.Net.Sockets.ProtocolType.Udp);
socket.IPv6Only(local.AddressFamily, false);
socket.WindowsUdpBug();
socket.ReuseBind(local);
socket.Ttl = (short)(tunnelTransportInfo.Local.RouteLevel);
_ = socket.SendToAsync(new byte[0], SocketFlags.None, ip);
socket.SafeClose();
}
catch (Exception ex)
{

View File

@@ -314,7 +314,8 @@ namespace linker.tunnel.transport
try
{
targetSocket.WindowsUdpBug();
targetSocket.ReuseBind(new IPEndPoint(ep.AddressFamily == AddressFamily.InterNetwork ? tunnelTransportInfo.Local.Local.Address : IPAddress.IPv6Any, tunnelTransportInfo.Local.Local.Port));
targetSocket.IPv6Only(ep.AddressFamily, false);
targetSocket.ReuseBind(new IPEndPoint(ep.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any : IPAddress.IPv6Any, tunnelTransportInfo.Local.Local.Port));
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
@@ -322,7 +323,7 @@ namespace linker.tunnel.transport
}
await targetSocket.SendToAsync($"{flagTexts}-{tunnelTransportInfo.Local.MachineId}-{tunnelTransportInfo.FlowId}".ToBytes(), ep).ConfigureAwait(false);
await targetSocket.ReceiveFromAsync(new byte[1024], new IPEndPoint(ep.AddressFamily == AddressFamily.InterNetwork ? IPAddress.Any: IPAddress.IPv6Any, 0)).WaitAsync(TimeSpan.FromMilliseconds(500)).ConfigureAwait(false);
await targetSocket.ReceiveFromAsync(new byte[1024], new IPEndPoint(IPAddress.IPv6Any, 0)).WaitAsync(TimeSpan.FromMilliseconds(500)).ConfigureAwait(false);
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
LoggerHelper.Instance.Debug($"{Name} connect to {tunnelTransportInfo.Remote.MachineId}->{tunnelTransportInfo.Remote.MachineName} {ep} success");
@@ -365,7 +366,7 @@ namespace linker.tunnel.transport
private void CleanTask()
{
TimerHelper.SetInterval(() =>
TimerHelper.SetIntervalLong(() =>
{
var keys = connectionsDic.Where(c => (c.Value.Connection == null && c.Value.LastTicks.DiffGreater(5000)) || (c.Value.Connection != null && c.Value.Connection.Connected == false)).Select(c => c.Key).ToList();
foreach (var item in keys)

View File

@@ -11,6 +11,7 @@
"@element-plus/icons-vue": "^2.3.1",
"core-js": "^3.38.0",
"element-plus": "^2.8.0",
"moment": "^2.30.1",
"vue": "^3.4.38",
"vue-i18n": "^11.0.1",
"vue-router": "^4.4.3"
@@ -6792,6 +6793,14 @@
"dev": true,
"license": "MIT"
},
"node_modules/moment": {
"version": "2.30.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
"engines": {
"node": "*"
}
},
"node_modules/mrmime": {
"version": "2.0.0",
"dev": true,

View File

@@ -10,6 +10,7 @@
"@element-plus/icons-vue": "^2.3.1",
"core-js": "^3.38.0",
"element-plus": "^2.8.0",
"moment": "^2.30.1",
"vue": "^3.4.38",
"vue-i18n": "^11.0.1",
"vue-router": "^4.4.3"

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1741182934817" class="icon" viewBox="0 0 1653 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9416" xmlns:xlink="http://www.w3.org/1999/xlink" width="322.8515625" height="200"><path d="M344.8620198 721.82988526a57.857089 57.857089 0 0 1-46.13274501-56.07295017v-308.4012244a59.89610558 59.89610558 0 0 1 46.13274501-56.07295017l286.35436058-62.06256065 30.07549089-122.72329718H328.80476645A210.78331632 210.78331632 0 0 0 116.61962599 327.28021902v364.47417458a213.71440233 213.71440233 0 0 0 212.18514046 212.31257895h332.48710482l-30.07549089-122.21354321zM1178.05508073 116.49690269H843.01920461l30.58524561 122.72329718 286.35436059 62.06256065a57.98452749 57.98452749 0 0 1 46.132745 56.07295017v308.4012244a60.15098257 60.15098257 0 0 1-46.13274502 56.07295017l-286.35436057 62.06256066-30.58524561 122.72329716H1178.05508073a212.44001744 212.44001744 0 0 0 212.94977139-212.82233291V327.28021902A213.33208686 213.33208686 0 0 0 1178.05508073 116.49690269z" fill="#F76E05" p-id="9417"></path><path d="M631.21638038 495.49906876h244.29964793v30.07549166H631.21638038z" fill="#F76E05" p-id="9418"></path></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1741182827482" class="icon" viewBox="0 0 1126 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7578" xmlns:xlink="http://www.w3.org/1999/xlink" width="219.921875" height="200"><path d="M344.73911 382.012725a206.3919 206.3919 0 0 0-34.79709 120.77158 181.777165 181.777165 0 0 0 50.646146 137.638871 179.607881 179.607881 0 0 0 129.448717 48.698218 305.691993 305.691993 0 0 0 86.01876-10.270897 251.504158 251.504158 0 0 0 112.581425-77.784335c2.744809 3.497417 7.481817 10.093812 14.432381 20.054812a230.519654 230.519654 0 0 0 14.78655 20.01054c3.010435 3.364604 8.145884 8.854221 15.317803 16.291768a326.587956 326.587956 0 0 0 25.057446 23.552228 28.289237 28.289237 0 0 0 29.705913-2.125013l92.128173-79.687992a17.708443 17.708443 0 0 0 8.145884-14.255297 30.547064 30.547064 0 0 0-6.153684-16.42458l-23.463686-31.211131a119.886158 119.886158 0 0 1-14.78655-31.786654 143.349844 143.349844 0 0 1-7.747444-48.698218V209.178324c0-2.656266-0.265627-11.377674-0.973964-26.031411a144.810791 144.810791 0 0 0-3.098978-28.687677q-3.320333-11.908928-6.197955-23.906398a101.159479 101.159479 0 0 0-9.164119-26.562664 175.092228 175.092228 0 0 0-13.812585-20.984505 208.428371 208.428371 0 0 0-18.859492-22.135553A258.897433 258.897433 0 0 0 601.777156 0.484326h-25.544428a320.257187 320.257187 0 0 0-159.110359 49.096657 196.38663 196.38663 0 0 0-85.398965 135.026877 30.989775 30.989775 0 0 0-1.018235 7.171919c0 9.518288 5.843786 15.671972 17.354274 18.461051l117.672602 14.343839a26.208495 26.208495 0 0 0 20.541793-23.552229 83.451037 83.451037 0 0 1 31.653842-51.177399 111.828816 111.828816 0 0 1 58.39359-21.515758h7.924528a76.589015 76.589015 0 0 1 67.557709 30.989775 182.219876 182.219876 0 0 1 14.432381 87.966689v17.354274q-45.067987 3.453146-90.047431 8.145884a515.050058 515.050058 0 0 0-136.000841 33.778854 215.069037 215.069037 0 0 0-95.18288 75.747864z m161.058287 164.245807a97.396435 97.396435 0 0 1-19.877727-64.060292 109.57099 109.57099 0 0 1 90.00316-113.511118 373.072618 373.072618 0 0 1 90.047431-8.234426v25.500158q0 32.760619-0.486982 47.148729a196.38663 196.38663 0 0 1-6.109413 37.276272 166.857802 166.857802 0 0 1-16.867291 42.455991 104.125643 104.125643 0 0 1-66.406661 55.206071 50.77896 50.77896 0 0 0-7.747443 1.062506 56.534204 56.534204 0 0 1-8.854222 0.929693 66.40666 66.40666 0 0 1-53.700852-23.729313z" fill="#221F1F" p-id="7579"></path><path d="M915.570762 811.663818a36.302308 36.302308 0 0 1 6.109413-8.190155 161.766625 161.766625 0 0 1 49.273742-23.463686 336.460413 336.460413 0 0 1 75.615051-11.244862 61.448296 61.448296 0 0 1 19.435016 1.018236c30.724148 2.833351 49.229471 7.880257 55.294612 15.494887a32.096553 32.096553 0 0 1 4.072942 18.283967v7.17192a233.264462 233.264462 0 0 1-19.435016 84.38073 198.37883 198.37883 0 0 1-54.143564 78.315588 15.317803 15.317803 0 0 1-9.296932 4.117213 8.854221 8.854221 0 0 1-4.072942-1.018236c-4.427111-2.125013-5.135448-5.843786-3.098978-11.244861a387.682083 387.682083 0 0 0 37.807526-123.959099 26.562664 26.562664 0 0 0-4.072942-16.424581c-6.81775-8.101613-25.898598-12.218826-57.331084-12.218826-11.554759 0-25.19026 0.708338-40.773689 2.125014-17.177189 2.125013-32.849161 4.117213-47.148729 6.153683a11.510488 11.510488 0 0 1-8.190155-2.125013 3.18752 3.18752 0 0 1-0.973964-4.072942 6.950564 6.950564 0 0 1 0.973964-3.054706z" fill="#FF9900" p-id="7580"></path><path d="M2.125013 794.353815q5.046906-8.190155 16.336039-1.062506a1090.043195 1090.043195 0 0 0 556.576356 148.39675 1102.350562 1102.350562 0 0 0 396.005051-74.729628l14.78655-6.153684a392.242007 392.242007 0 0 0 13.812585-6.109413 18.328238 18.328238 0 0 1 24.127754 6.109413c5.755244 8.145884 3.851586 15.716243-5.622431 22.489722-12.351639 8.854221-28.023611 19.125118-47.148729 30.679877a764.252119 764.252119 0 0 1-196.38663 80.883312 829.419188 829.419188 0 0 1-211.748705 28.643407 820.033713 820.033713 0 0 1-301.751864-55.737324 830.216068 830.216068 0 0 1-254.824492-157.073887 13.901128 13.901128 0 0 1-6.242226-10.182355 10.580795 10.580795 0 0 1 2.125013-6.153684z" fill="#FF9900" p-id="7581"></path></svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -11,4 +11,13 @@ export const relayConnect = (data) => {
}
export const relayCdkeyAccess = () => {
return sendWebsocketMsg('relay/AccessCdkey');
}
export const relayCdkeyPage = (data) => {
return sendWebsocketMsg('relay/PageCdkey', data);
}
export const relayCdkeyAdd = (data) => {
return sendWebsocketMsg('relay/AddCdkey', data);
}
export const relayCdkeyDel = (data) => {
return sendWebsocketMsg('relay/DelCdkey', data);
}

View File

@@ -11,7 +11,6 @@ export default {
'head.home': 'Home',
'head.server': 'Server',
'head.group': 'Group',
'head.protocol': 'P2P Protocol',
'head.action': 'Action',
'head.logger': 'Logs',
@@ -38,13 +37,19 @@ export default {
'server.messengerSecretKey': 'Messenger SecretKey',
'server.messengerSecretKeyText': 'Messenger SecretKey',
'server.messengerUserId': 'User Id',
'server.messengerUserIdText': 'Your unique identifier,for card key (CDKEY).',
'server.messengerUserIdText': 'Your unique identifier,used to unlock something.',
'permission.closed': 'Closed',
'permission.simple': 'Simple',
'permission.full': 'Full',
'permission.clear': 'Clear connection',
'status.group': 'Group manager',
'status.groupName': 'Name',
'status.groupPassword': 'Password',
'status.groupOper': 'Oper',
'status.groupDelConfirm': 'Are you sure to delete?',
'status.support': 'Support',
'status.export': 'Export',
'status.exportText': 'Export the configuration to run elsewhere',
@@ -126,6 +131,24 @@ export default {
'server.relayMyCdkey': 'My CDKEY',
'server.relayCdkey': 'Manager CDKEY',
'server.relayCdkeyUserId': 'UserId',
'server.relayCdkeyBandwidth': 'Bandwidth',
'server.relayCdkeyMaxBytes': 'Total',
'server.relayCdkeyLastBytes': 'Surplus',
'server.relayCdkeyMemory': 'Price',
'server.relayCdkeyPayMemory': 'Pay',
'server.relayCdkeyAddTime': 'Add',
'server.relayCdkeyStartTime': 'Start',
'server.relayCdkeyDuration': 'Duration',
'server.relayCdkeyStartTimeY': 'Year',
'server.relayCdkeyStartTimeM': 'Month',
'server.relayCdkeyEndTime': 'End',
'server.relayCdkeyUseTime': 'Use',
'server.relayCdkeyRemark': 'Remark',
'server.relayCdkeyOper': 'Oper',
'server.relayCdkeyDelConfirm': 'Are you sure to delete',
'server.sforwardSecretKey': 'Server forward secretKey',
'server.sforwardText': 'The server forward can be used when the key is correct',
@@ -139,11 +162,6 @@ export default {
'server.updaterMM': 'Minute',
'server.updaterS': 'Second',
'server.groupName': 'Name',
'server.groupPassword': 'Password',
'server.groupOper': 'Oper',
'server.groupDelConfirm': 'Are you sure to delete?',
'server.asyncText': 'Synchronize to all clients',
'server.asyncSelect': 'Please select',
'server.asyncCheckAll': 'Check all',

View File

@@ -37,13 +37,19 @@ export default {
'server.messengerSecretKey': '信标密钥',
'server.messengerSecretKeyText': '密钥正确时可连接服务器',
'server.messengerUserId': '用户id',
'server.messengerUserIdText': '你的唯一标识,用于流量卡密CDKEY',
'server.messengerUserIdText': '你的唯一标识,用于解锁一些限制',
'permission.closed': '禁止通行',
'permission.simple': '简单管理',
'permission.full': '专业管理',
'permission.clear': '清除连接',
'status.group': '管理分组',
'status.groupName': '名称',
'status.groupPassword': '密码',
'status.groupOper': '操作',
'status.groupDelConfirm': '确认删除吗?',
'status.support': '赞助',
'status.export': '导出配置',
@@ -128,6 +134,23 @@ export default {
'server.relayMyCdkey': '我的CDKEY',
'server.relayCdkey': '管理CDKEY',
'server.relayCdkeyUserId': '用户标识',
'server.relayCdkeyBandwidth': '最大带宽',
'server.relayCdkeyMaxBytes': '总流量',
'server.relayCdkeyLastBytes': '剩余流量',
'server.relayCdkeyMemory': '原价',
'server.relayCdkeyPayMemory': '支付',
'server.relayCdkeyAddTime': '添加',
'server.relayCdkeyStartTime': '开始',
'server.relayCdkeyDuration': '持续时间',
'server.relayCdkeyStartTimeY': '年',
'server.relayCdkeyStartTimeM': '月',
'server.relayCdkeyEndTime': '结束',
'server.relayCdkeyUseTime': '使用',
'server.relayCdkeyRemark': '备注',
'server.relayCdkeyOper': '操作',
'server.relayCdkeyDelConfirm': '确认删除吗?',
'server.sforwardSecretKey': '服务器穿透密钥',
'server.sforwardText': '当密钥正确是可用',
@@ -141,10 +164,7 @@ export default {
'server.updaterMM': '分',
'server.updaterS': '秒',
'server.groupName': '名称',
'server.groupPassword': '密码',
'server.groupOper': '操作',
'server.groupDelConfirm': '确认删除吗?',
'server.asyncText': '同步到所有客户端',
'server.asyncSelect': '请选择',

View File

@@ -16,11 +16,6 @@ const routes = [
name: 'FullServers',
component: () => import('@/views/full/server/Index.vue')
},
{
path: '/full/group.html',
name: 'FullGroup',
component: () => import('@/views/full/group/Index.vue')
},
{
path: '/full/transport.html',
name: 'FullTransport',

View File

@@ -14,9 +14,6 @@
<li v-if="hasConfig">
<router-link :to="{name:'FullServers'}"><img src="@/assets/fuwuqi.svg"/> {{$t('head.server')}}</router-link>
</li>
<li v-if="hasGroup">
<router-link :to="{name:'FullGroup'}"><img src="@/assets/group.svg"/> {{$t('head.group')}}</router-link>
</li>
<li v-if="hasTransport">
<router-link :to="{name:'FullTransport'}"><img src="@/assets/dadong.svg"/> {{$t('head.protocol')}}</router-link>
</li>

View File

@@ -9,11 +9,17 @@
class="system"
:src="`https://unpkg.com/flag-icons@7.2.3/flags/4x3/${tunnel.list[scope.row.MachineId].Net.CountryCode.toLowerCase()}.svg`" />
</template>
<template v-else>
<img title="?" class="system" src="/system.svg" />
</template>
<template v-if="tunnel.list[scope.row.MachineId].Net.Isp">
<img
:title="`${tunnel.list[scope.row.MachineId].Net.Isp}、${tunnel.list[scope.row.MachineId].Net.Org}、${tunnel.list[scope.row.MachineId].Net.As}`"
class="system" :src="netImg(tunnel.list[scope.row.MachineId].Net)" />
</template>
<template v-else>
<img title="?" class="system" src="/system.svg" />
</template>
</div>
<div class="flex">
<a href="javascript:;" class="a-line"
@@ -67,11 +73,16 @@ export default {
'china telecom':'chinanet.svg',
'china unicom':'chinaunicom.svg',
'china mobile':'chinamobile.svg',
'huawei':'huawei.svg',
'amazon':'amazon.svg',
'aliyun':'aliyun.svg',
}
const netImg = (item)=>{
const isp = item.Isp.toLowerCase();
const org = item.Org.toLowerCase();
const as = item.As.toLowerCase();
for(let j in imgMap){
if(as.indexOf(j) > -1){
if(isp.indexOf(j) > -1 || org.indexOf(j) > -1 || as.indexOf(j) > -1){
return `./${imgMap[j]}`;
}
}
@@ -121,7 +132,7 @@ export default {
img.system{
height:1.4rem;
vertical-align:middle;
margin-right:.4rem
border: 1px solid #eee;
}
</style>

View File

@@ -10,7 +10,7 @@
<a href="javascript:;" @click="state.show=true" class="mgl-1 delay a-line" :class="{red:state.nodes.length==0,green:state.nodes.length>0}">
{{$t('server.relayNodes')}} : {{state.nodes.length}}
</a>
<Relay></Relay>
<RelayCdkey></RelayCdkey>
</div>
</el-form-item>
@@ -68,9 +68,9 @@ import { ElMessage } from 'element-plus';
import { onMounted, onUnmounted, reactive, watch } from 'vue'
import { useI18n } from 'vue-i18n';
import Sync from '../sync/Index.vue'
import Relay from './relay/Index.vue'
import RelayCdkey from './relayCdkey/Index.vue'
export default {
components:{Sync,Relay},
components:{Sync,RelayCdkey},
setup(props) {
const {t} = useI18n();
const globalData = injectGlobalData();

View File

@@ -1,25 +1,29 @@
<template>
<a href="javascript:;" class="mgl-1 a-line">{{$t('server.relayMyCdkey')}}</a>
<a v-if="state.showManager && hasRelayCdkey" href="javascript:;" class="mgl-1 a-line">{{$t('server.relayCdkey')}}</a>
<a v-if="state.hasRelayCdkey && hasRelayCdkey" @click="state.showManager = true" href="javascript:;" class="mgl-1 a-line">{{$t('server.relayCdkey')}}</a>
<Manager v-if="state.showManager" v-model="state.showManager" />
</template>
<script>
import { relayCdkeyAccess } from '@/apis/relay';
import { injectGlobalData } from '@/provide';
import { computed, onMounted, reactive } from 'vue';
import Manager from './Manager.vue'
export default {
components:{Manager},
setup () {
const globalData = injectGlobalData();
const hasRelayCdkey = computed(()=>globalData.value.hasAccess('RelayCdkey'));
const state = reactive({
showManager:false
hasRelayCdkey:false,
showManager:false,
showList:false,
});
onMounted(()=>{
relayCdkeyAccess().then(res=>{
state.showManager = res;
state.hasRelayCdkey = res;
}).catch(()=>{})
})

View File

@@ -0,0 +1,233 @@
<template>
<el-dialog class="options-center" :title="$t('server.relayCdkey')" destroy-on-close v-model="state.show" width="77rem" top="2vh">
<div class="group-wrap">
<div class="head flex">
<div><span>用户id</span> <el-input v-model="state.page.UserId" style="width:10rem" size="small" clearable @change="handleSearch" /></div>
<div><span>备注</span> <el-input v-model="state.page.Remark" style="width:10rem" size="small" clearable @change="handleSearch" /></div>
<div>
<el-button size="small" @click="handleSearch()">
<el-icon><Search /></el-icon>
</el-button>
</div>
<div>
<el-button size="small" type="success" @click="handleAdd()">
<el-icon><Plus /></el-icon>
</el-button>
</div>
</div>
<el-table stripe :data="state.list.List" border size="small" width="100%" @cell-dblclick="handleCellClick">
<el-table-column prop="Bandwidth" :label="$t('server.relayCdkeyBandwidth')" width="110" sortable="custom">
<template #default="scope">{{ scope.row.Bandwidth }}Mbps</template>
</el-table-column>
<el-table-column prop="LastBytes" :label="`${$t('server.relayCdkeyLastBytes')}/${$t('server.relayCdkeyMaxBytes')}`" width="160" sortable="custom">
<template #default="scope">{{ parseSpeed(scope.row.LastBytes) }}/{{ parseSpeed(scope.row.MaxBytes) }}</template>
</el-table-column>
<el-table-column prop="PayMemory" :label="`${$t('server.relayCdkeyPayMemory')}/${$t('server.relayCdkeyMemory')}`" width="120" sortable="custom">
<template #default="scope">{{ scope.row.PayMemory }}/{{ scope.row.Memory }}</template>
</el-table-column>
<el-table-column prop="Remark" :label="$t('server.relayCdkeyRemark')">
</el-table-column>
<el-table-column prop="EndTime" :label="`${$t('server.relayCdkeyEndTime')}`" width="140" sortable="custom">
</el-table-column>
<el-table-column prop="UseTime" :label="`${$t('server.relayCdkeyUseTime')}`" width="140" sortable="custom">
</el-table-column>
<el-table-column prop="StartTime" :label="`${$t('server.relayCdkeyStartTime')}`" width="140" sortable="custom">
</el-table-column>
<el-table-column prop="AddTime" :label="`${$t('server.relayCdkeyAddTime')}`" width="140" sortable="custom">
</el-table-column>
<el-table-column fixed="right" prop="Oper" :label="$t('server.relayCdkeyOper')" width="60">
<template #default="scope">
<div>
<el-popconfirm :title="$t('server.relayCdkeyDelConfirm')" @confirm="handleDel(scope.row)">
<template #reference>
<el-button type="danger" size="small">
<el-icon><Delete /></el-icon>
</el-button>
</template>
</el-popconfirm>
</div>
</template>
</el-table-column>
</el-table>
<div class="t-c">
<div class="page">
<el-pagination small background layout="prev, pager, next"
:page-size="state.page.Size"
:total="state.list.Count"
:pager-count="5"
:current-page="state.page.Page" @current-change="handlePageChange" />
</div>
</div>
</div>
</el-dialog>
<el-dialog class="options-center" :title="$t('server.relayCdkey')" destroy-on-close v-model="state.showAdd" width="42rem" top="2vh">
<div>
<el-form ref="ruleFormRef" :model="state.ruleForm" :rules="state.rules" label-width="auto">
<el-form-item :label="$t('server.relayCdkeyUserId')" prop="UserId">
<el-input maxlength="32" show-word-limit v-model="state.ruleForm.UserId" />
</el-form-item>
<el-form-item :label="$t('server.relayCdkeyBandwidth')" prop="Bandwidth">
<el-input-number size="small" v-model="state.ruleForm.Bandwidth" :min="1" :max="102400" />Mbps
</el-form-item>
<el-form-item :label="$t('server.relayCdkeyMaxBytes')" prop="MaxBytes">
<el-input-number size="small" v-model="state.ruleForm.G" :min="0" :max="102400" />GB
<el-input-number size="small" v-model="state.ruleForm.M" :min="0" :max="1024" />MB
<el-input-number size="small" v-model="state.ruleForm.K" :min="0" :max="1024" />KB
<el-input-number size="small" v-model="state.ruleForm.B" :min="0" :max="1024" />B
</el-form-item>
<el-form-item></el-form-item>
<el-form-item :label="$t('server.relayCdkeyStartTime')" prop="StartTime">
<el-date-picker v-model="state.ruleForm.StartTime" type="datetime" placeholder="Select date and time"/>
</el-form-item>
<el-form-item :label="$t('server.relayCdkeyEndTime')" prop="EndTime">
<el-date-picker v-model="state.ruleForm.EndTime" type="datetime" placeholder="Select date and time"/>
</el-form-item>
<el-form-item></el-form-item>
<el-form-item :label="$t('server.relayCdkeyMemory')" prop="Memory">
<el-input-number size="small" v-model="state.ruleForm.Memory" :min="0" />
{{ $t('server.relayCdkeyPayMemory') }}
<el-input-number size="small" v-model="state.ruleForm.PayMemory" :min="0" />
</el-form-item>
<el-form-item :label="$t('server.relayCdkeyRemark')" prop="Remark">
<el-input v-model="state.ruleForm.Remark" />
</el-form-item>
<el-form-item></el-form-item>
<el-form-item label="" prop="Btns">
<div class="t-c w-100">
<el-button @click="state.showAdd = false">取消</el-button>
<el-button type="primary" @click="handleSave">确认</el-button>
</div>
</el-form-item>
</el-form>
</div>
</el-dialog>
</template>
<script>
import { injectGlobalData } from '@/provide';
import { ElMessage } from 'element-plus';
import { onMounted, reactive, ref, watch } from 'vue'
import { Delete,Plus,Search } from '@element-plus/icons-vue';
import { useI18n } from 'vue-i18n';
import moment from "moment";
import { relayCdkeyAdd,relayCdkeyDel,relayCdkeyPage } from '@/apis/relay';
export default {
props: ['modelValue'],
emits: ['update:modelValue'],
components:{Delete,Plus,Search },
setup(props,{emit}) {
const {t} = useI18n();
const globalData = injectGlobalData();
const defaultJson = {
UserId:'',
Bandwidth:1,
G:1,
M:0,
K:0,
B:0,
StartTime:new Date(),
EndTime:new Date(new Date().getFullYear() + 1,new Date().getMonth(),new Date().getDay(),new Date().getHours(),new Date().getMinutes(),new Date().getSeconds()),
Memory:0,
PayMemory:0,
Remark:'',
};
const state = reactive({
page:{
Page:1,
Size:15,
Order:'',
Sort:'',
Userid:'',
Remark:'',
},
list:{
Page:1,
Size:15,
Count:0,
List:[]
},
show:true,
showAdd:false,
ruleForm:JSON.parse(JSON.stringify(defaultJson)),
rules:{
UserId: [{ required: true, message: "required", trigger: "blur" }],
Remark: [{ required: true, message: "required", trigger: "blur" }],
}
});
watch(() => state.show, (val) => {
if (!val) {
setTimeout(() => {
emit('update:modelValue', val);
}, 300);
}
});
const parseSpeed = (num) => {
let index = 0;
while (num >= 1024) {
num /= 1024;
index++;
}
return `${(num*1.0).toFixed(2)}${['B', 'KB', 'MB', 'GB', 'TB'][index]}`;
}
const handleSearch = ()=>{
relayCdkeyPage(state.page).then((res)=>{
state.list = res;
}).catch(()=>{})
}
const handlePageChange = (p)=>{
state.page.Page = p;
handleSearch();
}
const handleAdd = (row)=>{
state.ruleForm = JSON.parse(JSON.stringify(defaultJson));
state.showAdd = true;
}
const handleDel = (row)=>{
relayCdkeyDel(row.CdkeyId).then((res)=>{
handleSearch();
}).catch(()=>{})
}
const ruleFormRef = ref(null);
const handleSave = ()=>{
ruleFormRef.value.validate((valid) => {
if (!valid) return;
const json = JSON.parse(JSON.stringify(state.ruleForm));
json.StartTime = moment(json.StartTime).format("YYYY-MM-DD HH:mm:ss");
json.MaxBytes = json.G*1024*1024*1024 + json.M*1024*1024 + json.K*1024 + json.B;
relayCdkeyAdd(json).then(()=>{
ElMessage.success(t('common.oper'));
state.showAdd = false;
handleSearch();
}).catch(()=>{
ElMessage.error(t('common.operFail'));
});
});
}
onMounted(()=>{
handleSearch();
})
return {state,ruleFormRef,parseSpeed,handleSearch,handlePageChange,handleAdd,handleDel,handleSave}
}
}
</script>
<style lang="stylus" scoped>
.head{
&>div{
margin-right:1rem;
}
}
.page{
padding:2rem 0;
display:inline-block;
}
.el-form-item{margin-bottom:1rem}
.el-input-number--small{width:10rem !important}
</style>

View File

@@ -1,7 +1,8 @@
<template>
<div class="group-wrap">
<el-table stripe :data="state.list" border size="small" width="100%" :height="`${state.height}px`" @cell-dblclick="handleCellClick">
<el-table-column prop="Name" :label="$t('server.groupName')" width="100">
<el-dialog class="options-center" :title="$t('status.group')" destroy-on-close v-model="state.show" width="77rem" top="2vh">
<div class="group-wrap">
<el-table stripe :data="state.list" border size="small" width="100%" height="70vh" @cell-dblclick="handleCellClick">
<el-table-column prop="Name" :label="$t('status.groupName')" width="100">
<template #default="scope">
<template v-if="scope.row.NameEditing">
<el-input autofocus size="small" v-model="scope.row.Name"
@@ -23,7 +24,7 @@
</template>
</template>
</el-table-column>
<el-table-column prop="Password" :label="$t('server.groupPassword')" >
<el-table-column prop="Password" :label="$t('status.groupPassword')" >
<template #default="scope">
<template v-if="scope.row.PasswordEditing">
<el-input type="password" show-password size="small" v-model="scope.row.Password" @blur="handleEditBlur(scope.row, 'Password')"></el-input>
@@ -31,15 +32,15 @@
<template v-else>{{ scope.row.Password.replace(/.{1}/g,'*') }}</template>
</template>
</el-table-column>
<el-table-column prop="Oper" :label="$t('server.groupOper')" width="160">
<el-table-column prop="Oper" :label="$t('status.groupOper')" width="110">
<template #header>
<div class="flex">
<strong>{{ $t('server.groupOper') }}</strong><span class="flex-1"></span><Sync name="GroupSecretKey"></Sync>
<strong>{{ $t('status.groupOper') }}</strong><span class="flex-1"></span><Sync name="GroupSecretKey"></Sync>
</div>
</template>
<template #default="scope">
<div>
<el-popconfirm :title="$t('server.groupDelConfirm')" @confirm="handleDel(scope.$index)">
<el-popconfirm :title="$t('status.groupDelConfirm')" @confirm="handleDel(scope.$index)">
<template #reference>
<el-button type="danger" size="small">
<el-icon><Delete /></el-icon>
@@ -49,15 +50,17 @@
<el-button size="small" @click="handleAdd(scope.$index)">
<el-icon><Plus /></el-icon>
</el-button>
<el-button v-if="scope.$index > 0" type="primary" size="small" @click="handleUse(scope.$index)">
<!-- <el-button v-if="scope.$index > 0" type="primary" size="small" @click="handleUse(scope.$index)">
<el-icon><Select /></el-icon>
</el-button>
</el-button> -->
</div>
</template>
</el-table-column>
</el-table>
</div>
</el-dialog>
</template>
<script>
import { setSignIn, setSignInGroups } from '@/apis/signin';
import { injectGlobalData } from '@/provide';
@@ -65,21 +68,30 @@ import { ElMessage } from 'element-plus';
import { computed, reactive, watch } from 'vue'
import { Delete,Plus,Select } from '@element-plus/icons-vue';
import { useI18n } from 'vue-i18n';
import Sync from '../sync/Index.vue'
import Sync from '../../sync/Index.vue'
export default {
props: ['modelValue'],
emits: ['update:modelValue'],
components:{Delete,Plus,Select,Sync },
setup(props) {
setup(props,{emit}) {
const {t} = useI18n();
const globalData = injectGlobalData();
const state = reactive({
list:globalData.value.config.Client.Groups || [],
height: computed(()=>globalData.value.height-70),
show:true
});
watch(()=>globalData.value.config.Client.Groups,()=>{
if(state.list.filter(c=>c['__editing']).length == 0){
state.list = globalData.value.config.Client.Groups;
}
})
});
watch(() => state.show, (val) => {
if (!val) {
setTimeout(() => {
emit('update:modelValue', val);
}, 300);
}
});
const handleCellClick = (row, column) => {
handleEdit(row, column.property);
@@ -143,5 +155,4 @@ export default {
}
</script>
<style lang="stylus" scoped>
.group-wrap{padding:1rem}
</style>

View File

@@ -1,78 +1,59 @@
<template>
<a href="javascript:;" :class="{connected:state.connected}" :title="$t('status.messengerChange')" @click="handleConfig">
<el-icon size="16"><Promotion /></el-icon> <span>{{$t('status.messenger')}}</span>
</a>
<el-dialog v-model="state.show" :title="$t('common.setting')" width="300" append-to-body>
<div>
<el-form :model="state.form" :rules="state.rules" label-width="6rem">
<el-form-item :label="$t('status.messengerName')" prop="name" v-if="hasRenameSelf">
<el-input v-model="state.form.name" maxlength="32" show-word-limit />
</el-form-item>
<el-form-item :label="$t('status.messengerGroup')" prop="groupid" v-if="hasGroup">
<el-select v-model="state.groupid" @change="handleGroupChange">
<el-option v-for="item in state.form.groups" :key="item.Id" :label="item.Name" :value="item.Id"/>
</el-select>
</el-form-item>
</el-form>
</div>
<template #footer>
<div class="dialog-footer t-c">
<el-button @click="state.show = false" :loading="state.loading">{{$t('common.cancel')}}</el-button>
<el-button type="primary" @click="handleSave" :loading="state.loading">{{$t('common.confirm')}}</el-button>
</div>
<el-dropdown>
<span class="el-dropdown-link" :class="{connected:state.connected}">
<el-icon><Avatar /></el-icon>
{{state.groupName}}
<el-icon><ArrowDown /></el-icon>
</span>
<template #dropdown>
<el-dropdown-menu v-if="hasGroup">
<el-dropdown-item v-for="item in state.groups" @click="handleGroupChange(item.Id)">{{item.Name}}</el-dropdown-item>
<el-dropdown-item @click="state.showGroups = true">{{$t('status.group')}}</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dialog>
</el-dropdown>
<Groups v-if="state.showGroups" v-model="state.showGroups"></Groups>
</template>
<script>
import { setSignIn } from '@/apis/signin';
import { injectGlobalData } from '@/provide';
import { ElMessage } from 'element-plus';
import { computed, reactive, ref } from 'vue';
import {Promotion,CirclePlus} from '@element-plus/icons-vue'
import {ArrowDown,Avatar} from '@element-plus/icons-vue'
import { useI18n } from 'vue-i18n';
import Groups from './Groups.vue';
export default {
components:{Promotion,CirclePlus},
components:{ArrowDown,Avatar,Groups},
props:['config'],
setup(props) {
const { t } = useI18n();
const globalData = injectGlobalData();
const hasRenameSelf = computed(()=>globalData.value.hasAccess('RenameSelf'));
const hasGroup = computed(()=>globalData.value.hasAccess('Group'));
const state = reactive({
show: false,
loading: false,
connected: computed(() => globalData.value.signin.Connected),
groupid: globalData.value.config.Client.Group.Id,
form: {
name: globalData.value.config.Client.Name,
groups: globalData.value.config.Client.Groups,
},
rules: {},
groupName: globalData.value.config.Client.Group.Name,
groups: globalData.value.config.Client.Groups.slice(),
showGroups:false
});
const handleConfig = () => {
if(!props.config || (!hasGroup.value && !hasRenameSelf.value)){
return;
}
state.form.name = globalData.value.config.Client.Name;
state.form.groups = globalData.value.config.Client.Groups;
state.groupid = globalData.value.config.Client.Group.Id;
state.show = true;
}
const handleGroupChange = (value)=>{
const index = state.form.groups.map((item,index)=>{
const index = state.groups.map((item,index)=>{
item.$index = index;
return item;
}).filter(c=>c.Id == value)[0].$index;
const temp = state.form.groups[index];
state.form.groups[index] = state.form.groups[0];
state.form.groups[0] = temp;
const temp = state.groups[index];
state.groups[index] = state.groups[0];
state.groups[0] = temp;
handleSave();
}
const handleSave = () => {
state.loading = true;
setSignIn(state.form).then(() => {
setSignIn({
Name:globalData.value.config.Client.Name,
Groups:state.groups,
}).then(() => {
state.loading = false;
state.show = false;
ElMessage.success(t('common.oper'));
@@ -86,21 +67,17 @@ export default {
});
}
return {
config:props.config,hasRenameSelf,hasGroup, state, handleConfig, handleSave,handleGroupChange
config:props.config,hasGroup, state,handleGroupChange
}
}
}
</script>
<style lang="stylus" scoped>
a{
a{color:#333;}
a{margin-left:.6rem;}
&.connected {
.el-dropdown{vertical-align: inherit;margin-right:1rem}
.connected {
color:green;font-weight:bold;
}
.el-icon{
vertical-align:text-bottom;
vertical-align:bottom;
}
}
</style>

View File

@@ -23,11 +23,12 @@
<Description>1. 优化linux下路由跟踪问题
2. 优化linux下获取本机IP问题
3. 增加ICS让win7+、win server2008+支持NAT
4. 增加中继卡密
4. 增加中继cdkey使用cdkey解锁公开中继节点的带宽、流量、连接数限制
5. 增加内外穿透定时开关功能
6. 优化管理页面连接接口的体验
7. 优化一些UI体验去除同步页面将同步功能放各个实际的位置
8. 其它一些修复优化</Description>
7. 优化一些UI体验去除同步页面将同步功能放各个实际的位置
8. 优化端口转发,让不同分组间可以使用相同端口
9. 其它一些修复优化</Description>
<Copyright>snltty</Copyright>
<PackageProjectUrl>https://github.com/snltty/linker</PackageProjectUrl>
<RepositoryUrl>https://github.com/snltty/linker</RepositoryUrl>

View File

@@ -1,10 +1,11 @@
v1.6.9
2025-03-03 17:44:29
2025-03-06 21:15:19
1. 优化linux下路由跟踪问题
2. 优化linux下获取本机IP问题
3. 增加ICS让win7+、win server2008+支持NAT
4. 增加中继卡密
4. 增加中继cdkey使用cdkey解锁公开中继节点的带宽、流量、连接数限制
5. 增加内外穿透定时开关功能
6. 优化管理页面连接接口的体验
7. 优化一些UI体验去除同步页面将同步功能放各个实际的位置
8. 其它一些修复优化
7. 优化一些UI体验去除同步页面将同步功能放各个实际的位置
8. 优化端口转发,让不同分组间可以使用相同端口
9. 其它一些修复优化