This commit is contained in:
snltty
2025-09-19 16:42:34 +08:00
parent d45c5af059
commit 5b0890a05f
20 changed files with 140 additions and 232 deletions

File diff suppressed because one or more lines are too long

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.e9b4f2f4.js"></script><script defer="defer" src="js/app.f1b9356d.js"></script><link href="css/chunk-vendors.d8267b33.css" rel="stylesheet"><link href="css/app.e16885f2.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.e9b4f2f4.js"></script><script defer="defer" src="js/app.166579fa.js"></script><link href="css/chunk-vendors.d8267b33.css" rel="stylesheet"><link href="css/app.e16885f2.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

File diff suppressed because one or more lines are too long

View File

@@ -11,37 +11,42 @@ namespace linker.libs
/// </summary>
/// <param name="packet">一个完整的IP包</param>
/// <param name="payload">是否计算荷载协议校验和</param>
public static unsafe void Checksum(ReadOnlyMemory<byte> packet,bool payload = true)
public static unsafe void Checksum(ReadOnlyMemory<byte> packet, bool ipHeader = true, bool payload = true)
{
fixed (byte* ptr = packet.Span)
{
Checksum(ptr, packet.Length, payload);
}
Checksum(packet.Span, ipHeader, payload);
}
/// <summary>
/// 计算IP包的校验和
/// </summary>
/// <param name="packet">一个完整的IP包</param>
/// <param name="payload">是否计算荷载协议校验和</param>
public static unsafe void Checksum(ReadOnlySpan<byte> packet, bool payload = true)
public static unsafe void Checksum(ReadOnlySpan<byte> packet, bool ipHeader = true, bool payload = true)
{
fixed (byte* ptr = packet)
{
Checksum(ptr, packet.Length, payload);
Checksum(ptr, ipHeader, payload);
}
}
/// <summary>
/// 计算IP包的校验和
/// </summary>
/// <param name="ptr">IP包指针</param>
/// <param name="length">IP包长度</param>
/// <param name="ipHeader">是否计算IP校验和</param>
/// <param name="payload">是否计算荷载协议校验和</param>
public static unsafe void Checksum(byte* ptr, int length, bool payload = true)
public static unsafe void Checksum(byte* ptr, bool ipHeader = true, bool payload = true)
{
byte ipHeaderLength = (byte)((*ptr & 0b1111) * 4);
//重新计算IP头校验和
*(ushort*)(ptr + 10) = 0;
*(ushort*)(ptr + 10) = Checksum((ushort*)ptr, ipHeaderLength);
byte* packetPtr = ptr + ipHeaderLength;
uint totalLength = BinaryPrimitives.ReverseEndianness(*(ushort*)(ptr + 2));
uint packetLength = totalLength - ipHeaderLength;
if (ipHeader)
{
//重新计算IP头校验和
*(ushort*)(ptr + 10) = 0;
*(ushort*)(ptr + 10) = Checksum((ushort*)ptr, ipHeaderLength);
}
if (payload)
{
@@ -50,22 +55,22 @@ namespace linker.libs
{
case ProtocolType.Tcp:
{
*(ushort*)(ptr + ipHeaderLength + 16) = 0;
ulong sum = PseudoHeaderSum(ptr, (uint)(length - ipHeaderLength));
*(ushort*)(ptr + ipHeaderLength + 16) = Checksum((ushort*)(ptr + ipHeaderLength), (uint)length - ipHeaderLength, sum);
*(ushort*)(packetPtr + 16) = 0;
ulong sum = PseudoHeaderSum(ptr, packetLength);
*(ushort*)(packetPtr + 16) = Checksum((ushort*)(packetPtr), packetLength, sum);
}
break;
case ProtocolType.Udp:
{
*(ushort*)(ptr + ipHeaderLength + 6) = 0;
ulong sum = PseudoHeaderSum(ptr, (uint)(length - ipHeaderLength));
*(ushort*)(ptr + ipHeaderLength + 6) = Checksum((ushort*)(ptr + ipHeaderLength), (uint)length - ipHeaderLength, sum);
*(ushort*)(packetPtr + 6) = 0;
ulong sum = PseudoHeaderSum(ptr, packetLength);
*(ushort*)(packetPtr + 6) = Checksum((ushort*)(packetPtr), packetLength, sum);
}
break;
case ProtocolType.Icmp:
{
*(ushort*)(ptr + ipHeaderLength + 2) = 0;
*(ushort*)(ptr + ipHeaderLength + 2) = Checksum((ushort*)(ptr + ipHeaderLength), (uint)length - ipHeaderLength);
*(ushort*)(packetPtr + 2) = 0;
*(ushort*)(packetPtr + 2) = Checksum((ushort*)(packetPtr), packetLength);
}
break;
}

View File

@@ -7,7 +7,6 @@ using linker.messenger.relay.client;
using linker.messenger.signin;
using linker.messenger.pcp;
using linker.messenger.tuntap.cidr;
using System.Net.Sockets;
namespace linker.messenger.tuntap
{
@@ -45,11 +44,12 @@ namespace linker.messenger.tuntap
protected override void Connected(ITunnelConnection connection)
{
/*
if (connection.ProtocolType == TunnelProtocolType.Tcp && tuntapConfigTransfer.Info.FakeAck && tuntapDecenter.HasSwitchFlag(connection.RemoteMachineId, TuntapSwitch.FakeAck))
{
connection.PacketBuffer = new byte[4096];
connection.PacketBuffer = new byte[1];
}
*/
Add(connection);
connection.BeginReceive(this, null);
//有哪些目标IP用了相同目标隧道更新一下
@@ -67,7 +67,7 @@ namespace linker.messenger.tuntap
public async Task Receive(ITunnelConnection connection, ReadOnlyMemory<byte> buffer, object state)
#pragma warning restore CS1998 // 异步方法缺少 "await" 运算符,将以同步方式运行
{
if (connection.PacketBuffer.Length > 0) fakeAckTransfer.Write(buffer);
//if (connection.PacketBuffer.Length > 0) fakeAckTransfer.Write(buffer, connection.SendBufferFree);
Callback.Receive(connection, buffer);
}
/// <summary>
@@ -101,31 +101,33 @@ namespace linker.messenger.tuntap
//IPV4+IPV6 单播
uint ip = BinaryPrimitives.ReadUInt32BigEndian(packet.DistIPAddress.Span[^4..]);
if (tuntapCidrConnectionManager.TryGet(ip, out ITunnelConnection connection) == false || connection == null || connection.Connected == false)
if (tuntapCidrConnectionManager.TryGet(ip, out ITunnelConnection connection) && connection.Connected)
{
//开始操作,开始失败直接丢包
if (operatingMultipleManager.StartOperation(ip) == false)
{
return;
}
_ = ConnectTunnel(ip).ContinueWith((result, state) =>
{
//结束操作
operatingMultipleManager.StopOperation((uint)state);
//连接成功就缓存隧道
if (result.Result != null)
{
tuntapCidrConnectionManager.Add((uint)state, result.Result);
}
}, ip);
await connection.SendAsync(packet.Buffer, packet.Offset, packet.Length).ConfigureAwait(false);
return;
}
ushort ackLength = 0;
if (connection.PacketBuffer.Length > 0 && fakeAckTransfer.Read(packet.IPPacket, connection.PacketBuffer, connection.SendBufferFree, out ackLength)) return;
await connection.SendAsync(packet.Buffer, packet.Offset, packet.Length).ConfigureAwait(false);
if (ackLength > 0) Callback.Receive(connection, connection.PacketBuffer.AsMemory(0, ackLength));
/*
if (connection.PacketBuffer.Length > 0)
{
fakeAckTransfer.Read(packet.IPPacket);
}
*/
//开始操作,开始失败直接丢包
if (operatingMultipleManager.StartOperation(ip) == false)
{
return;
}
_ = ConnectTunnel(ip).ContinueWith((result, state) =>
{
//结束操作
operatingMultipleManager.StopOperation((uint)state);
//连接成功就缓存隧道
if (result.Result != null)
{
tuntapCidrConnectionManager.Add((uint)state, result.Result);
}
}, ip);
}

View File

@@ -1,5 +1,4 @@
using linker.libs;
using linker.libs.extends;
using System.Buffers;
using System.IO.Compression;
using System.Runtime.InteropServices;

View File

@@ -126,7 +126,7 @@ namespace linker.snat
if (checksum)
{
//计算校验和
ChecksumHelper.Checksum(ptr, packet.Length);
ChecksumHelper.Checksum(ptr);
}
}
}

View File

@@ -83,7 +83,7 @@ namespace linker.tun
//response
*(ushort*)(ptr + 20) = 0;
ChecksumHelper.Checksum(ptr, writableMemory.Length);
ChecksumHelper.Checksum(ptr);
Program.linkerTunDeviceAdapter.Write(string.Empty, writableMemory);
}
}

