This commit is contained in:
snltty
2025-03-26 00:16:31 +08:00
parent 777234fb60
commit 72af4e529d
37 changed files with 277 additions and 116 deletions

View File

@@ -35,7 +35,7 @@ jobs:
release_name: v1.7.1.${{ steps.date.outputs.today }}
draft: false
prerelease: false
body: "1. 优化数据同步\r\n2. 优化linux网卡,我感觉网卡速度比较正常了\r\n3. 建议更新"
body: "1. 优化数据同步\r\n2. 优化linux的tun网卡网卡读写分离提高性能\r\n3. 优化windows网卡的禁用自动启用\r\n4. 增加TCP包合并。网卡IP包多个合并一起发送\r\n5. 建议更新"
- name: publish projects
run: ./publish.bat
- name: upload-win-x86-oss

View File

@@ -96,7 +96,7 @@ namespace linker.app
{
base.OnCreate();
string name = string.IsNullOrWhiteSpace(tuntapConfigTransfer.Info.Name) ? "linker" : tuntapConfigTransfer.Info.Name;
adapter.Setup(name, tuntapConfigTransfer.Info.IP, tuntapConfigTransfer.Info.PrefixLength, 1420);
tuntapTransfer.Setup(name, tuntapConfigTransfer.Info.IP, tuntapConfigTransfer.Info.PrefixLength);
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
@@ -112,13 +112,6 @@ namespace linker.app
public async Task Callback(LinkerTunDevicPacket packet)
{
if (packet.IPV4Broadcast || packet.IPV6Multicast)
{
if ((tuntapConfigTransfer.Switch & TuntapSwitch.Multicast) == TuntapSwitch.Multicast)
{
return;
}
}
await tuntapProxy.InputPacket(packet).ConfigureAwait(false);
}
public async ValueTask Close(ITunnelConnection connection)
@@ -366,7 +359,7 @@ namespace linker.app
}
*/
}
public async Task<bool> CheckAvailable()
public async Task<bool> CheckAvailable(bool order = false)
{
return await Task.FromResult(fd > 0);
}

View File

@@ -0,0 +1,7 @@
---
sidebar_position: 12
---
# 12、在线讨论咨询
<a href="https://jq.qq.com/?_wv=1027&k=ucoIVfz4" target="_blank">你可以加入QQ群1121552990</a>

View File

@@ -1,7 +1,7 @@
---
sidebar_position: 12
sidebar_position: 13
---
# 12、公益赞助
# 13、公益赞助
![Docusaurus Plushie](./img/qr.jpg)

View File

@@ -18,7 +18,6 @@ using linker.messenger.updater;
using linker.messenger.store.file;
using linker.messenger.serializer.memorypack;
using linker.libs;
using System;
namespace linker.messenger.entry
{

View File

@@ -93,6 +93,7 @@ namespace linker.messenger.store.file
serviceCollection.AddSingleton<ITuntapClientStore, TuntapClientStore>();
serviceCollection.AddSingleton<ILeaseServerStore, LeaseServerStore>();
serviceCollection.AddSingleton<ILeaseClientStore, LeaseClientStore>();
return serviceCollection;
}

View File

@@ -27,18 +27,18 @@ namespace linker.messenger.store.file
database = new LiteDatabase(new ConnectionString($"Filename={db}; Password={Helper.GlobalString}; journal=false"), bsonMapper);
CheckpointTask();
if (OperatingSystem.IsAndroid() == false)
{
AppDomain.CurrentDomain.ProcessExit += (s, e) => { database.Checkpoint(); database.Dispose(); };
Console.CancelKeyPress += (s, e) => { database.Checkpoint(); database.Dispose(); };
}
TimerHelper.SetIntervalLong(database.Checkpoint, 3000);
}
public ILiteCollection<T> GetCollection<T>(string name)
{
return database.GetCollection<T>(name);
}
private void CheckpointTask()
{
TimerHelper.SetIntervalLong(database.Checkpoint, 3000);
}
}
}

View File

@@ -1,4 +1,6 @@
using linker.messenger.tuntap;
using linker.messenger.tuntap.lease;
using System.Collections.Concurrent;
namespace linker.messenger.store.file
{
@@ -8,5 +10,6 @@ namespace linker.messenger.store.file
/// 虚拟网卡配置
/// </summary>
public TuntapConfigInfo Tuntap { get; set; } = new TuntapConfigInfo();
public ConcurrentDictionary<string, LeaseInfo> Leases { get; set; } = new ConcurrentDictionary<string, LeaseInfo>();
}
}

View File

@@ -0,0 +1,37 @@
using linker.messenger.tuntap;
using linker.messenger.tuntap.lease;
namespace linker.messenger.store.file.tuntap
{
public sealed class LeaseClientStore : ILeaseClientStore
{
public TuntapConfigInfo Info => runningConfig.Data.Tuntap;
private readonly RunningConfig runningConfig;
public LeaseClientStore(RunningConfig runningConfig)
{
this.runningConfig = runningConfig;
}
public LeaseInfo Get(string key)
{
if (runningConfig.Data.Leases.TryGetValue(key, out LeaseInfo info))
{
return info;
}
return new LeaseInfo();
}
public bool Set(string key, LeaseInfo info)
{
runningConfig.Data.Leases.AddOrUpdate(key, info, (a, b) => info);
return true;
}
public void Confirm()
{
runningConfig.Data.Update();
}
}
}

View File

