This commit is contained in:
snltty
2024-09-27 17:23:47 +08:00
parent 3815546b77
commit 40e857585d
43 changed files with 953 additions and 314 deletions

View File

@@ -37,7 +37,7 @@ jobs:
release_name: v1.4.4.${{ steps.date.outputs.today }}
draft: false
prerelease: false
body: 1. 增加流量统计(暂时显示服务流量)
body: "1. 总览,和详细流量统计,一眼知道服务流量花在哪里\r\n2. 优化信标。减少流量,没有操作时尽量不产生流量"
- name: upload-win-x86-oss
id: upload-win-x86-oss
uses: tvrcgo/oss-action@v0.1.1

View File

@@ -0,0 +1,45 @@
using System;
namespace linker.libs
{
public sealed class LastTicksManager
{
private long ticks = Environment.TickCount64;
public void Update()
{
ticks = Environment.TickCount64;
}
public bool Less(long ms)
{
return Environment.TickCount64 - ticks <= ms;
}
public bool Greater(long ms)
{
return Environment.TickCount64 - ticks > ms;
}
public bool Equal(long ms)
{
return ticks == ms;
}
public bool NotEqual(long ms)
{
return ticks != ms;
}
public long Diff()
{
return Environment.TickCount64 - ticks;
}
public bool Timeout(long ms)
{
return ticks == 0 || Environment.TickCount64 - ticks > ms;
}
public void Clear()
{
ticks = 0;
}
}
}

View File