View File

@@ -1,7 +1,6 @@
using linker.libs;
using System.Buffers.Binary;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Net.Sockets;
namespace linker.tun
@@ -11,60 +10,35 @@ namespace linker.tun
/// </summary>
public unsafe sealed class FakeAckTransfer
{
private readonly ConcurrentDictionary<FaceAckKey, FackAckState> dic = new(new FackAckKeyComparer());
private readonly ConcurrentDictionary<FaceAckKey, int> dic = new(new FackAckKeyComparer());
/// <summary>
/// 发起方
/// </summary>
/// <param name="packet">一个完整的TCP/IP包</param>
/// <param name="fakeBuffer">一个能容纳ACK包的缓冲区如果需要伪造ACK则写入到这里</param>
/// <param name="bufferFree">缓冲区可用字节数会根据这个来计算ack的窗口大小</param>
/// <param name="fakeLength">ack包长度</param>
/// <returns>是否丢包</returns>
public bool Read(ReadOnlyMemory<byte> packet, ReadOnlyMemory<byte> fakeBuffer, long bufferFree, out ushort fakeLength)
/// <returns></returns>
public void Read(ReadOnlyMemory<byte> packet)
{
fakeLength = 0;
fixed (byte* ptr = packet.Span)
{
FakeAckPacket originPacket = new(ptr);
if (originPacket.Version != 4 || originPacket.Protocol != ProtocolType.Tcp)
{
return false;
return;
}
FaceAckKey key = new() { srcAddr = originPacket.SrcAddr, srcPort = originPacket.SrcPort, dstAddr = originPacket.DstAddr, dstPort = originPacket.DstPort };
if ((originPacket.IsOnlyAck || originPacket.IsPshAck) && dic.TryGetValue(key, out FackAckState state))
{
if (originPacket.TcpPayloadLength == 0)
{
return state.Ack++ > 0;
}
fixed (byte* pptr = fakeBuffer.Span)
{
ushort win = (ushort)Math.Max(Math.Min(bufferFree*0.8 / state.WindowScale, 32*1024), 4);
fakeLength = originPacket.ToAck(state.Seq, win, pptr);
if (new FakeAckPacket(pptr).Cq <= state.Cq)
{
fakeLength = 0;
}
}
}
else if (originPacket.TcpFlagFin || originPacket.TcpFlagRst)
if (originPacket.TcpFlagFin || originPacket.TcpFlagRst)
{
FaceAckKey key = new() { srcAddr = originPacket.SrcAddr, srcPort = originPacket.SrcPort, dstAddr = originPacket.DstAddr, dstPort = originPacket.DstPort };
dic.TryRemove(key, out _);
}
}
return false;
}
/// <summary>
/// 接收方
/// </summary>
/// <param name="packet">一个完整的TCP/IP包</param>
/// <returns></returns>
public void Write(ReadOnlyMemory<byte> packet)
public void Write(ReadOnlyMemory<byte> packet, long bufferFree)
{
fixed (byte* ptr = packet.Span)
{
@@ -75,27 +49,20 @@ namespace linker.tun
}
FaceAckKey key = new() { srcAddr = originPacket.SrcAddr, srcPort = originPacket.SrcPort, dstAddr = originPacket.DstAddr, dstPort = originPacket.DstPort };
/*
//更新序列号
if (originPacket.TcpFlagAck && dic.TryGetValue(key, out FackAckState state))
if (originPacket.IsPshAck || originPacket.IsOnlyAck)
{
state.Cq = originPacket.Cq;
if (originPacket.TcpPayloadLength > 0)
if (dic.TryGetValue(key, out int wins) && originPacket.Window > 0)
{
state.Seq = originPacket.Seq + (uint)originPacket.TcpPayloadLength;
ushort win = (ushort)Math.Max(Math.Min(bufferFree / wins, 65535), 4);
originPacket.WriteWindow(ptr, win);
}
}
else*/
if (originPacket.IsOnlySyn || originPacket.IsSynAck)
else if (originPacket.IsOnlySyn || originPacket.IsSynAck)
{
FackAckState state = new()
{
Ack = (ulong)(originPacket.IsOnlySyn ? 1 : 0),
Seq = originPacket.Seq + 1,
WindowScale = originPacket.FindOptionWindowScale(ptr + originPacket.IPHeadLength)
};
dic.AddOrUpdate(key, state, (a, b) => state);
int windowScale = originPacket.FindWindowScale(ptr);
dic.AddOrUpdate(key, windowScale, (a, b) => windowScale);
}
else if (originPacket.TcpFlagFin || originPacket.TcpFlagRst)
{
@@ -104,17 +71,6 @@ namespace linker.tun
}
}
/// <summary>
/// 状态
/// </summary>
sealed class FackAckState
{
public ulong Ack { get; set; }
public uint Seq { get; set; }
public uint Cq { get; set; }
public int WindowScale { get; set; }
}
/// <summary>
/// 四元组缓存key
/// </summary>
@@ -172,33 +128,14 @@ namespace linker.tun
/// </summary>
public readonly ushort DstPort => BinaryPrimitives.ReverseEndianness(*(ushort*)(ptr + IPHeadLength + 2));
/// <summary>
/// IP头长度
/// </summary>
public readonly int IPHeadLength => (*ptr & 0b1111) * 4;
/// <summary>
/// 序列号
/// 窗口大小
/// </summary>
public readonly uint Seq => BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + IPHeadLength + 4));
/// <summary>
/// 确认号
/// </summary>
public readonly uint Cq => BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + IPHeadLength + 8));
/// <summary>
/// TCP负载长度
/// </summary>
public readonly int TcpPayloadLength
{
get
{
int ipHeadLength = (*ptr & 0b1111) * 4;
int tcpHeaderLength = (*(ptr + ipHeadLength + 12) >> 4) * 4;
return BinaryPrimitives.ReverseEndianness(*(ushort*)(ptr + 2)) - ipHeadLength - tcpHeaderLength;
}
}
public readonly ushort Window => BinaryPrimitives.ReverseEndianness(*(ushort*)(ptr + IPHeadLength + 14));
/// <summary>
/// TCP Flag
@@ -225,70 +162,7 @@ namespace linker.tun
this.ptr = ptr;
}
/// <summary>
/// 制作一个ACK包
/// </summary>
/// <param name="seq">给定一个序列号可以从syn+ack包中+1获得</param>
/// <param name="winSize">窗口大小</param>
/// <param name="ipPtr">目标内存</param>
/// <returns></returns>
public readonly unsafe ushort ToAck(uint seq, ushort winSize, byte* ipPtr)
{
//复制一份IP+TCP头部
int ipHeaderLength = (*ptr & 0b1111) * 4;
int tcpHeaderLength = (*(ptr + ipHeaderLength + 12) >> 4) * 4;
ushort totalLength = BinaryPrimitives.ReverseEndianness(*(ushort*)(ipPtr + 2));
uint payloadLength = (uint)(totalLength - ipHeaderLength - tcpHeaderLength);
new Span<byte>(ptr, ipHeaderLength + tcpHeaderLength).CopyTo(new Span<byte>(ipPtr, ipHeaderLength + tcpHeaderLength));
//TCP头指针
byte* tcpPtr = ipPtr + ipHeaderLength;
//如果有时间戳,就填充时间戳选项
//FullOptionTimestamp(tcpPtr);
*(tcpPtr + 12) = 0b01010000;
//重新计算头部长度
tcpHeaderLength = (*(tcpPtr + 12) >> 4) * 4;
totalLength = (ushort)(ipHeaderLength + tcpHeaderLength);
//交换地址和端口
(*(uint*)(ipPtr + 16), *(uint*)(ipPtr + 12)) = (*(uint*)(ipPtr + 12), *(uint*)(ipPtr + 16));
(*(ushort*)(tcpPtr + 2), *(ushort*)(tcpPtr)) = (*(ushort*)(tcpPtr), *(ushort*)(tcpPtr + 2));
//设置总长度
*(ushort*)(ipPtr + 2) = BinaryPrimitives.ReverseEndianness(totalLength);
//重置分片相关信息
*(ushort*)(ipPtr + 4) = 0; // 清除分片偏移和标志
*(ushort*)(ipPtr + 6) = 0; // 清除更多分片标志
//源序列号
uint _seq = BinaryPrimitives.ReverseEndianness(*(uint*)(tcpPtr + 4));
//设置序列号
*(uint*)(tcpPtr + 4) = BinaryPrimitives.ReverseEndianness(seq);
//设置确认号
*(uint*)(tcpPtr + 8) = BinaryPrimitives.ReverseEndianness(_seq + payloadLength);
//设置TCP标志位为ACK其他标志位清除
*(tcpPtr + 13) = 0b00010000;
//设置窗口大小
*(ushort*)(tcpPtr + 14) = BinaryPrimitives.ReverseEndianness(Math.Max(winSize, (ushort)8));
//计算校验和
ChecksumHelper.Checksum(ipPtr, totalLength);
//只需要IP头+TCP头
return totalLength;
}
/// <summary>
/// 从TCP的SYN包或SYN+ACK包中获取窗口缩放比例
/// </summary>
/// <param name="ipPtr">一个完整TCP/IP包</param>
/// <returns></returns>
public int FindOptionWindowScale(byte* ipPtr)
public int FindWindowScale(byte* ipPtr)
{
//指针移动到TCP头开始位置
byte* tcpPtr = ipPtr + ((*ipPtr & 0b1111) * 4);
@@ -316,51 +190,55 @@ namespace linker.tun
//Window Scale 1kind 1length 1shiftCount
else if (kind == 3 && length == 3)
{
byte shiftCount = *(tcpPtr + index + 2);
return shiftCount > 14 ? 1 : 1 << shiftCount;
return *(tcpPtr + index + 2);
}
index += length;
}
return 1;
return 0;
}
private void FullOptionTimestamp(byte* tcpPtr)
public int WriteWindowScale(byte* ipPtr, byte windowScale = 7)
{
int index = 20, end = (*(tcpPtr + 12) >> 4) * 4;
//找时间戳
uint timestampValue = 0;
//指针移动到TCP头开始位置
byte* tcpPtr = ipPtr + ((*ipPtr & 0b1111) * 4);
//tcp头固定20所以option从这里开始
int index = 20;
//tcp头结束位置就是option结束位置
int end = (*(tcpPtr + 12) >> 4) * 4;
while (index < end)
{
byte kind = *(tcpPtr + index);
if (kind == 0) break;
//EOF结束符
if (kind == 0)
{
break;
}
byte length = *(tcpPtr + index + 1);
//NOP 空选项
if (kind == 1)
{
index++;
continue;
}
else if (kind == 8 && length == 10)
//Window Scale 1kind 1length 1shiftCount
else if (kind == 3 && length == 3)
{
timestampValue = *(uint*)(tcpPtr + index + 2);
break;
if (*(tcpPtr + index + 2) < windowScale)
{
*(tcpPtr + index + 2) = windowScale;
ChecksumHelper.Checksum(ipPtr, false, true);
}
return *(tcpPtr + index + 2);
}
index += length;
}
// TCP头长度
if (timestampValue > 0) //有时间戳选项有选项8个32位字32字节12字节OPTIONS
{
*(tcpPtr + 12) = 0b10000000;
*(tcpPtr + 20) = 0x01; //NOP
*(tcpPtr + 21) = 0x01; //NOP
*(tcpPtr + 22) = 0x08; //kind timestamp
*(tcpPtr + 23) = 0x0A; //length 10
*(uint*)(tcpPtr + 24) = (uint)(Stopwatch.GetTimestamp() / (Stopwatch.Frequency / 1000)); //val
*(uint*)(tcpPtr + 28) = timestampValue; //ecr 10
}
else //没有时间戳就没有选项5个32位字,20字节
{
*(tcpPtr + 12) = 0b01010000;
}
return 0;
}
public void WriteWindow(byte* ipPtr, ushort window)
{
*(ushort*)(ipPtr + ((*ipPtr & 0b1111) * 4) + 14) = BinaryPrimitives.ReverseEndianness(Math.Max(window, (ushort)8));
ChecksumHelper.Checksum(ipPtr, false, true);
}
}
}