@@ -1,5 +1,4 @@
using linker.libs;
using System.Collections.Concurrent;
using System.Collections.Concurrent;
using System.Net;
namespace linker.messenger.tuntap
@@ -41,7 +40,18 @@ namespace linker.messenger.tuntap
public ConcurrentDictionary<string, TuntapGroup2IPInfo> Group2IP { get; set; } = new ConcurrentDictionary<string, TuntapGroup2IPInfo>();
public bool DisableNat => (Switch & TuntapSwitch.DisableNat) == TuntapSwitch.DisableNat;
/// <summary>
/// 禁用nat
/// </summary>
public bool DisableNat => Switch.HasFlag(TuntapSwitch.DisableNat);
/// <summary>
/// tcp包合并
/// </summary>
public bool TcpMerge => Switch.HasFlag(TuntapSwitch.TcpMerge);
/// <summary>
/// 调整网卡顺序
/// </summary>
public bool InterfaceOrder => Switch.HasFlag(TuntapSwitch.InterfaceOrder);
}
public sealed class TuntapGroup2IPInfo
@@ -258,6 +268,48 @@ namespace linker.messenger.tuntap
}
}
}
/// <summary>
/// tcp包合并
/// </summary>
public bool TcpMerge
{
get
{
return (Switch & TuntapSwitch.TcpMerge) == TuntapSwitch.TcpMerge;
}
set
{
if (value)
{
Switch |= TuntapSwitch.TcpMerge;
}
else
{
Switch &= ~TuntapSwitch.TcpMerge;
}
}
}
/// <summary>
/// 调整网卡顺序
/// </summary>
public bool InterfaceOrder
{
get
{
return (Switch & TuntapSwitch.InterfaceOrder) == TuntapSwitch.InterfaceOrder;
}
set
{
if (value)
{
Switch |= TuntapSwitch.InterfaceOrder;
}
else
{
Switch &= ~TuntapSwitch.InterfaceOrder;
}
}
}
}
public sealed partial class TuntapForwardInfo
@@ -335,6 +387,14 @@ namespace linker.messenger.tuntap
/// 禁用Nat
/// </summary>
DisableNat = 32,
/// <summary>
/// 启用小包合并
/// </summary>
TcpMerge = 64,
/// <summary>
/// 调整网卡顺序
/// </summary>
InterfaceOrder = 128,
}
}

View File

@@ -96,7 +96,7 @@ namespace linker.messenger.tuntap
await RetstartDevice().ConfigureAwait(false);
return;
}
if (await tuntapTransfer.CheckAvailable().ConfigureAwait(false) == false)
if (await tuntapTransfer.CheckAvailable(tuntapConfigTransfer.Info.InterfaceOrder).ConfigureAwait(false) == false)
{
tuntapTransfer.Refresh();
}
@@ -112,13 +112,6 @@ namespace linker.messenger.tuntap
public async Task Callback(LinkerTunDevicPacket packet)
{
if (packet.IPV4Broadcast || packet.IPV6Multicast)
{
if ((tuntapConfigTransfer.Switch & TuntapSwitch.Multicast) == TuntapSwitch.Multicast)
{
return;
}
}
await tuntapProxy.InputPacket(packet).ConfigureAwait(false);
}
public async ValueTask Close(ITunnelConnection connection)

View File