@@ -1,4 +1,5 @@
using System.Net;
using linker.libs;
using System.Net;
namespace linker.tunnel.connection
{
@@ -139,7 +140,7 @@ namespace linker.tunnel.connection
/// <summary>
/// 最后通信时间
/// </summary>
public long LastTicks { get; }
public LastTicksManager LastTicks { get; }
/// <summary>
/// 发送ping

View File

@@ -32,12 +32,12 @@ namespace linker.tunnel.connection
public byte BufferSize { get; init; } = 3;
public bool Connected => Stream != null && Stream.CanWrite && LastTicks > 0;
public bool Connected => Stream != null && Stream.CanWrite && LastTicks.NotEqual(0);
public int Delay { get; private set; }
public long SendBytes { get; private set; }
public long ReceiveBytes { get; private set; }
public long LastTicks { get; private set; } = Environment.TickCount64;
public LastTicksManager LastTicks { get; private set; } = new LastTicksManager();
[JsonIgnore]
public QuicStream Stream { get; init; }
@@ -56,7 +56,7 @@ namespace linker.tunnel.connection
private bool framing;
private ReceiveDataBuffer bufferCache = new ReceiveDataBuffer();
private long pingStart = Environment.TickCount64;
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;
@@ -148,7 +148,7 @@ namespace linker.tunnel.connection
private async Task CallbackPacket(Memory<byte> packet)
{
ReceiveBytes += packet.Length;
LastTicks = Environment.TickCount64;
LastTicks.Update();
if (packet.Length == pingBytes.Length && (packet.Span.SequenceEqual(pingBytes) || packet.Span.SequenceEqual(pongBytes)))
{
if (packet.Span.SequenceEqual(pingBytes))
@@ -157,7 +157,7 @@ namespace linker.tunnel.connection
}
else if (packet.Span.SequenceEqual(pongBytes))
{
Delay = (int)(Environment.TickCount64 - pingStart);
Delay = (int)pingTicks.Diff();
pong = true;
}
}
@@ -179,9 +179,9 @@ namespace linker.tunnel.connection
{
while (cancellationTokenSource.IsCancellationRequested == false)
{
if (Environment.TickCount64 - LastTicks > 3000)
if (LastTicks.Greater(3000))
{
pingStart = Environment.TickCount64;
pingTicks.Update();
await SendPingPong(pingBytes).ConfigureAwait(false);
}
await Task.Delay(3000).ConfigureAwait(false);
@@ -223,7 +223,7 @@ namespace linker.tunnel.connection
{
if (pong == false) return;
pong = false;
pingStart = Environment.TickCount64;
pingTicks.Update();
await SendPingPong(pingBytes).ConfigureAwait(false);
}
private SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1);
@@ -254,7 +254,7 @@ namespace linker.tunnel.connection
public void Dispose()
{
LastTicks = 0;
LastTicks.Clear();
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
LoggerHelper.Instance.Error($"tunnel connection writer offline {ToString()}");

View File

@@ -27,12 +27,12 @@ namespace linker.tunnel.connection
public IPEndPoint IPEndPoint { get; init; }
public bool SSL { get; init; }
public byte BufferSize { get; init; } = 3;
public bool Connected => Socket != null && LastTicks > 0 && Environment.TickCount64 - LastTicks < 15000;
public bool Connected => Socket != null && LastTicks.Timeout(15000) == false;
public int Delay { get; private set; }
public long SendBytes { get; private set; }
public long ReceiveBytes { get; private set; }
public long LastTicks { get; private set; } = Environment.TickCount64;
public LastTicksManager LastTicks { get; private set; } = new LastTicksManager();
[JsonIgnore]
public SslStream Stream { get; init; }
@@ -47,7 +47,7 @@ namespace linker.tunnel.connection
private bool framing;
private ReceiveDataBuffer bufferCache = new ReceiveDataBuffer();
private long pingStart = Environment.TickCount64;
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;
@@ -157,7 +157,7 @@ namespace linker.tunnel.connection
private async Task CallbackPacket(Memory<byte> packet)
{
ReceiveBytes += packet.Length;
LastTicks = Environment.TickCount64;
LastTicks.Update();
if (packet.Length == pingBytes.Length)
{
if (packet.Span.SequenceEqual(pingBytes))
@@ -166,7 +166,7 @@ namespace linker.tunnel.connection
}
else if (packet.Span.SequenceEqual(pongBytes))
{
Delay = (int)(Environment.TickCount64 - pingStart);
Delay = (int)pingTicks.Diff();
pong = true;
}
return;
@@ -192,9 +192,9 @@ namespace linker.tunnel.connection
break;
}
if (Environment.TickCount64 - LastTicks > 3000)
if (LastTicks.Greater(3000))
{
pingStart = Environment.TickCount64;
pingTicks.Update();
await SendPingPong(pingBytes).ConfigureAwait(false);
}
@@ -246,7 +246,7 @@ namespace linker.tunnel.connection
{
if (pong == false) return;
pong = false;
pingStart = Environment.TickCount64;
pingTicks.Update();
await SendPingPong(pingBytes).ConfigureAwait(false);
}
public async Task<bool> SendAsync(ReadOnlyMemory<byte> data)
@@ -286,7 +286,7 @@ namespace linker.tunnel.connection
public void Dispose()
{
LastTicks = 0;
LastTicks.Clear();
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
LoggerHelper.Instance.Error($"tunnel connection {this.GetHashCode()} writer offline {ToString()}");

View File

@@ -27,11 +27,11 @@ namespace linker.tunnel.connection
public bool SSL { get; init; }
public byte BufferSize { get; init; } = 3;
public bool Connected => UdpClient != null && LastTicks > 0 && Environment.TickCount64 - LastTicks < 15000;
public bool Connected => UdpClient != null && LastTicks.Timeout(15000) == false;
public int Delay { get; private set; }
public long SendBytes { get; private set; }
public long ReceiveBytes { get; private set; }
public long LastTicks { get; private set; } = Environment.TickCount64;
public LastTicksManager LastTicks { get; private set; } = new LastTicksManager();
public bool Receive { get; init; }
@@ -56,7 +56,7 @@ namespace linker.tunnel.connection
private CancellationTokenSource cancellationTokenSource;
private object userToken;
private long pingStart = Environment.TickCount64;
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");
@@ -129,7 +129,7 @@ namespace linker.tunnel.connection
private async Task CallbackPacket(Memory<byte> packet)
{
ReceiveBytes += packet.Length;
LastTicks = Environment.TickCount64;
LastTicks.Update();
Memory<byte> memory = packet.Slice(4);
if (memory.Length == pingBytes.Length && memory.Span.Slice(0, pingBytes.Length - 4).SequenceEqual(pingBytes.AsSpan(0, pingBytes.Length - 4)))
@@ -140,7 +140,7 @@ namespace linker.tunnel.connection
}
else if (memory.Span.SequenceEqual(pongBytes))
{
Delay = (int)(Environment.TickCount64 - pingStart);
Delay = (int)pingTicks.Diff();
pong = true;
}
else if (memory.Span.SequenceEqual(finBytes))
@@ -178,9 +178,9 @@ namespace linker.tunnel.connection
break;
}
if (Environment.TickCount64 - LastTicks > 3000)
if (LastTicks.Greater(3000))
{
pingStart = Environment.TickCount64;
pingTicks.Update();
await SendPingPong(pingBytes).ConfigureAwait(false);
}
await Task.Delay(3000).ConfigureAwait(false);
@@ -218,7 +218,7 @@ namespace linker.tunnel.connection
{
if (pong == false) return;
pong = false;
pingStart = Environment.TickCount64;
pingTicks.Update();
await SendPingPong(pingBytes).ConfigureAwait(false);
}
@@ -258,7 +258,7 @@ namespace linker.tunnel.connection
SendPingPong(finBytes).ContinueWith((result) =>
{
LastTicks = 0;
LastTicks.Clear();
if (Receive == true)
UdpClient?.SafeClose();
uUdpClient = null;

View File

@@ -351,8 +351,7 @@ namespace linker.tunnel.transport
{
TimerHelper.SetInterval(() =>
{
long ticks = Environment.TickCount64;
var keys = connectionsDic.Where(c => (c.Value.Connection == null && ticks - c.Value.LastTicks > 5000) || (c.Value.Connection != null && c.Value.Connection.Connected == false)).Select(c => c.Key).ToList();
var keys = connectionsDic.Where(c => (c.Value.Connection == null && c.Value.LastTicks.Greater(5000)) || (c.Value.Connection != null && c.Value.Connection.Connected == false)).Select(c => c.Key).ToList();
foreach (var item in keys)
{
connectionsDic.TryRemove(item, out _);
@@ -370,7 +369,7 @@ namespace linker.tunnel.transport
public sealed class ConnectionCacheInfo
{
public long LastTicks { get; set; } = Environment.TickCount64;
public LastTicksManager LastTicks { get; set; } = new LastTicksManager();
public TunnelConnectionUdp Connection { get; set; }
}

View File

@@ -3,3 +3,9 @@ import { sendWebsocketMsg } from './request'
export const getFlows = () => {
return sendWebsocketMsg('flowClient/GetFlows');
}
export const getMessengerFlows = () => {
return sendWebsocketMsg('flowClient/GetMessengerFlows');
}
export const getSForwardFlows = (data) => {
return sendWebsocketMsg('flowClient/GetSForwardFlows', data);
}

View File

@@ -23,3 +23,7 @@ export const updateTuntap = (name) => {
export const refreshTuntap = () => {
return sendWebsocketMsg('tuntapclient/refresh');
}
export const subscribePing = () => {
return sendWebsocketMsg('tuntapclient/SubscribePing');
}

View File

@@ -31,3 +31,6 @@ export const confirmServer = (version) => {
export const exitServer = () => {
return sendWebsocketMsg('updaterclient/exitserver');
}
export const subscribeUpdater = () => {
return sendWebsocketMsg('updaterclient/Subscribe');
}

View File

@@ -81,7 +81,7 @@ export default {
handleTunnelConnections,clearConnectionsTimeout
} = provideConnections();
const {_getUpdater,clearUpdaterTimeout} = provideUpdater();
const {_getUpdater,_subscribeUpdater,clearUpdaterTimeout} = provideUpdater();
const {_getAccessInfo,clearAccessTimeout} = provideAccess();
@@ -158,6 +158,7 @@ export default {
_getSForwardInfo();
_getUpdater();
_subscribeUpdater();
_getAccessInfo();

View File

@@ -9,17 +9,14 @@
<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: 3rem;"></span>
<span style="width: 2rem;"></span>
<el-checkbox v-model="state.ruleForm.ShowDelay" label="显示延迟" size="large" />
<el-popover
placement="top" title="提示" :width="400" trigger="hover"
content="在测试延迟时如果未连接将自动去打洞连接当你有一百个设备时每个设备都同时去与其它99台设备连接这数据量不小所以尽量不要个设备都勾选"
>
<el-checkbox v-model="state.ruleForm.AutoConnect" label="自动连接" size="large" />
<!-- <el-popover placement="top" title="提示" :width="400" trigger="hover" content="当有大量客户端使用中继是,广播会使用服务器更多流量" >
<template #reference>
<el-checkbox v-model="state.ruleForm.AutoConnect" label="自动连接?" size="large" />
<el-checkbox v-model="state.ruleForm.Multicast" label="启用广播" size="large" />
</template>
</el-popover>
</el-popover> -->
</el-form-item>
<el-form-item prop="upgrade" style="margin-bottom:0">
<el-checkbox v-model="state.ruleForm.Upgrade" label="我很懂,我要使用高级功能(点对网和网对网)" size="large" />
@@ -104,6 +101,7 @@ export default {
ShowDelay: tuntap.value.current.ShowDelay,
AutoConnect: tuntap.value.current.AutoConnect,
Upgrade: tuntap.value.current.Upgrade,
Multicast: tuntap.value.current.Multicast,
Forwards:tuntap.value.current.Forwards.length == 0 ? [
{ListenAddr:'0.0.0.0',ListenPort:0,ConnectAddr:'0.0.0.0',ConnectPort:0}
@@ -165,6 +163,7 @@ export default {
json.ShowDelay = state.ruleForm.ShowDelay;
json.AutoConnect = state.ruleForm.AutoConnect;
json.Upgrade = state.ruleForm.Upgrade;
json.Multicast = state.ruleForm.Multicast;
json.Forwards = state.ruleForm.Forwards;
json.Forwards.forEach(c=>{
c.ListenPort=+c.ListenPort;

View File

@@ -1,7 +1,7 @@
import { injectGlobalData } from "@/provide";
import { ElMessage } from "element-plus";
import { inject, provide, ref } from "vue"
import { getTuntapInfo, refreshTuntap } from "@/apis/tuntap";
import { getTuntapInfo, refreshTuntap, subscribePing } from "@/apis/tuntap";
const tuntapSymbol = Symbol();
export const provideTuntap = () => {
@@ -57,6 +57,7 @@ export const provideTuntap = () => {
tuntap.value.list = res.List;
}
tuntap.value.timer = setTimeout(_getTuntapInfo, 1100);
subscribePing();
}).catch((e) => {
tuntap.value.timer = setTimeout(_getTuntapInfo, 1100);
});

View File

@@ -1,4 +1,4 @@
import { getUpdater } from "@/apis/updater";
import { getUpdater, subscribeUpdater } from "@/apis/updater";
import { injectGlobalData } from "@/provide";
import { inject, provide, ref } from "vue";
@@ -9,7 +9,9 @@ export const provideUpdater = () => {
timer: 0,
list: {},
hashcode: 0,
current: { Version: '', Msg: [], DateTime: '', Status: 0, Length: 0, Current: 0 }
current: { Version: '', Msg: [], DateTime: '', Status: 0, Length: 0, Current: 0 },
subscribeTimer: 0
});
provide(updaterSymbol, updater);
const _getUpdater = () => {
@@ -36,15 +38,23 @@ export const provideUpdater = () => {
updater.value.timer = setTimeout(_getUpdater, 800);
});
}
const _subscribeUpdater = () => {
subscribeUpdater().then(() => {
updater.value.subscribeTimer = setTimeout(_subscribeUpdater, 5000);
}).catch(() => {
updater.value.subscribeTimer = setTimeout(_subscribeUpdater, 5000);
});
}
const clearUpdaterTimeout = () => {
clearTimeout(updater.value.timer);
clearTimeout(updater.value.subscribeTimer);
}
return {
updater, _getUpdater, clearUpdaterTimeout
updater, _getUpdater, _subscribeUpdater, clearUpdaterTimeout
}
}
export const useUpdater = () => {

View File

@@ -6,7 +6,7 @@
<el-dialog :title="state.time" destroy-on-close v-model="state.show" width="540">
<div>
<el-table :data="state.list" border size="small" width="100%">
<el-table-column prop="id" label="类别" width="80"></el-table-column>
<el-table-column prop="text" label="类别" width="80"></el-table-column>
<el-table-column prop="sendtBytes" label="已上传" sortable>
<template #default="scope">
<span>{{ scope.row.sendtBytesText }}</span>
@@ -29,21 +29,24 @@
</el-table-column>
<el-table-column prop="oper" label="操作" width="64">
<template #default="scope">
<el-button v-if="scope.row.detail" size="small">详情</el-button>
<el-button v-if="scope.row.detail" size="small" @click="handleShowDetail(scope.row.id)">详情</el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-dialog>
<ServerFlowMessenger :config="config" v-if="state.details.Messenger" v-model="state.details.Messenger"></ServerFlowMessenger>
<ServerFlowSForward :config="config" v-if="state.details.SForward" v-model="state.details.SForward"></ServerFlowSForward>
</template>
<script>
import { getFlows } from '@/apis/flow';
import { getTunnelRecords } from '@/apis/tunnel';
import { onMounted, onUnmounted, reactive } from 'vue';
import ServerFlowMessenger from './ServerFlowMessenger.vue';
import ServerFlowSForward from './ServerFlowSForward.vue';
export default {
props:['config'],
components:{ServerFlowMessenger,ServerFlowSForward},
setup (props) {
const state = reactive({
@@ -53,105 +56,51 @@ export default {
overallReceiveSpeed: '0000.00KB',
time:'',
list:[],
old:null
old:null,
details:{
Messenger:false,
SForward:false,
}
});
const handleShow = ()=>{
state.show = true;
}
const details = {
'Relay':true,
'Messenger':true,
'SForward':true,
};
const id2text = {
'External':'外网端口',
'Relay':'中继流量',
'Messenger':'信标流量',
'SForward':'内网穿透',
'0':'[信标]登入信标',
'1':'[信标]客户端列表',
'2':'[信标]客户端删除',
'4':'[信标]客户端改名(转发)',
'7':'[信标]获取服务器版本',
'8':'[信标]客户端搜索ids',
'9':'[信标]客户端id列表',
'10':'[信标]客户端排序',
'11':'[信标]客户端在线',
'12':'[信标]生成客户端id',
'13':'[信标]登入信标V_1_3_1',
'2001':'[信标]外网端口(转发)',
'2002':'[信标]外网端口(转发)',
'2003':'[信标]开始打洞(转发)',
'2004':'[信标]开始打洞(转发)',
'2005':'[信标]打洞失败(转发)',
'2006':'[信标]打洞失败(转发)',
'2007':'[信标]打洞成功(转发)',
'2008':'[信标]打洞成功(转发)',
'2009':'[信标]隧道配置(转发)',
'2010':'[信标]隧道配置(转发)',
'2012':'[信标]隧道同步(转发)',
'2101':'[信标]中继通知(转发)',
'2102':'[信标]中继通知(转发)',
'2103':'[信标]中继请求',
'2105':'[信标]中继连通测试',
'2201':'[信标]运行网卡(转发)',
'2203':'[信标]停止网卡(转发)',
'2205':'[信标]更新网卡(转发)',
'2206':'[信标]同步网卡(转发)',
'2207':'[信标]同步网卡(转发)',
'2301':'[信标]添加内网穿透',
'2302':'[信标]移除内网穿透',
'2305':'[信标]获取穿透列表(转发)',
'2401':'[信标]测试端口转发(转发)',
'2403':'[信标]获取端口转发(转发)',
'2503':'[信标]获取权限(转发)',
'2504':'[信标]获取权限(转发)',
'2506':'[信标]更新权限(转发)',
'2508':'[信标]同步密钥(转发)',
'2510':'[信标]同步服务器(转发)',
'2601':'[信标]更新信息(转发)',
'2602':'[信标]更新信息(转发)',
'2603':'[信标]确认更新(转发)',
'2604':'[信标]确认更新(转发)',
'2605':'[信标]重启(转发)',
'2607':'[信标]服务器更新信息',
'2608':'[信标]确认服务器更新',
'2609':'[信标]服务器重启',
'2701':'[信标]获取服务器流量',
const handleShowDetail = (id)=>{
state.details[id] = true;
}
const id2text = {
'External':{text:'外网端口',detail:false},
'Relay':{text:'中继',detail:true},
'Messenger':{text:'信标',detail:true},
'SForward':{text:'内网穿透',detail:true},
};
const _getFlows = ()=>{
getFlows().then(res => {
const old = state.old || res;
let _receiveBytes = 0,_sendtBytes = 0,receiveBytes = 0,sendtBytes = 0;
for(let j in old.Resolvers){
_receiveBytes+=old.Resolvers[j].ReceiveBytes;
_sendtBytes+=old.Resolvers[j].SendtBytes;
for(let j in old.Items){
_receiveBytes+=old.Items[j].ReceiveBytes;
_sendtBytes+=old.Items[j].SendtBytes;
}
for(let j in res.Resolvers){
receiveBytes+=res.Resolvers[j].ReceiveBytes;
sendtBytes+=res.Resolvers[j].SendtBytes;
for(let j in res.Items){
receiveBytes+=res.Items[j].ReceiveBytes;
sendtBytes+=res.Items[j].SendtBytes;
}
state.overallSendtSpeed = parseSpeed(sendtBytes-_sendtBytes);
state.overallReceiveSpeed = parseSpeed(receiveBytes-_receiveBytes);
state.time = `${res.Start}启动 至今`;
const list = [];
for(let j in res.Resolvers){
const item = res.Resolvers[j];
const itemOld = old.Resolvers[j];
for(let j in res.Items){
const item = res.Items[j];
const itemOld = old.Items[j];
const text = id2text[`${j}`] || {text:'未知',detail:false};
list.push({
id:id2text[`${j}`],
detail:details[`${j}`] || false,
id:j,
text:text.text,
detail:text.detail,
sendtBytes:item.SendtBytes,
sendtBytesText:parseSpeed(item.SendtBytes),
@@ -166,24 +115,6 @@ export default {
receiveSpeedText:parseSpeed(item.ReceiveBytes-itemOld.ReceiveBytes),
});
}
// for(let j in res.Messangers){
// const item = res.Messangers[j];
// const itemOld = old.Messangers[j];
// list.push({
// id:id2text[`${j}`] || `未知的${j}`,
// sendtBytes:item.SendtBytes,
// sendtBytesText:parseSpeed(item.SendtBytes),
// sendtSpeed:item.SendtBytes-itemOld.SendtBytes,
// sendtSpeedText:parseSpeed(item.SendtBytes-itemOld.SendtBytes),
// receiveBytes:item.ReceiveBytes,
// receiveBytesText:parseSpeed(item.ReceiveBytes),
// receiveSpeed:item.ReceiveBytes-itemOld.ReceiveBytes,
// receiveSpeedText:parseSpeed(item.ReceiveBytes-itemOld.ReceiveBytes),
// });
// }
state.list = list.filter(c=>!!c.id);
state.old = res;
@@ -201,18 +132,6 @@ export default {
return `${num.toFixed(2)}${['B', 'KB', 'MB', 'GB', 'TB'][index]}`;
}
const _getTunnelRecords = ()=>{
getTunnelRecords().then(res => {
console.log(Object.values(res).map(c=>{
c.To = Object.values(c.To).sort((a,b)=>b.Times-a.Times);
return c;
}).sort((a,b)=>b.Times-a.Times));
setTimeout(_getTunnelRecords,1000);
}).catch((e)=>{
setTimeout(_getTunnelRecords,1000);
});
}
onMounted(()=>{
_getFlows();
//_getTunnelRecords();
@@ -223,7 +142,7 @@ export default {
return {
config:props.config,state,handleShow
config:props.config,state,handleShow,handleShowDetail
}
}
}

View File

@@ -0,0 +1,189 @@
<template>
<el-dialog title="信标流量" class="options-center" top="1vh" destroy-on-close v-model="state.show" width="680">
<div>
<el-table :data="state.list" stripe border size="small" width="100%" height="60vh">
<el-table-column prop="id" label="信标id" width="200"></el-table-column>
<el-table-column prop="sendtBytes" label="已上传" sortable>
<template #default="scope">
<span>{{ scope.row.sendtBytesText }}</span>
</template>
</el-table-column>
<el-table-column prop="sendtSpeed" label="上传速度" sortable>
<template #default="scope">
<span>{{ scope.row.sendtSpeedText }}/s</span>
</template>
</el-table-column>
<el-table-column prop="receiveBytes" label="已下载" sortable>
<template #default="scope">
<span>{{ scope.row.receiveBytesText }}</span>
</template>
</el-table-column>
<el-table-column prop="receiveSpeed" label="下载速度" sortable>
<template #default="scope">
<span>{{ scope.row.receiveSpeedText }}/s</span>
</template>
</el-table-column>
</el-table>
</div>
</el-dialog>
</template>
<script>
import { getMessengerFlows } from '@/apis/flow';
import { onMounted, onUnmounted, reactive, watch } from 'vue';
export default {
props: ['modelValue','config'],
emits: ['update:modelValue'],
setup (props,{emit}) {
const state = reactive({
show:true,
timer:0,
list:[],
old:null
});
watch(() => state.show, (val) => {
if (!val) {
setTimeout(() => {
emit('update:modelValue', val);
}, 300);
}
});
const id2text = {
'0':'登入信标',
'1':'客户端列表',
'2':'客户端删除',
'4':'客户端改名(转发)',
'7':'获取服务器版本',
'8':'客户端搜索ids',
'9':'客户端id列表',
'10':'客户端排序',
'11':'客户端在线',
'12':'生成客户端id',
'13':'登入信标V_1_3_1',
'2001':'外网端口(转发)',
'2002':'外网端口(转发)',
'2003':'开始打洞(转发)',
'2004':'开始打洞(转发)',
'2005':'打洞失败(转发)',
'2006':'打洞失败(转发)',
'2007':'打洞成功(转发)',
'2008':'打洞成功(转发)',
'2009':'隧道配置(转发)',
'2010':'隧道配置(转发)',
'2012':'隧道同步(转发)',
'2101':'中继通知(转发)',
'2102':'中继通知(转发)',
'2103':'中继请求',
'2105':'中继连通测试',
'2201':'运行网卡(转发)',
'2203':'停止网卡(转发)',
'2205':'更新网卡(转发)',
'2206':'同步网卡(转发)',
'2207':'同步网卡(转发)',
'2301':'添加内网穿透',
'2302':'移除内网穿透',
'2303':'通知内网穿透(转发)',
'2304':'通知内网穿透UDP(转发)',
'2305':'获取穿透列表(转发)',
'2401':'测试端口转发(转发)',
'2403':'获取端口转发(转发)',
'2503':'获取权限(转发)',
'2504':'获取权限(转发)',
'2506':'更新权限(转发)',
'2508':'同步密钥(转发)',
'2510':'同步服务器(转发)',
'2601':'更新信息(转发)',
'2602':'更新信息(转发)',
'2603':'确认更新(转发)',
'2604':'确认更新(转发)',
'2605':'重启(转发)',
'2607':'服务器更新信息',
'2608':'确认服务器更新',
'2609':'服务器重启',
'2610':'订阅更新信息(转发)',
'2611':'订阅更新信息(转发)',
'2701':'服务器流量',
'2702':'服务器信标流量',
'2703':'服务器中继流量',
'2704':'服务器内网穿透流量',
}
const _getMessengerFlows = ()=>{
getMessengerFlows().then(res => {
const old = state.old || res;
const list = [];
for(let j in res){
const item = res[j];
const itemOld = old[j];
const text = `[${j}]${id2text[`${j}`] || `未知`}`;
list.push({
id:text,
sendtBytes:item.SendtBytes,
sendtBytesText:parseSpeed(item.SendtBytes),
sendtSpeed:item.SendtBytes-itemOld.SendtBytes,
sendtSpeedText:parseSpeed(item.SendtBytes-itemOld.SendtBytes),
receiveBytes:item.ReceiveBytes,
receiveBytesText:parseSpeed(item.ReceiveBytes),
receiveSpeed:item.ReceiveBytes-itemOld.ReceiveBytes,
receiveSpeedText:parseSpeed(item.ReceiveBytes-itemOld.ReceiveBytes),
});
}
state.list = list.filter(c=>!!c.id);
state.old = res;
state.timer = setTimeout(_getMessengerFlows,1000);
}).catch((e)=>{
state.timer = setTimeout(_getMessengerFlows,1000);
});
}
const parseSpeed = (num) => {
let index = 0;
while (num >= 1024) {
num /= 1024;
index++;
}
return `${num.toFixed(2)}${['B', 'KB', 'MB', 'GB', 'TB'][index]}`;
}
onMounted(()=>{
_getMessengerFlows();
});
onUnmounted(()=>{
clearTimeout(state.timer);
});
return {
config:props.config,state
}
}
}
</script>
<style lang="stylus" scoped>
a{
font-weight:bold;position:absolute;right:1rem;bottom:90%;
border:1px solid #ddd;
background-color:#fff;
z-index :9
p{
line-height:normal;
white-space: nowrap;
}
}
</style>

View File

@@ -0,0 +1,158 @@
<template>
<el-dialog title="内网穿透流量" class="options-center" top="1vh" destroy-on-close v-model="state.show" width="680">
<div>
<div class="head">
<el-input v-model="state.page.Key" placeholder="域名/端口搜索"></el-input>
</div>
<el-table :data="state.list" stripe border size="small" width="100%" height="60vh" @sort-change="handleSort">
<el-table-column prop="id" label="域名/端口" width="200"></el-table-column>
<el-table-column prop="sendtBytes" label="已上传" sortable="custom">
<template #default="scope">
<span>{{ scope.row.sendtBytesText }}</span>
</template>
</el-table-column>
<el-table-column prop="sendtSpeed" label="上传速度" sortable="custom">
<template #default="scope">
<span>{{ scope.row.sendtSpeedText }}/s</span>
</template>
</el-table-column>
<el-table-column prop="receiveBytes" label="已下载" sortable="custom">
<template #default="scope">
<span>{{ scope.row.receiveBytesText }}</span>
</template>
</el-table-column>
<el-table-column prop="receiveSpeed" label="下载速度" sortable="custom">
<template #default="scope">
<span>{{ scope.row.receiveSpeedText }}/s</span>
</template>
</el-table-column>
</el-table>
<div class="page t-c">
<div class="page-wrap">
<el-pagination small background layout="total,prev,pager, next" :total="state.page.Count"
:page-size="state.page.PageSize" :current-page="state.page.Page" @current-change="handlePageChange"/>
</div>
</div>
</div>
</el-dialog>
</template>
<script>
import { getSForwardFlows } from '@/apis/flow';
import { onMounted, onUnmounted, reactive, watch } from 'vue';
export default {
props: ['modelValue','config'],
emits: ['update:modelValue'],
setup (props,{emit}) {
const state = reactive({
show:true,
timer:0,
list:[],
page:{
Key:'',
Page:1,
PageSize:15,
Count:0,
Order:1,
OrderType:0
}
});
watch(() => state.show, (val) => {
if (!val) {
setTimeout(() => {
emit('update:modelValue', val);
}, 300);
}
});
const _getSForwardFlows = ()=>{
getSForwardFlows({
Key: state.page.Key,
Page:state.page.Page,
PageSize:state.page.PageSize,
Order:state.page.Order,
OrderType:state.page.OrderType,
}).then(res => {
state.page.Page = res.Page;
state.page.PageSize = res.PageSize;
state.page.Count = res.Count;
const list = [];
const keys = Object.keys(res.Data);
console.log(res.Data);
for(let i = 0; i < keys.length; i++ ){
const item = res.Data[keys[i]];
list.push({
id:keys[i],
sendtBytes:item.SendtBytes,
sendtBytesText:parseSpeed(item.SendtBytes),
sendtSpeed:item.DiffSendtBytes,
sendtSpeedText:parseSpeed(item.DiffSendtBytes),
receiveBytes:item.ReceiveBytes,
receiveBytesText:parseSpeed(item.ReceiveBytes),
receiveSpeed:item.DiffReceiveBytes,
receiveSpeedText:parseSpeed(item.DiffReceiveBytes),
});
}
state.list = list.filter(c=>!!c.id);
state.old = res;
state.timer = setTimeout(_getSForwardFlows,1000);
}).catch((e)=>{
state.timer = setTimeout(_getSForwardFlows,1000);
});
}
const parseSpeed = (num) => {
let index = 0;
while (num >= 1024) {
num /= 1024;
index++;
}
return `${num.toFixed(2)}${['B', 'KB', 'MB', 'GB', 'TB'][index]}`;
}
const handlePageChange = (page)=>{
if (page) {
state.page.Page = page;
}
}
const handleSort = (a)=>{
const orderType = {'ascending':1,'descending':0}[a.order];
const order = {'sendtBytes':1,'sendtSpeed':2,'receiveBytes':3,'receiveSpeed':4}[a.prop];
state.page.Order = order;
state.page.OrderType = orderType;
}
onMounted(()=>{
_getSForwardFlows();
});
onUnmounted(()=>{
clearTimeout(state.timer);
});
return {
config:props.config,state,handlePageChange,handleSort
}
}
}
</script>
<style lang="stylus" scoped>
.head{
padding-bottom:1rem;
text-align:center;
.el-input{width:20rem}
}
.page{padding-top:1rem}
.page-wrap{
display:inline-block;
}
</style>

View File

@@ -52,7 +52,7 @@ export default {
const {devices, machineId, _getSignList, _getSignList1,handleDeviceEdit,
handlePageChange, handlePageSizeChange, handleDel,clearDevicesTimeout} = provideDevices();
const {tuntap,_getTuntapInfo,handleTuntapRefresh,clearTuntapTimeout,handleTuntapEdit,sortTuntapIP} = provideTuntap();
const {_getUpdater,clearUpdaterTimeout} = provideUpdater();
const {_getUpdater,_subscribeUpdater,clearUpdaterTimeout} = provideUpdater();
onMounted(() => {
handlePageChange();
@@ -63,6 +63,7 @@ export default {
_getTuntapInfo();
_getUpdater();
_subscribeUpdater();
});
onUnmounted(() => {
clearDevicesTimeout();

View File

@@ -19,7 +19,8 @@
<Title>linker</Title>
<Authors>snltty</Authors>
<Company>snltty</Company>
<Description>1. 增加流量统计(暂时显示服务流量)</Description>
<Description>1. 总览,和详细流量统计,一眼知道服务流量花在哪里
2. 优化信标。减少流量,没有操作时尽量不产生流量</Description>
<Copyright>snltty</Copyright>
<PackageProjectUrl>https://github.com/snltty/linker</PackageProjectUrl>
<RepositoryUrl>https://github.com/snltty/linker</RepositoryUrl>

View File

@@ -6,6 +6,8 @@ using linker.plugins.client;
using linker.plugins.capi;
using linker.plugins.messenger;
using linker.plugins.flow.messenger;
using linker.libs.extends;
using linker.plugins.sforward.proxy;
namespace linker.plugins.flow
{
@@ -37,7 +39,34 @@ namespace linker.plugins.flow
}
return new FlowInfo();
}
public async Task<Dictionary<ushort, FlowItemInfo>> GetMessengerFlows(ApiControllerParamsInfo param)
{
MessageResponeInfo resp = await messengerSender.SendReply(new MessageRequestWrap
{
Connection = clientSignInState.Connection,
MessengerId = (ushort)FlowMessengerIds.Messenger,
});
if (resp.Code == MessageResponeCodes.OK && resp.Data.Length > 0)
{
return MemoryPackSerializer.Deserialize<Dictionary<ushort, FlowItemInfo>>(resp.Data.Span);
}
return new Dictionary<ushort, FlowItemInfo>();
}
public async Task<SForwardFlowResponseInfo> GetSForwardFlows(ApiControllerParamsInfo param)
{
MessageResponeInfo resp = await messengerSender.SendReply(new MessageRequestWrap
{
Connection = clientSignInState.Connection,
MessengerId = (ushort)FlowMessengerIds.SForward,
Payload = MemoryPackSerializer.Serialize(param.Content.DeJson<SForwardFlowRequestInfo>())
});
if (resp.Code == MessageResponeCodes.OK && resp.Data.Length > 0)
{
return MemoryPackSerializer.Deserialize<SForwardFlowResponseInfo>(resp.Data.Span);
}
return new SForwardFlowResponseInfo();
}
}
}

View File

@@ -7,6 +7,7 @@ namespace linker.plugins.flow
{
private List<IFlow> flows = new List<IFlow>();
private readonly ServiceProvider serviceProvider;
public FlowTransfer(ServiceProvider serviceProvider)
{

View File

@@ -1,4 +1,5 @@
using MemoryPack;
using linker.libs;
using MemoryPack;
using System.Text.Json.Serialization;
namespace linker.plugins.flow
@@ -11,7 +12,7 @@ namespace linker.plugins.flow
}
[MemoryPackable]
public sealed partial class FlowItemInfo
public partial class FlowItemInfo
{
public ulong ReceiveBytes { get; set; }
public ulong SendtBytes { get; set; }
@@ -23,11 +24,10 @@ namespace linker.plugins.flow
[MemoryPackable]
public sealed partial class FlowInfo
{
public Dictionary<string, FlowItemInfo> Resolvers { get; set; }
public Dictionary<ushort, FlowItemInfo> Messangers { get; set; }
public Dictionary<string, FlowItemInfo> Items { get; set; }
public DateTime Start { get; set; }
public DateTime Now { get; set; }
}
}

View File

@@ -1,4 +1,5 @@
using linker.plugins.messenger;
using linker.plugins.sforward.proxy;
using MemoryPack;
namespace linker.plugins.flow.messenger
@@ -8,14 +9,16 @@ namespace linker.plugins.flow.messenger
private readonly MessengerResolver messengerResolver;
private readonly FlowTransfer flowTransfer;
private readonly MessengerFlow messengerFlow;
private readonly SForwardFlow sForwardFlow;
private DateTime start = DateTime.Now;
public FlowMessenger(MessengerResolver messengerResolver, FlowTransfer flowTransfer, MessengerFlow messengerFlow)
public FlowMessenger(MessengerResolver messengerResolver, FlowTransfer flowTransfer, MessengerFlow messengerFlow, SForwardFlow sForwardFlow)
{
this.messengerResolver = messengerResolver;
this.flowTransfer = flowTransfer;
this.messengerFlow = messengerFlow;
this.sForwardFlow = sForwardFlow;
}
[MessengerId((ushort)FlowMessengerIds.List)]
@@ -23,14 +26,26 @@ namespace linker.plugins.flow.messenger
{
FlowInfo serverFlowInfo = new FlowInfo
{
Messangers = messengerFlow.GetFlows(),
Resolvers = flowTransfer.GetFlows(),
Items = flowTransfer.GetFlows(),
Start = start,
Now = DateTime.Now,
};
connection.Write(MemoryPackSerializer.Serialize(serverFlowInfo));
}
[MessengerId((ushort)FlowMessengerIds.Messenger)]
public void Messenger(IConnection connection)
{
connection.Write(MemoryPackSerializer.Serialize(messengerFlow.GetFlows()));
}
[MessengerId((ushort)FlowMessengerIds.SForward)]
public void SForward(IConnection connection)
{
sForwardFlow.Update();
SForwardFlowRequestInfo info = MemoryPackSerializer.Deserialize<SForwardFlowRequestInfo>(connection.ReceiveRequestWrap.Payload.Span);
connection.Write(MemoryPackSerializer.Serialize(sForwardFlow.GetFlows(info)));
}
}
}

View File

@@ -5,6 +5,9 @@
Min = 2700,
List = 2701,
Messenger = 2702,
Relay = 2703,
SForward = 2704,
Max = 2799
}

View File

@@ -149,7 +149,7 @@ namespace linker.plugins.forward.proxy
{
token.Connection = tunnelToken.Connection;
await token.TargetSocket.SendToAsync(tunnelToken.Proxy.Data, target).ConfigureAwait(false);
token.Update();
token.LastTicks.Update();
return;
}
@@ -221,7 +221,7 @@ namespace linker.plugins.forward.proxy
{
SocketReceiveFromResult result = await socket.ReceiveFromAsync(udpToken.Buffer, SocketFlags.None, target).ConfigureAwait(false);
udpToken.Proxy.Data = udpToken.Buffer.AsMemory(0, result.ReceivedBytes);
udpToken.Update();
udpToken.LastTicks.Update();
await SendToConnection(udpToken).ConfigureAwait(false);
}
}
@@ -384,8 +384,8 @@ namespace linker.plugins.forward.proxy
public byte[] Buffer { get; set; }
public long LastTime { get; set; } = Environment.TickCount64;
public bool Timeout => Environment.TickCount64 - LastTime > 60*60*1000;
public LastTicksManager LastTicks { get; set; } = new LastTicksManager();
public bool Timeout => LastTicks.Timeout(60 * 60 * 1000);
public void Clear()
{
TargetSocket?.SafeClose();
@@ -393,10 +393,6 @@ namespace linker.plugins.forward.proxy
GC.Collect();
GC.SuppressFinalize(this);
}
public void Update()
{
LastTime = Environment.TickCount64;
}
}
public sealed class ConnectIdUdpComparer : IEqualityComparer<ConnectIdUdp>
{

View File

@@ -246,7 +246,7 @@ namespace linker.plugins.messenger
}
public override bool Connected => SourceSocket != null && ticks > 0 && Environment.TickCount64 - ticks < 15000;
public override bool Connected => SourceSocket != null && lastTicks.Timeout(15000) == false;
private IConnectionReceiveCallback callback;
@@ -256,8 +256,8 @@ namespace linker.plugins.messenger
private bool framing;
private ReceiveDataBuffer bufferCache = new ReceiveDataBuffer();
private long ticks = Environment.TickCount64;
private long pingStart = Environment.TickCount64;
private LastTicksManager lastTicks = new LastTicksManager();
private LastTicksManager pingTicks =new LastTicksManager();
private static byte[] pingBytes = Encoding.UTF8.GetBytes($"{Helper.GlobalString}.tcp.ping");
private static byte[] pongBytes = Encoding.UTF8.GetBytes($"{Helper.GlobalString}.tcp.pong");
private bool pong = true;
@@ -297,7 +297,7 @@ namespace linker.plugins.messenger
break;
}
ReceiveBytes += length;
ticks = Environment.TickCount64;
lastTicks.Update();
await ReadPacket(buffer.AsMemory(0, length)).ConfigureAwait(false);
}
}
@@ -362,7 +362,7 @@ namespace linker.plugins.messenger
}
else if (packet.Span.SequenceEqual(pongBytes))
{
Delay = (int)(Environment.TickCount64 - pingStart);
Delay = (int)pingTicks.Diff();
pong = true;
}
}
@@ -384,9 +384,9 @@ namespace linker.plugins.messenger
{
while (cancellationTokenSource.IsCancellationRequested == false)
{
if (Environment.TickCount64 - ticks > 3000)
if (lastTicks.Greater(3000))
{
pingStart = Environment.TickCount64;
pingTicks .Update();
await SendPingPong(pingBytes).ConfigureAwait(false);
}
@@ -435,7 +435,7 @@ namespace linker.plugins.messenger
{
if (pong == false) return;
pong = false;
pingStart = Environment.TickCount64;
pingTicks.Update();
await SendPingPong(pingBytes);
}
public override async Task<bool> SendAsync(ReadOnlyMemory<byte> data)
@@ -448,7 +448,7 @@ namespace linker.plugins.messenger
else
await SourceSocket.SendAsync(data, cancellationTokenSourceWrite.Token).ConfigureAwait(false);
SendBytes += data.Length;
ticks = Environment.TickCount64;
lastTicks.Update();
}
catch (OperationCanceledException)
{
@@ -496,7 +496,7 @@ namespace linker.plugins.messenger
SourceSocket?.SafeClose();
TargetSocket?.SafeClose();
ticks = 0;
lastTicks.Clear();
}
}

View File

@@ -1,5 +1,4 @@
using linker.plugins.flow;
using static System.Reflection.Metadata.BlobBuilder;
namespace linker.plugins.messenger
{
@@ -10,6 +9,7 @@ namespace linker.plugins.messenger
public string FlowName => "Messenger";
private Dictionary<ushort, FlowItemInfo> flows { get; } = new Dictionary<ushort, FlowItemInfo>();
public MessengerFlow()
{
}

View File

@@ -28,6 +28,10 @@ namespace linker.plugins.sforward
serviceCollection.AddSingleton<SForwardClientApiController>();
serviceCollection.AddSingleton<SForwardTransfer>();
serviceCollection.AddSingleton<SForwardClientMessenger>();
serviceCollection.AddSingleton<SForwardFlow>();
}
public void AddServer(ServiceCollection serviceCollection, FileConfig config, Assembly[] assemblies)
@@ -37,6 +41,7 @@ namespace linker.plugins.sforward
serviceCollection.AddSingleton<ISForwardServerCahing, SForwardServerCahing>();
serviceCollection.AddSingleton<ISForwardValidator, Validator>();
serviceCollection.AddSingleton<SForwardFlow>();
}
bool added = false;

View File

@@ -0,0 +1,153 @@
using linker.libs;
using linker.libs.extends;
using linker.plugins.flow;
using MemoryPack;
using System.Text.Json.Serialization;
namespace linker.plugins.sforward.proxy
{
public sealed class SForwardFlow : IFlow
{
public ulong ReceiveBytes { get; private set; }
public ulong SendtBytes { get; private set; }
public string FlowName => "SForward";
private readonly LastTicksManager lastTicksManager = new LastTicksManager();
private Dictionary<string, SForwardFlowItemInfo> flows { get; } = new Dictionary<string, SForwardFlowItemInfo>();
public SForwardFlow()
{
TimerHelper.SetInterval(() =>
{
if (lastTicksManager.Less(5000))
{
foreach (var item in flows.Values)
{
item.DiffReceiveBytes = item.SendtBytes - item.OldSendtBytes;
item.DiffSendtBytes = item.ReceiveBytes - item.OldReceiveBytes;
item.OldSendtBytes = item.SendtBytes;
item.OldReceiveBytes = item.ReceiveBytes;
}
}
return true;
}, 1000);
AddSendt("snltty", 0);
}
public void Update()
{
lastTicksManager.Update();
}
public void AddReceive(string key, ulong bytes)
{
if (flows.TryGetValue(key, out SForwardFlowItemInfo messengerFlowItemInfo) == false)
{
messengerFlowItemInfo = new SForwardFlowItemInfo { };
flows.TryAdd(key, messengerFlowItemInfo);
}
ReceiveBytes += bytes;
messengerFlowItemInfo.ReceiveBytes += bytes;
}
public void AddSendt(string key, ulong bytes)
{
if (flows.TryGetValue(key, out SForwardFlowItemInfo messengerFlowItemInfo) == false)
{
messengerFlowItemInfo = new SForwardFlowItemInfo { };
flows.TryAdd(key, messengerFlowItemInfo);
}
SendtBytes += bytes;
messengerFlowItemInfo.SendtBytes += bytes;
}
public SForwardFlowResponseInfo GetFlows(SForwardFlowRequestInfo info)
{
var items = flows.Where(c => string.IsNullOrWhiteSpace(info.Key) || c.Key.Contains(info.Key));
switch (info.Order)
{
case SForwardFlowOrder.Sendt:
if (info.OrderType == SForwardFlowOrderType.Desc)
items = items.OrderByDescending(x => x.Value.SendtBytes);
else
items = items.OrderBy(x => x.Value.SendtBytes);
break;
case SForwardFlowOrder.DiffSendt:
if (info.OrderType == SForwardFlowOrderType.Desc)
items = items.OrderByDescending(x => x.Value.DiffSendtBytes);
else
items = items.OrderBy(x => x.Value.DiffSendtBytes);
break;
case SForwardFlowOrder.Receive:
if (info.OrderType == SForwardFlowOrderType.Desc)
items = items.OrderByDescending(x => x.Value.ReceiveBytes);
else
items = items.OrderBy(x => x.Value.ReceiveBytes);
break;
case SForwardFlowOrder.DiffRecive:
if (info.OrderType == SForwardFlowOrderType.Desc)
items = items.OrderByDescending(x => x.Value.DiffReceiveBytes);
else
items = items.OrderBy(x => x.Value.DiffReceiveBytes);
break;
default:
break;
}
SForwardFlowResponseInfo resp = new SForwardFlowResponseInfo
{
Page = info.Page,
PageSize = info.PageSize,
Count = flows.Count,
Data = items.Skip((info.Page - 1) * info.PageSize).Take(info.PageSize).ToDictionary()
};
return resp;
}
}
[MemoryPackable]
public sealed partial class SForwardFlowItemInfo : FlowItemInfo
{
public ulong DiffReceiveBytes { get; set; }
public ulong DiffSendtBytes { get; set; }
[MemoryPackIgnore, JsonIgnore]
public ulong OldReceiveBytes { get; set; }
[MemoryPackIgnore, JsonIgnore]
public ulong OldSendtBytes { get; set; }
}
[MemoryPackable]
public sealed partial class SForwardFlowRequestInfo
{
public string Key { get; set; } = string.Empty;
public int Page { get; set; } = 1;
public int PageSize { get; set; } = 15;
public SForwardFlowOrder Order { get; set; }
public SForwardFlowOrderType OrderType { get; set; }
}
public enum SForwardFlowOrder : byte
{
Sendt = 1,
DiffSendt = 2,
Receive = 3,
DiffRecive = 4
}
public enum SForwardFlowOrderType : byte
{
Desc = 0,
Asc = 1,
}
[MemoryPackable]
public sealed partial class SForwardFlowResponseInfo
{
public int Page { get; set; }
public int PageSize { get; set; }
public int Count { get; set; }
public Dictionary<string,SForwardFlowItemInfo> Data { get; set; }
}
}

View File

@@ -4,19 +4,18 @@ using System.Text;
namespace linker.plugins.sforward.proxy
{
public partial class SForwardProxy : IFlow
public partial class SForwardProxy
{
public ulong ReceiveBytes { get; private set; }
public ulong SendtBytes { get; private set; }
public string FlowName => "SForward";
private readonly NumberSpace ns = new NumberSpace();
private byte[] flagBytes = Encoding.UTF8.GetBytes($"snltty.sforward");
public SForwardProxy()
private readonly SForwardFlow sForwardFlow;
public SForwardProxy(SForwardFlow sForwardFlow)
{
this.sForwardFlow = sForwardFlow;
UdpTask();
}
public string Start(int port, bool isweb, byte bufferSize)

View File

@@ -154,7 +154,7 @@ namespace linker.plugins.sforward.proxy
await token.TargetSocket.SendAsync(buffer1.AsMemory(0, length)).ConfigureAwait(false);
//两端交换数据
await Task.WhenAll(CopyToAsync(buffer1, token.SourceSocket, token.TargetSocket), CopyToAsync(buffer2, token.TargetSocket, token.SourceSocket)).ConfigureAwait(false);
await Task.WhenAll(CopyToAsync(token.Host, token.ListenPort, buffer1, token.SourceSocket, token.TargetSocket), CopyToAsync(token.Host, token.ListenPort, buffer2, token.TargetSocket, token.SourceSocket)).ConfigureAwait(false);
CloseClientSocket(token);
}
@@ -248,7 +248,7 @@ namespace linker.plugins.sforward.proxy
await sourceSocket.SendAsync(buffer1.AsMemory(0, flagBytes.Length + 8)).ConfigureAwait(false);
//交换数据即可
await Task.WhenAll(CopyToAsync(buffer1, sourceSocket, targetSocket), CopyToAsync(buffer2, targetSocket, sourceSocket)).ConfigureAwait(false);
await Task.WhenAll(CopyToAsync(string.Empty, service.Port, buffer1, sourceSocket, targetSocket), CopyToAsync(string.Empty, service.Port, buffer2, targetSocket, sourceSocket)).ConfigureAwait(false);
}
catch (Exception)
@@ -265,20 +265,32 @@ namespace linker.plugins.sforward.proxy
/// <summary>
/// 读取数据,然后发送给对方,用户两端交换数据
/// </summary>
/// <param name="domain"></param>
/// <param name="port"></param>
/// <param name="buffer"></param>
/// <param name="source"></param>
/// <param name="target"></param>
/// <param name="receive"></param>
/// <returns></returns>
private async Task CopyToAsync(Memory<byte> buffer, Socket source, Socket target)
private async Task CopyToAsync(string domain, int port, Memory<byte> buffer, Socket source, Socket target)
{
bool isDomain = string.IsNullOrWhiteSpace(domain) == false;
string portStr = port.ToString();
try
{
int bytesRead;
while ((bytesRead = await source.ReceiveAsync(buffer, SocketFlags.None).ConfigureAwait(false)) != 0)
{
ReceiveBytes += (ulong)bytesRead;
SendtBytes += (ulong)bytesRead;
if (isDomain)
{
sForwardFlow.AddReceive(domain, (ulong)bytesRead);
sForwardFlow.AddSendt(domain, (ulong)bytesRead);
}
else
{
sForwardFlow.AddReceive(portStr, (ulong)bytesRead);
sForwardFlow.AddSendt(portStr, (ulong)bytesRead);
}
await target.SendAsync(buffer.Slice(0, bytesRead), SocketFlags.None).ConfigureAwait(false);
}
}

View File

@@ -40,6 +40,9 @@ namespace linker.plugins.sforward.proxy
{
byte[] buffer = new byte[(1 << bufferSize) * 1024];
IPEndPoint tempRemoteEP = new IPEndPoint(IPAddress.Any, IPEndPoint.MinPort);
string portStr = token.ListenPort.ToString();
while (true)
{
SocketReceiveFromResult result = await token.SourceSocket.ReceiveFromAsync(buffer, tempRemoteEP).ConfigureAwait(false);
@@ -49,14 +52,15 @@ namespace linker.plugins.sforward.proxy
}
Memory<byte> memory = buffer.AsMemory(0, result.ReceivedBytes);
ReceiveBytes += (ulong)memory.Length;
sForwardFlow.AddReceive(portStr, (ulong)memory.Length);
IPEndPoint source = result.RemoteEndPoint as IPEndPoint;
//已经连接
if (udpConnections.TryGetValue(source, out UdpTargetCache cache) && cache != null)
{
SendtBytes += (ulong)memory.Length;
cache.Update();
sForwardFlow.AddSendt(portStr, (ulong)memory.Length);
cache.LastTicks.Update();
await token.SourceSocket.SendToAsync(memory, cache.IPEndPoint).ConfigureAwait(false);
}
else
@@ -171,6 +175,8 @@ namespace linker.plugins.sforward.proxy
IPEndPoint tempEp = new IPEndPoint(IPAddress.Any, IPEndPoint.MinPort);
UdpConnectedCache cache = new UdpConnectedCache { SourceSocket = socketUdp, TargetSocket = serviceUdp };
string portStr = service.Port.ToString();
while (true)
{
try
@@ -183,11 +189,12 @@ namespace linker.plugins.sforward.proxy
socketUdp?.Dispose();
break;
}
cache.Update();
cache.LastTicks.Update();
Memory<byte> memory = buffer.AsMemory(0, result.ReceivedBytes);
ReceiveBytes += (ulong)memory.Length;
SendtBytes += (ulong)memory.Length;
sForwardFlow.AddReceive(portStr, (ulong)memory.Length);
sForwardFlow.AddSendt(portStr, (ulong)memory.Length);
if (serviceUdp == null)
{
@@ -215,11 +222,11 @@ namespace linker.plugins.sforward.proxy
break;
}
Memory<byte> memory = buffer.AsMemory(0, result.ReceivedBytes);
ReceiveBytes += (ulong)memory.Length;
SendtBytes += (ulong)memory.Length;
sForwardFlow.AddReceive(portStr, (ulong)memory.Length);
sForwardFlow.AddSendt(portStr, (ulong)memory.Length);
await socketUdp.SendToAsync(memory, server).ConfigureAwait(false);
cache.Update();
cache.LastTicks.Update();
}
catch (Exception ex)
{
@@ -284,24 +291,16 @@ namespace linker.plugins.sforward.proxy
public sealed class UdpTargetCache
{
public IPEndPoint IPEndPoint { get; set; }
public long LastTime { get; set; } = Environment.TickCount64;
public void Update()
{
LastTime = Environment.TickCount64;
}
public bool Timeout => Environment.TickCount64 - LastTime > 5 * 60 * 1000;
public LastTicksManager LastTicks { get; set; } = new LastTicksManager();
public bool Timeout => LastTicks.Greater(5 * 60 * 1000);
}
public sealed class UdpConnectedCache
{
public Socket SourceSocket { get; set; }
public Socket TargetSocket { get; set; }
public long LastTime { get; set; } = Environment.TickCount64;
public void Update()
{
LastTime = Environment.TickCount64;
}
public bool Timeout => Environment.TickCount64 - LastTime > 5 * 60 * 1000;
public LastTicksManager LastTicks { get; set; } = new LastTicksManager();
public bool Timeout => LastTicks.Greater(5 * 60 * 1000);
public void Clear()
{

View File

@@ -180,23 +180,6 @@ namespace linker.plugins.tunnel
return true;
}
public async Task<ConcurrentDictionary<string, TunnelRecordInfo>> Records(ApiControllerParamsInfo param)
{
MessageResponeInfo resp = await messengerSender.SendReply(new MessageRequestWrap
{
Connection = clientSignInState.Connection,
MessengerId = (ushort)TunnelMessengerIds.Records
}).ConfigureAwait(false);
if(resp.Code == MessageResponeCodes.OK)
{
return MemoryPackSerializer.Deserialize<ConcurrentDictionary<string, TunnelRecordInfo>>(resp.Data.Span);
}
return new ConcurrentDictionary<string, TunnelRecordInfo>();
}
}
}

View File

@@ -156,22 +156,6 @@ namespace linker.plugins.tunnel.messenger
tunnelTransportInfo.Local.MachineName = cacheFrom.MachineName;
tunnelTransportInfo.Remote.MachineName = cacheTo.MachineName;
/*
TunnelRecordInfo tunnelRecordInfo = new TunnelRecordInfo
{
MachineName = cacheFrom.MachineName,
Times = 1
};
records.AddOrUpdate(cacheFrom.MachineName, tunnelRecordInfo, (a, b) =>
{
TunnelRecordItemInfo item = new TunnelRecordItemInfo { MachineName = cacheTo.MachineName, Times = 1 };
b.To.AddOrUpdate(cacheTo.MachineId, item, (a, b) => { b.Times++; return b; });
b.Times++;
return b;
});
*/
await messengerSender.SendOnly(new MessageRequestWrap
{
Connection = cacheTo.Connection,
@@ -269,13 +253,6 @@ namespace linker.plugins.tunnel.messenger
}
}
[MessengerId((ushort)TunnelMessengerIds.Records)]
public void Records(IConnection connection)
{
connection.Write(MemoryPackSerializer.Serialize(records));
}
}
[MemoryPackable]

View File

@@ -22,8 +22,6 @@
RouteLevel = 2011,
RouteLevelForward = 2012,
Records = 2013,
None = 2099
}
}

View File

@@ -202,6 +202,11 @@ namespace linker.plugins.tuntap
}
public void SubscribePing(ApiControllerParamsInfo param)
{
tuntapTransfer.SubscribePing();
}
public sealed class RouteItemListInfo
{
public object[] List { get; set; }

View File

@@ -190,6 +190,9 @@ namespace linker.plugins.tuntap
TimerHelper.Async(() =>
{
DeleteForward();
bool needReboot = info.IP.Equals(runningConfig.Data.Tuntap.IP) == false || info.PrefixLength != runningConfig.Data.Tuntap.PrefixLength;
runningConfig.Data.Tuntap.IP = info.IP;
runningConfig.Data.Tuntap.LanIPs = info.LanIPs;
runningConfig.Data.Tuntap.Masks = info.Masks;
@@ -197,7 +200,7 @@ namespace linker.plugins.tuntap
runningConfig.Data.Tuntap.Switch = info.Switch;
runningConfig.Data.Tuntap.Forwards = info.Forwards;
runningConfig.Data.Update();
if (Status == TuntapStatus.Running)
if (Status == TuntapStatus.Running && needReboot)
{
Shutdown();
Setup();
@@ -254,7 +257,7 @@ namespace linker.plugins.tuntap
foreach (var item in list)
{
tuntapInfos.AddOrUpdate(item.MachineId, item, (a, b) => item);
item.LastTicks = Environment.TickCount64;
item.LastTicks.Update();
}
var removes = tuntapInfos.Keys.Except(list.Select(c => c.MachineId)).ToList();
foreach (var item in removes)
@@ -262,7 +265,7 @@ namespace linker.plugins.tuntap
if (tuntapInfos.TryGetValue(item, out TuntapInfo tuntapInfo))
{
tuntapInfo.Status = TuntapStatus.Normal;
tuntapInfo.LastTicks = 0;
tuntapInfo.LastTicks.Clear();
}
}
Version.Add();
@@ -428,7 +431,7 @@ namespace linker.plugins.tuntap
return infos
//自己的ip不要
.Where(c => c.IP.Equals(runningConfig.Data.Tuntap.IP) == false && c.LastTicks > 0)
.Where(c => c.IP.Equals(runningConfig.Data.Tuntap.IP) == false && c.LastTicks.Greater(0))
.OrderBy(c => c.LastTicks)
.Select(c =>
{
@@ -511,9 +514,32 @@ namespace linker.plugins.tuntap
}
private readonly LastTicksManager lastTicksManager = new LastTicksManager();
public void SubscribePing()
{
lastTicksManager.Update();
}
private void PingTask()
{
TimerHelper.SetInterval(async () =>
{
if (lastTicksManager.Less(5000))
{
await Ping();
}
return true;
}, 3000);
TimerHelper.SetInterval(async () =>
{
if (lastTicksManager.Greater(15000))
{
await Ping();
}
return true;
}, 30000);
}
private async Task Ping()
{
if (Status == TuntapStatus.Running && (runningConfig.Data.Tuntap.Switch & TuntapSwitch.ShowDelay) == TuntapSwitch.ShowDelay)
{
@@ -533,8 +559,6 @@ namespace linker.plugins.tuntap
Version.Add();
}
}
return true;
}, 3000);
}
}
}

View File

@@ -1,4 +1,5 @@
using linker.plugins.tuntap.config;
using linker.libs;
using linker.plugins.tuntap.config;
using MemoryPack;
using System.Net;
@@ -149,7 +150,7 @@ namespace linker.plugins.tuntap.config
public TuntapSwitch Switch { get; set; }
[MemoryPackIgnore]
public long LastTicks { get; set; } = Environment.TickCount64;
public LastTicksManager LastTicks { get; set; } = new LastTicksManager();
/// <summary>
/// 延迟ms
@@ -246,6 +247,29 @@ namespace linker.plugins.tuntap.config
}
}
/// <summary>
/// 使用广播
/// </summary>
[MemoryPackIgnore]
public bool Multicast
{
get
{
return (Switch & TuntapSwitch.Multicast) == TuntapSwitch.Multicast;
}
set
{
if (value)
{
Switch |= TuntapSwitch.Multicast;
}
else
{
Switch &= ~TuntapSwitch.Multicast;
}
}
}
}
[Flags]
@@ -255,6 +279,7 @@ namespace linker.plugins.tuntap.config
ShowDelay = 2,
Upgrade = 4,
AutoConnect = 8,
Multicast = 16,
}

View File

@@ -41,15 +41,6 @@ namespace linker.plugins.updater
updaterTransfer.SetSecretKey(param.Content);
}
public UpdateInfo GetCurrent(ApiControllerParamsInfo param)
{
var updaters = updaterTransfer.Get();
if (updaters.TryGetValue(config.Data.Client.Id, out UpdateInfo info))
{
return info;
}
return new UpdateInfo { };
}
public async Task<UpdateInfo> GetServer(ApiControllerParamsInfo param)
{
MessageResponeInfo resp = await messengerSender.SendReply(new MessageRequestWrap
@@ -85,7 +76,15 @@ namespace linker.plugins.updater
});
}
public UpdateInfo GetCurrent(ApiControllerParamsInfo param)
{
var updaters = updaterTransfer.Get();
if (updaters.TryGetValue(config.Data.Client.Id, out UpdateInfo info))
{
return info;
}
return new UpdateInfo { };
}
public UpdaterListInfo Get(ApiControllerParamsInfo param)
{
ulong hashCode = ulong.Parse(param.Content);
@@ -144,6 +143,16 @@ namespace linker.plugins.updater
}
return true;
}
public async Task Subscribe(ApiControllerParamsInfo param)
{
await messengerSender.SendOnly(new MessageRequestWrap
{
Connection = clientSignInState.Connection,
MessengerId = (ushort)UpdaterMessengerIds.SubscribeForward
});
}
}
public sealed class UpdaterListInfo

View File

@@ -13,6 +13,7 @@ namespace linker.plugins.updater
{
private UpdateInfo updateInfo = new UpdateInfo();
private ConcurrentDictionary<string, UpdateInfo> updateInfos = new ConcurrentDictionary<string, UpdateInfo>();
private ConcurrentDictionary<string, LastTicksManager> subscribes = new ConcurrentDictionary<string, LastTicksManager>();
private readonly FileConfig fileConfig;
private readonly MessengerSender messengerSender;
@@ -76,19 +77,43 @@ namespace linker.plugins.updater
}
}
public void Subscribe(string machineId)
{
if (subscribes.TryGetValue(machineId, out LastTicksManager lastTicksManager) == false)
{
lastTicksManager = new LastTicksManager();
subscribes.TryAdd(machineId, lastTicksManager);
}
//距离上次订阅超过一分钟,需要立即更新一次
bool needUpdate = lastTicksManager.Greater(60 * 1000);
lastTicksManager.Update();
if (needUpdate)
{
updateInfo.Update();
}
}
private void UpdateTask()
{
TimerHelper.SetInterval(async () =>
{
if (updateInfo.Updated)
{
string[] machines = subscribes.Where(c => c.Value.Less(15000)).Select(c => c.Key).ToArray();
if (machines.Length > 0)
{
updateInfo.MachineId = fileConfig.Data.Client.Id;
await messengerSender.SendOnly(new MessageRequestWrap
{
Connection = clientSignInState.Connection,
MessengerId = (ushort)UpdaterMessengerIds.UpdateForward,
Payload = MemoryPackSerializer.Serialize(updateInfo),
Payload = MemoryPackSerializer.Serialize(new UpdateClientInfo { ToMachines = machines, Info = updateInfo }),
});
}
Update(updateInfo);
}
return true;
@@ -105,5 +130,11 @@ namespace linker.plugins.updater
}
}
[MemoryPackable]
public sealed partial class UpdateClientInfo
{
public string[] ToMachines { get; set; }
public UpdateInfo Info { get; set; }
}
}

View File

@@ -44,6 +44,14 @@ namespace linker.plugins.updater.messenger
{
Environment.Exit(1);
}
[MessengerId((ushort)UpdaterMessengerIds.Subscribe)]
public void Subscribe(IConnection connection)
{
string machineId = MemoryPackSerializer.Deserialize<string>(connection.ReceiveRequestWrap.Payload.Span);
updaterTransfer.Subscribe(machineId);
}
}
@@ -144,16 +152,17 @@ namespace linker.plugins.updater.messenger
[MessengerId((ushort)UpdaterMessengerIds.UpdateForward)]
public void UpdateForward(IConnection connection)
{
UpdateInfo info = MemoryPackSerializer.Deserialize<UpdateInfo>(connection.ReceiveRequestWrap.Payload.Span);
UpdateClientInfo info = MemoryPackSerializer.Deserialize<UpdateClientInfo>(connection.ReceiveRequestWrap.Payload.Span);
if (signCaching.TryGet(connection.Id, out SignCacheInfo cache))
{
foreach (var item in signCaching.Get(cache.GroupId).Where(c => c.Connected && c.MachineId != connection.Id))
byte[] payload = MemoryPackSerializer.Serialize(info.Info);
foreach (var item in signCaching.Get(cache.GroupId).Where(c => info.ToMachines.Contains(c.MachineId)).Where(c => c.Connected && c.MachineId != connection.Id))
{
_ = messengerSender.SendOnly(new MessageRequestWrap
{
Connection = item.Connection,
MessengerId = (ushort)UpdaterMessengerIds.Update,
Payload = connection.ReceiveRequestWrap.Payload
Payload = payload
});
}
@@ -177,5 +186,30 @@ namespace linker.plugins.updater.messenger
});
}
}
/// <summary>
/// 订阅更新信息
/// </summary>
/// <param name="connection"></param>
[MessengerId((ushort)UpdaterMessengerIds.SubscribeForward)]
public void SubscribeForward(IConnection connection)
{
if (signCaching.TryGet(connection.Id, out SignCacheInfo cache))
{
byte[] mechineId = MemoryPackSerializer.Serialize(connection.Id);
foreach (var item in signCaching.Get(cache.GroupId).Where(c => c.Connected && c.MachineId != connection.Id))
{
_ = messengerSender.SendOnly(new MessageRequestWrap
{
Connection = item.Connection,
MessengerId = (ushort)UpdaterMessengerIds.Subscribe,
Payload = mechineId
});
}
}
}
}
}

View File

@@ -17,6 +17,9 @@
ConfirmServer = 2608,
ExitServer = 2609,
Subscribe = 2610,
SubscribeForward = 2611,
Max = 2299
}
}

View File

@@ -1,3 +1,4 @@
v1.4.4
2024-09-26 23:52:00
1. 增加流量统计(暂时显示服务流量)
2024-09-27 17:23:47
1. 总览,和详细流量统计,一眼知道服务流量花在哪里
2. 优化信标。减少流量,没有操作时尽量不产生流量