mirror of
https://github.com/snltty/linker.git
synced 2025-10-08 10:30:08 +08:00
sync
This commit is contained in:
2
.github/workflows/dotnet.yml
vendored
2
.github/workflows/dotnet.yml
vendored
@@ -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
|
||||
|
45
linker.libs/LastTicksManager.cs
Normal file
45
linker.libs/LastTicksManager.cs
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -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
|
||||
|
@@ -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()}");
|
||||
|
@@ -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()}");
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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; }
|
||||
}
|
||||
|
||||
|
@@ -2,4 +2,10 @@ 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);
|
||||
}
|
@@ -22,4 +22,8 @@ export const updateTuntap = (name) => {
|
||||
}
|
||||
export const refreshTuntap = () => {
|
||||
return sendWebsocketMsg('tuntapclient/refresh');
|
||||
}
|
||||
}
|
||||
export const subscribePing = () => {
|
||||
return sendWebsocketMsg('tuntapclient/SubscribePing');
|
||||
}
|
||||
|
||||
|
@@ -30,4 +30,7 @@ export const confirmServer = (version) => {
|
||||
}
|
||||
export const exitServer = () => {
|
||||
return sendWebsocketMsg('updaterclient/exitserver');
|
||||
}
|
||||
export const subscribeUpdater = () => {
|
||||
return sendWebsocketMsg('updaterclient/Subscribe');
|
||||
}
|
@@ -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();
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
});
|
||||
|
@@ -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 = () => {
|
||||
|
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
189
linker.web/src/views/full/status/ServerFlowMessenger.vue
Normal file
189
linker.web/src/views/full/status/ServerFlowMessenger.vue
Normal 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>
|
158
linker.web/src/views/full/status/ServerFlowSForward.vue
Normal file
158
linker.web/src/views/full/status/ServerFlowSForward.vue
Normal 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>
|
@@ -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();
|
||||
|
@@ -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>
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -7,6 +7,7 @@ namespace linker.plugins.flow
|
||||
{
|
||||
private List<IFlow> flows = new List<IFlow>();
|
||||
|
||||
|
||||
private readonly ServiceProvider serviceProvider;
|
||||
public FlowTransfer(ServiceProvider serviceProvider)
|
||||
{
|
||||
|
@@ -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; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -5,6 +5,9 @@
|
||||
Min = 2700,
|
||||
|
||||
List = 2701,
|
||||
Messenger = 2702,
|
||||
Relay = 2703,
|
||||
SForward = 2704,
|
||||
|
||||
Max = 2799
|
||||
}
|
||||
|
@@ -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>
|
||||
{
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -310,7 +310,7 @@ namespace linker.plugins.messenger
|
||||
{
|
||||
LoggerHelper.Instance.Error(ex);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,15 +1,15 @@
|
||||
using linker.plugins.flow;
|
||||
using static System.Reflection.Metadata.BlobBuilder;
|
||||
|
||||
namespace linker.plugins.messenger
|
||||
{
|
||||
public sealed class MessengerFlow : IFlow
|
||||
public sealed class MessengerFlow : IFlow
|
||||
{
|
||||
public ulong ReceiveBytes { get; private set; }
|
||||
public ulong SendtBytes { get; private set; }
|
||||
public string FlowName => "Messenger";
|
||||
|
||||
private Dictionary<ushort, FlowItemInfo> flows { get; } = new Dictionary<ushort, FlowItemInfo>();
|
||||
|
||||
public MessengerFlow()
|
||||
{
|
||||
}
|
||||
|
@@ -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;
|
||||
|
153
linker/plugins/sforward/proxy/SForwardFlow.cs
Normal file
153
linker/plugins/sforward/proxy/SForwardFlow.cs
Normal 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; }
|
||||
}
|
||||
}
|
@@ -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)
|
||||
|
@@ -18,7 +18,7 @@ namespace linker.plugins.sforward.proxy
|
||||
|
||||
#region 服务端
|
||||
|
||||
|
||||
|
||||
private void StartTcp(int port, bool isweb, byte bufferSize)
|
||||
{
|
||||
IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Any, port);
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -296,7 +308,7 @@ namespace linker.plugins.sforward.proxy
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public sealed class AsyncUserToken
|
||||
|
@@ -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()
|
||||
{
|
||||
|
@@ -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>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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]
|
||||
|
@@ -22,8 +22,6 @@
|
||||
RouteLevel = 2011,
|
||||
RouteLevelForward = 2012,
|
||||
|
||||
Records = 2013,
|
||||
|
||||
None = 2099
|
||||
}
|
||||
}
|
||||
|
@@ -202,6 +202,11 @@ namespace linker.plugins.tuntap
|
||||
}
|
||||
|
||||
|
||||
public void SubscribePing(ApiControllerParamsInfo param)
|
||||
{
|
||||
tuntapTransfer.SubscribePing();
|
||||
}
|
||||
|
||||
public sealed class RouteItemListInfo
|
||||
{
|
||||
public object[] List { get; set; }
|
||||
|
@@ -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,30 +514,51 @@ namespace linker.plugins.tuntap
|
||||
}
|
||||
|
||||
|
||||
|
||||
private readonly LastTicksManager lastTicksManager = new LastTicksManager();
|
||||
public void SubscribePing()
|
||||
{
|
||||
lastTicksManager.Update();
|
||||
}
|
||||
private void PingTask()
|
||||
{
|
||||
TimerHelper.SetInterval(async () =>
|
||||
{
|
||||
if (Status == TuntapStatus.Running && (runningConfig.Data.Tuntap.Switch & TuntapSwitch.ShowDelay) == TuntapSwitch.ShowDelay)
|
||||
if (lastTicksManager.Less(5000))
|
||||
{
|
||||
var items = tuntapInfos.Values.Where(c => c.IP != null && c.IP.Equals(IPAddress.Any) == false && (c.Status & TuntapStatus.Running) == TuntapStatus.Running);
|
||||
if ((runningConfig.Data.Tuntap.Switch & TuntapSwitch.AutoConnect) != TuntapSwitch.AutoConnect)
|
||||
{
|
||||
var connections = tuntapProxy.GetConnections();
|
||||
items = items.Where(c => (connections.TryGetValue(c.MachineId, out ITunnelConnection connection) && connection.Connected) || c.MachineId == config.Data.Client.Id);
|
||||
}
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
using Ping ping = new Ping();
|
||||
PingReply pingReply = await ping.SendPingAsync(item.IP, 500);
|
||||
item.Delay = pingReply.Status == IPStatus.Success ? (int)pingReply.RoundtripTime : -1;
|
||||
|
||||
Version.Add();
|
||||
}
|
||||
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)
|
||||
{
|
||||
var items = tuntapInfos.Values.Where(c => c.IP != null && c.IP.Equals(IPAddress.Any) == false && (c.Status & TuntapStatus.Running) == TuntapStatus.Running);
|
||||
if ((runningConfig.Data.Tuntap.Switch & TuntapSwitch.AutoConnect) != TuntapSwitch.AutoConnect)
|
||||
{
|
||||
var connections = tuntapProxy.GetConnections();
|
||||
items = items.Where(c => (connections.TryGetValue(c.MachineId, out ITunnelConnection connection) && connection.Connected) || c.MachineId == config.Data.Client.Id);
|
||||
}
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
using Ping ping = new Ping();
|
||||
PingReply pingReply = await ping.SendPingAsync(item.IP, 500);
|
||||
item.Delay = pingReply.Status == IPStatus.Success ? (int)pingReply.RoundtripTime : -1;
|
||||
|
||||
Version.Add();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
{
|
||||
updateInfo.MachineId = fileConfig.Data.Client.Id;
|
||||
await messengerSender.SendOnly(new MessageRequestWrap
|
||||
string[] machines = subscribes.Where(c => c.Value.Less(15000)).Select(c => c.Key).ToArray();
|
||||
if (machines.Length > 0)
|
||||
{
|
||||
Connection = clientSignInState.Connection,
|
||||
MessengerId = (ushort)UpdaterMessengerIds.UpdateForward,
|
||||
Payload = MemoryPackSerializer.Serialize(updateInfo),
|
||||
});
|
||||
updateInfo.MachineId = fileConfig.Data.Client.Id;
|
||||
await messengerSender.SendOnly(new MessageRequestWrap
|
||||
{
|
||||
Connection = clientSignInState.Connection,
|
||||
MessengerId = (ushort)UpdaterMessengerIds.UpdateForward,
|
||||
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; }
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -17,6 +17,9 @@
|
||||
ConfirmServer = 2608,
|
||||
ExitServer = 2609,
|
||||
|
||||
Subscribe = 2610,
|
||||
SubscribeForward = 2611,
|
||||
|
||||
Max = 2299
|
||||
}
|
||||
}
|
||||
|
@@ -1,3 +1,4 @@
|
||||
v1.4.4
|
||||
2024-09-26 23:52:00
|
||||
1. 增加流量统计(暂时显示服务端流量)
|
||||
2024-09-27 17:23:47
|
||||
1. 总览,和详细流量统计,一眼知道服务器流量花在哪里
|
||||
2. 优化信标。减少流量,没有操作时尽量不产生流量
|
Reference in New Issue
Block a user