@@ -48,7 +48,7 @@ namespace linker.messenger.tuntap
if (tuntapTransfer.Status == TuntapStatus.Running && tuntapConfigTransfer.Switch.HasFlag(TuntapSwitch.ShowDelay))
{
var items = tuntapDecenter.Infos.Values.Where(c => c.IP != null && c.IP.Equals(IPAddress.Any) == false && (c.Status & TuntapStatus.Running) == TuntapStatus.Running);
if ((tuntapConfigTransfer.Switch & TuntapSwitch.AutoConnect) != TuntapSwitch.AutoConnect)
if (tuntapConfigTransfer.Switch.HasFlag(TuntapSwitch.AutoConnect) == false)
{
var connections = tuntapProxy.GetConnections();
items = items.Where(c => connections.TryGetValue(c.MachineId, out ITunnelConnection connection) && connection.Connected || c.MachineId == signInClientStore.Id);

View File

@@ -27,16 +27,21 @@ namespace linker.messenger.tuntap
private readonly OperatingMultipleManager operatingMultipleManager = new OperatingMultipleManager();
protected override string TransactionId => "tuntap";
private readonly TuntapConfigTransfer tuntapConfigTransfer;
public TuntapProxy(ISignInClientStore signInClientStore,
TunnelTransfer tunnelTransfer, RelayClientTransfer relayTransfer, PcpTransfer pcpTransfer,
SignInClientTransfer signInClientTransfer, IRelayClientStore relayClientStore)
SignInClientTransfer signInClientTransfer, IRelayClientStore relayClientStore, TuntapConfigTransfer tuntapConfigTransfer)
: base(tunnelTransfer, relayTransfer, pcpTransfer, signInClientTransfer, signInClientStore, relayClientStore)
{
this.tuntapConfigTransfer = tuntapConfigTransfer;
}
protected override void Connected(ITunnelConnection connection)
{
connection.BeginReceive(this, null);
if (tuntapConfigTransfer.Info.TcpMerge)
connection.StartPacketMerge();
//有哪些目标IP用了相同目标隧道更新一下
List<uint> keys = ipConnections.Where(c => c.Value.RemoteMachineId == connection.RemoteMachineId).Select(c => c.Key).ToList();
foreach (uint ip in keys)
@@ -80,7 +85,7 @@ namespace linker.messenger.tuntap
//IPV4广播组播、IPV6 多播
if (packet.IPV4Broadcast || packet.IPV6Multicast)
{
if (connections.IsEmpty == false)
if (tuntapConfigTransfer.Switch.HasFlag(TuntapSwitch.Multicast) == false && connections.IsEmpty == false)
{
await Task.WhenAll(connections.Values.Where(c => c != null && c.Connected).Select(c => c.SendAsync(packet.Buffer, packet.Offset, packet.Length)));
}
@@ -119,6 +124,12 @@ namespace linker.messenger.tuntap
/// <returns></returns>
private async Task<ITunnelConnection> ConnectTunnel(uint ip)
{
/*
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
Console.WriteLine($"tuntap connect to {NetworkHelper.ToIP(ip)}");
}
*/
if (cidrManager.FindValue(ip, out string machineId))
{
return await ConnectTunnel(machineId, TunnelProtocolType.Quic).ConfigureAwait(false);

View File

@@ -181,9 +181,9 @@ namespace linker.messenger.tuntap
{
linkerTunDeviceAdapter.DelRoute(ips);
}
public async Task<bool> CheckAvailable()
public async Task<bool> CheckAvailable(bool order = false)
{
return await linkerTunDeviceAdapter.CheckAvailable().ConfigureAwait(false);
return await linkerTunDeviceAdapter.CheckAvailable(order).ConfigureAwait(false);
}
}
}

View File

@@ -0,0 +1,9 @@
namespace linker.messenger.tuntap.lease
{
public interface ILeaseClientStore
{
public LeaseInfo Get(string key);
public bool Set(string key,LeaseInfo info);
public void Confirm();
}
}

View File

@@ -11,16 +11,23 @@ namespace linker.messenger.tuntap.lease
private readonly IMessengerSender messengerSender;
private readonly SignInClientState signInClientState;
private readonly ISerializer serializer;
public LeaseClientTreansfer(IMessengerSender messengerSender, SignInClientState signInClientState, ISerializer serializer)
private readonly ILeaseClientStore leaseClientStore;
private readonly ISignInClientStore signInClientStore;
public LeaseClientTreansfer(IMessengerSender messengerSender, SignInClientState signInClientState, ISerializer serializer, ILeaseClientStore leaseClientStore, ISignInClientStore signInClientStore)
{
this.messengerSender = messengerSender;
this.signInClientState = signInClientState;
this.serializer = serializer;
this.leaseClientStore = leaseClientStore;
this.signInClientStore = signInClientStore;
LeaseExpTask();
}
public async Task AddNetwork(LeaseInfo info)
{
leaseClientStore.Set(signInClientStore.Group.Id, info);
leaseClientStore.Confirm();
await messengerSender.SendOnly(new MessageRequestWrap
{
Connection = signInClientState.Connection,
@@ -39,6 +46,8 @@ namespace linker.messenger.tuntap.lease
if (resp.Code == MessageResponeCodes.OK)
{
LeaseInfo info = serializer.Deserialize<LeaseInfo>(resp.Data.Span);
leaseClientStore.Set(signInClientStore.Group.Id, info);
leaseClientStore.Confirm();
return info;
}
return new LeaseInfo { IP = IPAddress.Any, PrefixLength = 24 };
@@ -74,6 +83,25 @@ namespace linker.messenger.tuntap.lease
private void LeaseExpTask()
{
signInClientState.OnSignInSuccess += async (times) =>
{
try
{
await GetNetwork().ConfigureAwait(false);
LeaseInfo info = leaseClientStore.Get(signInClientStore.Group.Id);
if (info != null && info.IP.Equals(IPAddress.Any) == false)
{
await AddNetwork(info);
}
}
catch (Exception ex)
{
if(LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
LoggerHelper.Instance.Error(ex);
}
}
};
TimerHelper.SetIntervalLong(async () =>
{
await messengerSender.SendReply(new MessageRequestWrap
@@ -81,7 +109,7 @@ namespace linker.messenger.tuntap.lease
Connection = signInClientState.Connection,
MessengerId = (ushort)TuntapMessengerIds.LeaseExp,
}).ConfigureAwait(false);
}, 60000);
}, 60000);
}
}
}

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.230" ProductVersion="0.0.0.230" 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.231" ProductVersion="0.0.0.231" 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

@@ -1 +1 @@
.table-sort th[data-v-754b053a]{border-bottom:0}.dropdown[data-v-2f0ed5e0]{border:1px solid #ddd;padding:.4rem;font-size:1.3rem;border-radius:.4rem;position:relative}.dropdown .el-icon[data-v-2f0ed5e0]{vertical-align:middle}.dropdown .badge[data-v-2f0ed5e0]{position:absolute;right:-1rem;top:-50%;border-radius:10px;background-color:#f1ae05;color:#fff;padding:.2rem .6rem;font-size:1.2rem}a[data-v-56c0e8be]{color:#666;text-decoration:underline}a.green[data-v-56c0e8be]{color:green;font-weight:700}a.download[data-v-56c0e8be]{margin-left:.6rem}a.download .el-icon[data-v-56c0e8be]{vertical-align:middle;font-weight:700;margin-left:.3rem}a.download .el-icon.loading[data-v-56c0e8be]{animation:loading-56c0e8be 1s linear infinite}a.download+a.download[data-v-56c0e8be]{margin-left:.2rem}@keyframes loading-56c0e8be{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}img.system[data-v-9f58a72e]{height:1.6rem;vertical-align:middle;margin-right:.4rem}.self[data-v-9f58a72e]{color:#d400ff}.self .el-icon[data-v-9f58a72e]{vertical-align:text-bottom}.ipaddress span[data-v-5db71b03]{vertical-align:middle}.el-input[data-v-5db71b03]{width:12rem;margin-right:.6rem}.el-col[data-v-7a697708]{text-align:left}div.point[data-v-41d1beca]{margin:-.2rem .3rem 0 -1.3rem;position:absolute}span.point[data-v-41d1beca]{width:.8rem;height:.8rem;border-radius:50%;display:inline-block;vertical-align:middle;background-color:#eee;border:1px solid #ddd;cursor:pointer;transition:.3s}span.point[data-v-41d1beca]:hover{transform:scale(2)}span.point.p2p[data-v-41d1beca]{background-color:#01c901;border:1px solid #049538}span.point.relay[data-v-41d1beca]{background-color:#e3e811;border:1px solid #b3c410}span.point.node[data-v-41d1beca]{background-color:#09dda9;border:1px solid #0cac90}.el-icon.loading[data-v-5ce8d590],a.loading[data-v-5ce8d590]{vertical-align:middle;font-weight:700;animation:loading-5ce8d590 1s linear infinite}.el-switch.is-disabled[data-v-5ce8d590]{opacity:1}.el-input[data-v-5ce8d590]{width:8rem}.delay[data-v-5ce8d590]{position:absolute;right:0;bottom:0;line-height:normal}.switch-btn[data-v-5ce8d590]{font-size:1.5rem}.any[data-v-5ce8d590]{position:absolute;left:-7px;top:-2px;line-height:normal}.any.green[data-v-5ce8d590]{background:linear-gradient(270deg,#caff00,green,#0d6d23,#e38a00,green);background-clip:text;-webkit-background-clip:text;-webkit-text-fill-color:hsla(0,0%,100%,0)}@keyframes loading-5ce8d590{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.wrap[data-v-786fe646]{padding-right:1rem}.remark[data-v-786fe646]{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.wrap[data-v-286c7cac]{padding-right:1rem}.el-switch.is-disabled[data-v-0b701835]{opacity:1}.upgrade-wrap[data-v-0b701835]{border:1px solid #ddd;margin-bottom:2rem;padding:0 0 1rem 0}.el-switch.is-disabled[data-v-67ed3552]{opacity:1}.calc span[data-v-67ed3552]{display:inline-block}.calc span.label[data-v-67ed3552]{width:6rem}.el-icon.loading[data-v-3a4bfe6c],a.loading[data-v-3a4bfe6c]{vertical-align:middle;font-weight:700;animation:loading-3a4bfe6c 1s linear infinite}.el-switch.is-disabled[data-v-3a4bfe6c]{opacity:1}.el-input[data-v-3a4bfe6c]{width:8rem}.switch-btn[data-v-3a4bfe6c]{font-size:1.5rem}@keyframes loading-3a4bfe6c{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.el-switch.is-disabled[data-v-022e3781]{opacity:1}.upgrade-wrap[data-v-022e3781]{border:1px solid #ddd;margin-bottom:2rem;padding:1rem 0 1rem 0}.lan-item[data-v-022e3781]{margin-bottom:0}.el-switch.is-disabled[data-v-64b81c5b]{opacity:1}.green[data-v-64b81c5b]{font-weight:700}img.system[data-v-64b81c5b]{height:1.4rem;margin-right:.4rem;border:1px solid #eee}.el-switch.is-disabled[data-v-6941c158]{opacity:1}ul li[data-v-6941c158]{padding-left:2rem}a[data-v-2ee190a4]{text-decoration:underline}a+a[data-v-2ee190a4]{margin-left:1rem}a.green[data-v-2ee190a4]{font-weight:700}.head[data-v-6897ed85]{padding-bottom:1rem}.green[data-v-6897ed85]{color:green;font-weight:700}.error[data-v-6897ed85]{font-weight:700}.error .el-icon[data-v-6897ed85]{vertical-align:text-bottom}.head[data-v-7d65167d]{padding-bottom:1rem}.error[data-v-7d65167d]{font-weight:700}.error .el-icon[data-v-7d65167d]{vertical-align:text-bottom}.head[data-v-8c388c86]{padding-bottom:1rem}.blue[data-v-8c388c86]{color:#409eff}.dropdown[data-v-8c388c86]{border:1px solid #ddd;padding:.4rem;font-size:1.3rem;border-radius:.4rem;position:relative}.dropdown .el-icon[data-v-8c388c86]{vertical-align:middle}.dropdown .badge[data-v-8c388c86]{position:absolute;right:-1rem;top:-50%;border-radius:10px;background-color:#f1ae05;color:#fff;padding:.2rem .6rem;font-size:1.2rem}.table-sort.el-table th.el-table__cell.is-leaf{border-bottom:0}.table-sort.el-table .el-table__inner-wrapper:before{height:0}.home-list-wrap[data-v-4766ad40]{padding:1rem}.home-list-wrap .page[data-v-4766ad40]{padding-top:1rem}.home-list-wrap .page-wrap[data-v-4766ad40]{display:inline-block}
.table-sort th[data-v-754b053a]{border-bottom:0}.dropdown[data-v-2f0ed5e0]{border:1px solid #ddd;padding:.4rem;font-size:1.3rem;border-radius:.4rem;position:relative}.dropdown .el-icon[data-v-2f0ed5e0]{vertical-align:middle}.dropdown .badge[data-v-2f0ed5e0]{position:absolute;right:-1rem;top:-50%;border-radius:10px;background-color:#f1ae05;color:#fff;padding:.2rem .6rem;font-size:1.2rem}a[data-v-56c0e8be]{color:#666;text-decoration:underline}a.green[data-v-56c0e8be]{color:green;font-weight:700}a.download[data-v-56c0e8be]{margin-left:.6rem}a.download .el-icon[data-v-56c0e8be]{vertical-align:middle;font-weight:700;margin-left:.3rem}a.download .el-icon.loading[data-v-56c0e8be]{animation:loading-56c0e8be 1s linear infinite}a.download+a.download[data-v-56c0e8be]{margin-left:.2rem}@keyframes loading-56c0e8be{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}img.system[data-v-9f58a72e]{height:1.6rem;vertical-align:middle;margin-right:.4rem}.self[data-v-9f58a72e]{color:#d400ff}.self .el-icon[data-v-9f58a72e]{vertical-align:text-bottom}.ipaddress span[data-v-5db71b03]{vertical-align:middle}.el-input[data-v-5db71b03]{width:12rem;margin-right:.6rem}.el-col[data-v-7a697708]{text-align:left}div.point[data-v-41d1beca]{margin:-.2rem .3rem 0 -1.3rem;position:absolute}span.point[data-v-41d1beca]{width:.8rem;height:.8rem;border-radius:50%;display:inline-block;vertical-align:middle;background-color:#eee;border:1px solid #ddd;cursor:pointer;transition:.3s}span.point[data-v-41d1beca]:hover{transform:scale(2)}span.point.p2p[data-v-41d1beca]{background-color:#01c901;border:1px solid #049538}span.point.relay[data-v-41d1beca]{background-color:#e3e811;border:1px solid #b3c410}span.point.node[data-v-41d1beca]{background-color:#09dda9;border:1px solid #0cac90}.el-icon.loading[data-v-5ce8d590],a.loading[data-v-5ce8d590]{vertical-align:middle;font-weight:700;animation:loading-5ce8d590 1s linear infinite}.el-switch.is-disabled[data-v-5ce8d590]{opacity:1}.el-input[data-v-5ce8d590]{width:8rem}.delay[data-v-5ce8d590]{position:absolute;right:0;bottom:0;line-height:normal}.switch-btn[data-v-5ce8d590]{font-size:1.5rem}.any[data-v-5ce8d590]{position:absolute;left:-7px;top:-2px;line-height:normal}.any.green[data-v-5ce8d590]{background:linear-gradient(270deg,#caff00,green,#0d6d23,#e38a00,green);background-clip:text;-webkit-background-clip:text;-webkit-text-fill-color:hsla(0,0%,100%,0)}@keyframes loading-5ce8d590{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.wrap[data-v-786fe646]{padding-right:1rem}.remark[data-v-786fe646]{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.wrap[data-v-286c7cac]{padding-right:1rem}.el-switch.is-disabled[data-v-d52cdcd0]{opacity:1}.upgrade-wrap[data-v-d52cdcd0]{border:1px solid #ddd;margin-bottom:2rem;padding:0 0 1rem 0}.el-switch.is-disabled[data-v-67ed3552]{opacity:1}.calc span[data-v-67ed3552]{display:inline-block}.calc span.label[data-v-67ed3552]{width:6rem}.el-icon.loading[data-v-3a4bfe6c],a.loading[data-v-3a4bfe6c]{vertical-align:middle;font-weight:700;animation:loading-3a4bfe6c 1s linear infinite}.el-switch.is-disabled[data-v-3a4bfe6c]{opacity:1}.el-input[data-v-3a4bfe6c]{width:8rem}.switch-btn[data-v-3a4bfe6c]{font-size:1.5rem}@keyframes loading-3a4bfe6c{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.el-switch.is-disabled[data-v-022e3781]{opacity:1}.upgrade-wrap[data-v-022e3781]{border:1px solid #ddd;margin-bottom:2rem;padding:1rem 0 1rem 0}.lan-item[data-v-022e3781]{margin-bottom:0}.el-switch.is-disabled[data-v-64b81c5b]{opacity:1}.green[data-v-64b81c5b]{font-weight:700}img.system[data-v-64b81c5b]{height:1.4rem;margin-right:.4rem;border:1px solid #eee}.el-switch.is-disabled[data-v-6941c158]{opacity:1}ul li[data-v-6941c158]{padding-left:2rem}a[data-v-2ee190a4]{text-decoration:underline}a+a[data-v-2ee190a4]{margin-left:1rem}a.green[data-v-2ee190a4]{font-weight:700}.head[data-v-6897ed85]{padding-bottom:1rem}.green[data-v-6897ed85]{color:green;font-weight:700}.error[data-v-6897ed85]{font-weight:700}.error .el-icon[data-v-6897ed85]{vertical-align:text-bottom}.head[data-v-7d65167d]{padding-bottom:1rem}.error[data-v-7d65167d]{font-weight:700}.error .el-icon[data-v-7d65167d]{vertical-align:text-bottom}.head[data-v-8c388c86]{padding-bottom:1rem}.blue[data-v-8c388c86]{color:#409eff}.dropdown[data-v-8c388c86]{border:1px solid #ddd;padding:.4rem;font-size:1.3rem;border-radius:.4rem;position:relative}.dropdown .el-icon[data-v-8c388c86]{vertical-align:middle}.dropdown .badge[data-v-8c388c86]{position:absolute;right:-1rem;top:-50%;border-radius:10px;background-color:#f1ae05;color:#fff;padding:.2rem .6rem;font-size:1.2rem}.table-sort.el-table th.el-table__cell.is-leaf{border-bottom:0}.table-sort.el-table .el-table__inner-wrapper:before{height:0}.home-list-wrap[data-v-4766ad40]{padding:1rem}.home-list-wrap .page[data-v-4766ad40]{padding-top:1rem}.home-list-wrap .page-wrap[data-v-4766ad40]{display:inline-block}

View File

@@ -1 +1 @@
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="favicon.ico"><title>linker.web</title><link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/><script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script><script defer="defer" src="js/chunk-vendors.3be25225.js"></script><script defer="defer" src="js/app.0e347be3.js"></script><link href="css/chunk-vendors.d8267b33.css" rel="stylesheet"><link href="css/app.7bd6c330.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but linker.web doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="favicon.ico"><title>linker.web</title><link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/><script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script><script defer="defer" src="js/chunk-vendors.3be25225.js"></script><script defer="defer" src="js/app.3d2b2fb0.js"></script><link href="css/chunk-vendors.d8267b33.css" rel="stylesheet"><link href="css/app.7bd6c330.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but linker.web doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -98,7 +98,7 @@ namespace linker.tun
/// 检查网卡是否可用
/// </summary>
/// <returns></returns>
public Task<bool> CheckAvailable();
public Task<bool> CheckAvailable(bool order = false);
}
/// <summary>

View File

@@ -341,7 +341,7 @@ namespace linker.tun
return output;
}
public async Task<bool> CheckAvailable()
public async Task<bool> CheckAvailable(bool order = false)
{
string output = CommandHelper.Linux(string.Empty, new string[] { $"ip link show {Name}" });
return await Task.FromResult(output.Contains("state UP")).ConfigureAwait(false);

View File

@@ -184,7 +184,7 @@ namespace linker.tun
}
public async Task<bool> CheckAvailable()
public async Task<bool> CheckAvailable(bool order = false)
{
return await Task.FromResult(true).ConfigureAwait(false);
}

View File

@@ -1,6 +1,5 @@
using linker.libs;
using linker.libs.timer;
using System.Buffers.Binary;
using System.Net;
namespace linker.tun
@@ -285,9 +284,9 @@ namespace linker.tun
}
public async Task<bool> CheckAvailable()
public async Task<bool> CheckAvailable(bool order = false)
{
return await linkerTunDevice.CheckAvailable();
return await linkerTunDevice.CheckAvailable(order);
}
}
}