View File

@@ -326,7 +326,7 @@ namespace linker.tun
Span<byte> ipPacket = Buffer.AsSpan(Offset + 4);
if (Version == 4) TTL = --ipPacket[8];
else if (Version == 6) TTL = --ipPacket[7];
ChecksumHelper.Checksum(ipPacket, false);
ChecksumHelper.Checksum(ipPacket, true, false);
return true;

View File

@@ -157,6 +157,15 @@ namespace linker.tunnel.connection
/// </summary>
public long SendBufferFree { get; }
/// <summary>
/// 发送缓冲区剩余大小
/// </summary>
public long RecvBufferRemaining { get; }
/// <summary>
/// 发送缓冲区剩余比例
/// </summary>
public long RecvBufferFree { get; }
/// <summary>
/// 最后通信时间
/// </summary>

View File

@@ -41,6 +41,8 @@ namespace linker.tunnel.connection
public long ReceiveBytes { get; private set; }
public long SendBufferRemaining { get; }
public long SendBufferFree { get; } = 512 * 1024;
public long RecvBufferRemaining { get; }
public long RecvBufferFree { get; } = 512 * 1024;
public LastTicksManager LastTicks { get; private set; } = new LastTicksManager();

View File

@@ -40,8 +40,12 @@ namespace linker.tunnel.connection
private long sendRemaining = 0;
public long SendBufferRemaining { get => sendRemaining; }
public long SendBufferFree { get => maxSendRemaining - sendRemaining; }
private const long maxSendRemaining = 1 * 1024 * 1024;
public long SendBufferFree { get => maxRemaining - sendRemaining; }
private const long maxRemaining = 1 * 1024 * 1024;
private long recvRemaining = 0;
public long RecvBufferRemaining { get => recvRemaining; }
public long RecvBufferFree { get => maxRemaining - recvRemaining; }
public LastTicksManager LastTicks { get; private set; } = new LastTicksManager();
@@ -80,8 +84,8 @@ namespace linker.tunnel.connection
cancellationTokenSource = new CancellationTokenSource();
pipeSender = new Pipe(new PipeOptions(pauseWriterThreshold: maxSendRemaining, resumeWriterThreshold: (maxSendRemaining / 2), useSynchronizationContext: false));
pipeWriter = new Pipe(new PipeOptions(pauseWriterThreshold: maxSendRemaining, resumeWriterThreshold: (maxSendRemaining / 2), useSynchronizationContext: false));
pipeSender = new Pipe(new PipeOptions(pauseWriterThreshold: maxRemaining, resumeWriterThreshold: (maxRemaining / 2), useSynchronizationContext: false));
pipeWriter = new Pipe(new PipeOptions(pauseWriterThreshold: maxRemaining, resumeWriterThreshold: (maxRemaining / 2), useSynchronizationContext: false));
_ = ProcessWrite();
_ = Sender();
_ = Recver();
@@ -99,10 +103,8 @@ namespace linker.tunnel.connection
{
Memory<byte> memory = pipeWriter.Writer.GetMemory(8 * 1024);
length = await Stream.ReadAsync(memory).ConfigureAwait(false);
if (length == 0)
{
break;
}
if (length == 0) break;
Interlocked.Add(ref recvRemaining, length);
pipeWriter.Writer.Advance(length);
await pipeWriter.Writer.FlushAsync().ConfigureAwait(false);
}
@@ -111,6 +113,7 @@ namespace linker.tunnel.connection
Memory<byte> memory = pipeWriter.Writer.GetMemory(8 * 1024);
length = await Socket.ReceiveAsync(memory, SocketFlags.None).ConfigureAwait(false);
if (length == 0) break;
Interlocked.Add(ref recvRemaining, length);
pipeWriter.Writer.Advance(length);
await pipeWriter.Writer.FlushAsync().ConfigureAwait(false);
}
@@ -173,6 +176,7 @@ namespace linker.tunnel.connection
temp.CopyTo(packetBuffer.Memory.Span);
//处理数据包
await WritePacket(packetBuffer.Memory.Slice(0, packetLength)).ConfigureAwait(false);
Interlocked.Add(ref recvRemaining, -(packetLength + 4));
//移动位置
offset += 4 + packetLength;
@@ -303,6 +307,7 @@ namespace linker.tunnel.connection
} while (sendt < memoryBlock.Length);
}
Interlocked.Add(ref sendRemaining, -memoryBlock.Length);
SendBytes += memoryBlock.Length;
}
pipeSender.Reader.AdvanceTo(buffer.End);
LastTicks.Update();