View File

@@ -154,14 +154,17 @@ namespace linker.tun
if (session == 0) return;
try
{
WinTun.SetEvent(waitHandle);
WinTun.WintunEndSession(session);
IntPtr oldSession = session;
IntPtr oldWaitHandle = waitHandle;
CommandHelper.Windows(string.Empty, new string[] { $"netsh interface set interface {Name} enable" });
session = WinTun.WintunStartSession(adapter, 0x400000);
waitHandle = WinTun.WintunGetReadWaitEvent(session);
AddIPV4();
AddIPV6();
WinTun.SetEvent(oldWaitHandle);
WinTun.WintunEndSession(oldSession);
}
catch (Exception)
{
@@ -391,11 +394,12 @@ namespace linker.tun
}
}
public async Task<bool> CheckAvailable()
public async Task<bool> CheckAvailable(bool order = false)
{
NetworkInterface[] interfaces = NetworkInterface.GetAllNetworkInterfaces();
InterfaceOrder(interfaces);
if (order)
InterfaceOrder(interfaces);
NetworkInterface networkInterface = interfaces.FirstOrDefault(c => c.Name == Name || c.Description == $"{Name} Tunnel" || c.Name.Contains(Name));
UnicastIPAddressInformation firstIpv4 = networkInterface?.GetIPProperties()

View File

@@ -117,7 +117,6 @@ namespace linker.tunnel
{
if (tunnelMessengerAdapter.ServerHost == null) return;
Console.WriteLine(tunnelMessengerAdapter.ServerHost);
GetLocalIP(tunnelMessengerAdapter.ServerHost).ContinueWith((result) =>
{
if (tunnelMessengerAdapter.PortMapPrivate > 0)

View File

@@ -154,6 +154,7 @@ namespace linker.tunnel.connection
/// </summary>
public LastTicksManager LastTicks { get; }
/// <summary>
/// 发送数据
/// </summary>
@@ -175,6 +176,10 @@ namespace linker.tunnel.connection
/// <param name="userToken">自定义数据,回调带上</param>
public void BeginReceive(ITunnelConnectionReceiveCallback callback, object userToken);
public void StartPacketMerge()
{
}
public string ToString();
public bool Equals(ITunnelConnection connection);
}

View File

@@ -41,6 +41,7 @@ namespace linker.tunnel.connection
public LastTicksManager LastTicks { get; private set; } = new LastTicksManager();
[JsonIgnore]
public QuicStream Stream { get; init; }
[JsonIgnore]
@@ -55,12 +56,11 @@ namespace linker.tunnel.connection
private ITunnelConnectionReceiveCallback callback;
private CancellationTokenSource cancellationTokenSource;
private object userToken;
private ReceiveDataBuffer bufferCache = new ReceiveDataBuffer();
private readonly ReceiveDataBuffer bufferCache = new ReceiveDataBuffer();
private LastTicksManager pingTicks = new();
private byte[] pingBytes = Encoding.UTF8.GetBytes($"{Helper.GlobalString}.tcp.ping");
private byte[] pongBytes = Encoding.UTF8.GetBytes($"{Helper.GlobalString}.tcp.pong");
private bool pong = true;
private readonly LastTicksManager pingTicks = new();
private readonly byte[] pingBytes = Encoding.UTF8.GetBytes($"{Helper.GlobalString}.tcp.ping");
private readonly byte[] pongBytes = Encoding.UTF8.GetBytes($"{Helper.GlobalString}.tcp.pong");
/// <summary>
@@ -153,7 +153,6 @@ namespace linker.tunnel.connection
else if (packet.Span.SequenceEqual(pongBytes))
{
Delay = (int)pingTicks.Diff();
pong = true;
}
}
@@ -201,7 +200,6 @@ namespace linker.tunnel.connection
}
catch (Exception)
{
pong = true;
Dispose();
}
finally

View File

@@ -6,6 +6,7 @@ using System.Net;
using System.Text.Json.Serialization;
using System.Text;
using System.Net.Sockets;
using System.IO.Pipelines;
namespace linker.tunnel.connection
{
@@ -39,6 +40,7 @@ namespace linker.tunnel.connection
public LastTicksManager LastTicks { get; private set; } = new LastTicksManager();
[JsonIgnore]
public SslStream Stream { get; init; }
@@ -49,19 +51,17 @@ namespace linker.tunnel.connection
private ITunnelConnectionReceiveCallback callback;
private CancellationTokenSource cancellationTokenSource;
private object userToken;
private ReceiveDataBuffer bufferCache = new ReceiveDataBuffer();
private readonly ReceiveDataBuffer bufferCache = new ReceiveDataBuffer();
private LastTicksManager pingTicks = new LastTicksManager();
private byte[] pingBytes = Encoding.UTF8.GetBytes($"{Helper.GlobalString}.tcp.ping");
private byte[] pongBytes = Encoding.UTF8.GetBytes($"{Helper.GlobalString}.tcp.pong");
private bool pong = true;
private readonly LastTicksManager pingTicks = new LastTicksManager();
private readonly byte[] pingBytes = Encoding.UTF8.GetBytes($"{Helper.GlobalString}.tcp.ping");
private readonly byte[] pongBytes = Encoding.UTF8.GetBytes($"{Helper.GlobalString}.tcp.pong");
/// <summary>
/// 开始接收数据
/// </summary>
/// <param name="callback">数据回调</param>
/// <param name="userToken">自定义数据</param>
/// <param name="byFrame">是否处理粘包true时请在首部4字节标注数据长度</param>
public void BeginReceive(ITunnelConnectionReceiveCallback callback, object userToken)
{
if (this.callback != null) return;
@@ -163,7 +163,6 @@ namespace linker.tunnel.connection
else if (packet.Span.SequenceEqual(pongBytes))
{
Delay = (int)pingTicks.Diff();
pong = true;
return;
}
}
@@ -226,7 +225,6 @@ namespace linker.tunnel.connection
}
catch (Exception)
{
pong = true;
Dispose();
}
finally
@@ -236,12 +234,21 @@ namespace linker.tunnel.connection
ArrayPool<byte>.Shared.Return(heartData);
}
private SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1);
private readonly SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1);
public async Task<bool> SendAsync(ReadOnlyMemory<byte> data)
{
if (callback == null) return false;
if (pipe != null)
{
Memory<byte> memory = pipe.Writer.GetMemory(data.Length);
data.CopyTo(memory);
pipe.Writer.Advance(data.Length);
await pipe.Writer.FlushAsync();
return true;
}
if (Stream != null)
{
await semaphoreSlim.WaitAsync().ConfigureAwait(false);
@@ -278,6 +285,16 @@ namespace linker.tunnel.connection
{
if (callback == null) return false;
if (pipe != null)
{
ReadOnlyMemory<byte> data = buffer.AsMemory(offset, length);
Memory<byte> memory = pipe.Writer.GetMemory(data.Length);
data.CopyTo(memory);
pipe.Writer.Advance(data.Length);
await pipe.Writer.FlushAsync();
return true;
}
if (Stream != null)
{
await semaphoreSlim.WaitAsync().ConfigureAwait(false);
@@ -311,11 +328,11 @@ namespace linker.tunnel.connection
return false;
}
/*
private Pipe pipe;
public void PipeLines()
public void StartPacketMerge()
{
pipe = new Pipe(new PipeOptions { });
pipe = new Pipe(new PipeOptions(pauseWriterThreshold:800*1024));
_ = Reader();
}
private async Task Reader()
@@ -328,13 +345,13 @@ namespace linker.tunnel.connection
{
break;
}
ReadOnlySequence<byte> buffer = result.Buffer;
while (buffer.Length > 0)
{
int chunkSize = (int)Math.Min(buffer.Length, 8192);
ReadOnlySequence<byte> chunk = buffer.Slice(0, chunkSize);
if (Stream != null) await semaphoreSlim.WaitAsync().ConfigureAwait(false);
try
{
@@ -367,27 +384,8 @@ namespace linker.tunnel.connection
}
pipe.Reader.AdvanceTo(result.Buffer.End);
}
}
public async Task<bool> WriteAsync(ReadOnlyMemory<byte> data)
{
Memory<byte> memory = pipe.Writer.GetMemory(data.Length);
data.CopyTo(memory);
pipe.Writer.Advance(data.Length);
await pipe.Writer.FlushAsync();
return true;
}
public async Task<bool> WriteAsync(byte[] buffer, int offset, int length)
{
ReadOnlyMemory<byte> data = buffer.AsMemory(offset, length);
Memory<byte> memory = pipe.Writer.GetMemory(data.Length);
data.CopyTo(memory);
pipe.Writer.Advance(data.Length);
await pipe.Writer.FlushAsync();
return true;
}
*/
public void Dispose()
{
@@ -407,6 +405,14 @@ namespace linker.tunnel.connection
Socket?.SafeClose();
try
{
pipe?.Writer.Complete();
pipe?.Reader.Complete();
}
catch (Exception)
{ }
}
public override string ToString()
{

View File

@@ -5,7 +5,6 @@ using System.Net;
using System.Text;
using System.Text.Json.Serialization;
using System.Net.Sockets;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace linker.tunnel.connection
{
@@ -62,12 +61,11 @@ namespace linker.tunnel.connection
private CancellationTokenSource cancellationTokenSource;
private object userToken;
private LastTicksManager pingTicks = new LastTicksManager();
private byte[] pingBytes = Encoding.UTF8.GetBytes($"{Helper.GlobalString}.udp.ping");
private byte[] pongBytes = Encoding.UTF8.GetBytes($"{Helper.GlobalString}.udp.pong");
private byte[] finBytes = Encoding.UTF8.GetBytes($"{Helper.GlobalString}.udp.fing");
private byte[] ttlBytes = Encoding.UTF8.GetBytes($"{Helper.GlobalString}.ttl");
private bool pong = true;
private readonly LastTicksManager pingTicks = new LastTicksManager();
private readonly byte[] pingBytes = Encoding.UTF8.GetBytes($"{Helper.GlobalString}.udp.ping");
private readonly byte[] pongBytes = Encoding.UTF8.GetBytes($"{Helper.GlobalString}.udp.pong");
private readonly byte[] finBytes = Encoding.UTF8.GetBytes($"{Helper.GlobalString}.udp.fing");
private readonly byte[] ttlBytes = Encoding.UTF8.GetBytes($"{Helper.GlobalString}.ttl");
/// <summary>
@@ -148,7 +146,6 @@ namespace linker.tunnel.connection
else if (memory.Span.SequenceEqual(pongBytes))
{
Delay = (int)pingTicks.Diff();
pong = true;
}
else if (memory.Span.SequenceEqual(finBytes))
{
@@ -226,7 +223,6 @@ namespace linker.tunnel.connection
}
catch (Exception)
{
pong = true;
Dispose();
}
finally
@@ -236,7 +232,6 @@ namespace linker.tunnel.connection
ArrayPool<byte>.Shared.Return(heartData);
}
private byte[] encodeBuffer = new byte[8 * 1024];
public async Task<bool> SendAsync(ReadOnlyMemory<byte> data)
{
@@ -308,6 +303,7 @@ namespace linker.tunnel.connection
return false;
}
public void Dispose()
{
@@ -337,4 +333,4 @@ namespace linker.tunnel.connection
return connection != null && GetHashCode() == connection.GetHashCode() && IPEndPoint.Equals(connection.IPEndPoint);
}
}
}
}

View File

@@ -38,6 +38,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Mono.Nat" Version="3.0.4" />
<PackageReference Include="System.IO.Pipelines" Version="9.0.3" />
</ItemGroup>
<ItemGroup>

View File

@@ -6,15 +6,19 @@
<el-form-item label="网卡名" prop="Name">
<el-input v-model="state.ruleForm.Name" style="width:14rem" /> <span>留空则使用本组网络的设置</span>
</el-form-item>
<el-form-item label="网卡IP" prop="IP">
<el-form-item label="网卡IP" prop="IP" class="mgb-0">
<el-input v-model="state.ruleForm.IP" style="width:14rem" />
<span>/</span>
<el-input @change="handlePrefixLengthChange" v-model="state.ruleForm.PrefixLength" style="width:4rem" />
<span style="width: 2rem;"></span>
<el-checkbox v-model="state.ruleForm.ShowDelay" label="显示延迟" size="large" style="margin-right:1rem" />
<el-checkbox v-model="state.ruleForm.AutoConnect" label="自动连接" size="large" style="margin-right:1rem" />
<el-checkbox v-model="state.ruleForm.Multicast" label="禁用广播" size="large" />
<el-checkbox v-model="state.ruleForm.Nat" label="禁用NAT" size="large" />
<span>/</span>
<el-input @change="handlePrefixLengthChange" v-model="state.ruleForm.PrefixLength" style="width:4rem" />
</el-form-item>
<el-form-item label="">
<el-checkbox class="mgr-1" v-model="state.ruleForm.ShowDelay" label="显示延迟" size="large" />
<el-checkbox class="mgr-1" v-model="state.ruleForm.AutoConnect" label="自动连接" size="large" />
<el-checkbox class="mgr-1" v-model="state.ruleForm.Multicast" label="禁用广播" size="large" />
<el-checkbox class="mgr-1" v-model="state.ruleForm.Nat" label="禁用NAT" size="large" />
<el-checkbox class="mgr-1" v-model="state.ruleForm.TcpMerge" label="TCP包合并" size="large" />
<el-checkbox v-model="state.ruleForm.InterfaceOrder" label="调整网卡顺序" size="large" />
</el-form-item>
<el-form-item prop="upgrade" class="mgb-0">
<el-checkbox v-model="state.ruleForm.Upgrade" label="我很懂,我要使用高级功能(点对网和网对网)" size="large" />
@@ -68,6 +72,8 @@ export default {
Upgrade: tuntap.value.current.Upgrade,
Multicast: tuntap.value.current.Multicast,
Nat: tuntap.value.current.Nat,
TcpMerge: tuntap.value.current.TcpMerge,
InterfaceOrder: tuntap.value.current.InterfaceOrder,
Forwards: tuntap.value.current.Forwards,
Name: tuntap.value.current.Name,
},
@@ -112,6 +118,8 @@ export default {
json.Upgrade = state.ruleForm.Upgrade;
json.Multicast = state.ruleForm.Multicast;
json.Nat = state.ruleForm.Nat;
json.TcpMerge = state.ruleForm.TcpMerge;
json.InterfaceOrder = state.ruleForm.InterfaceOrder;
json.Forwards = forwardDom.value ? forwardDom.value.getData() : tuntap.value.current.Forwards;
json.Name = state.ruleForm.Name;
updateTuntap(json).then(() => {

View File

@@ -21,8 +21,10 @@
<Authors>snltty</Authors>
<Company>snltty</Company>
<Description>1. 优化数据同步
2. 优化linux网卡,我感觉网卡速度比较正常了
3. 建议更新</Description>
2. 优化linux的tun网卡网卡读写分离提高性能
3. 优化windows网卡的禁用自动启用
4. 增加TCP包合并。网卡IP包多个合并一起发送
5. 建议更新</Description>
<Copyright>snltty</Copyright>
<PackageProjectUrl>https://github.com/snltty/linker</PackageProjectUrl>
<RepositoryUrl>https://github.com/snltty/linker</RepositoryUrl>

View File

@@ -1,5 +1,7 @@
v1.7.1
2025-03-23 20:24:26
2025-03-26 00:16:31
1. 优化数据同步
2. 优化linux网卡,我感觉网卡速度比较正常了
3. 建议更新
2. 优化linux的tun网卡网卡读写分离提高性能
3. 优化windows网卡的禁用自动启用
4. 增加TCP包合并。网卡IP包多个合并一起发送
5. 建议更新