View File

@@ -39,6 +39,9 @@ namespace linker.tunnel.connection
public long ReceiveBytes { get; private set; }
public long SendBufferRemaining { get; }
public long SendBufferFree { get; } = 512 * 1024;
public long RecvBufferRemaining { get; }
public long RecvBufferFree { get; } = 512 * 1024;
public LastTicksManager LastTicks { get; private set; } = new LastTicksManager();
[JsonIgnore]

View File

@@ -19,7 +19,7 @@
<template #default="scope">
<div>
<p>{{ scope.row.TransportName }}({{ state.protocolTypes[scope.row.ProtocolType] }})</p>
<p>{{ state.types[scope.row.Type] }} - {{ scope.row.SendBufferRemainingText }}</p>
<p>{{ state.types[scope.row.Type] }} - {{ scope.row.SendBufferRemainingText }} - {{ scope.row.RecvBufferRemainingText }}</p>
</div>
</template>
</el-table-column>

View File

@@ -102,6 +102,7 @@ export const provideConnections = () => {
connection.SendBytesText = parseSpeed(connection.SendBytes - cache.SendBytes,'/s');
connection.ReceiveBytesText = parseSpeed(connection.ReceiveBytes - cache.ReceiveBytes,'/s');
connection.SendBufferRemainingText = parseSpeed(connection.SendBufferRemaining,'');
connection.RecvBufferRemainingText = parseSpeed(connection.RecvBufferRemaining || 0,'');
cache.SendBytes = connection.SendBytes;
cache.ReceiveBytes = connection.ReceiveBytes;

View File

@@ -21,7 +21,7 @@
<el-checkbox class="mgr-1" v-model="state.ruleForm.DisableNat" label="禁用NAT" size="large" />
<!-- <el-checkbox class="mgr-1" v-model="state.ruleForm.TcpMerge" label="TCP包合并" size="large" /> -->
<el-checkbox class="mgr-1" v-model="state.ruleForm.InterfaceOrder" label="网卡顺序" size="large" />
<el-checkbox v-model="state.ruleForm.FakeAck" label="伪ACK" size="large" />
<!-- <el-checkbox v-model="state.ruleForm.FakeAck" label="伪ACK" size="large" /> -->
</el-form-item>
</el-form>
</div>

View File

@@ -1,5 +1,5 @@
v1.9.1
2025-09-17 18:03:28
2025-09-19 16:42:33
1. 一些累计更新
2. 服务器转发多节点
3. 虚拟网卡下伪造ACK为TCP-in-TCP隧道提速