From 79bed8cfb8a6a14dcbcea9b7857f14bb36c72c83 Mon Sep 17 00:00:00 2001 From: snltty Date: Fri, 28 Nov 2025 11:00:47 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E4=B8=AD=E7=BB=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- install-package/fpk/docker/ICON.PNG | Bin 5357 -> 20282 bytes .../fpk/docker/app/ui/images/icon_64.png | Bin 5357 -> 20282 bytes shells/version.txt | 2 +- .../SignInArgsAction.cs | 2 +- src/linker.messenger.relay/Entry.cs | 2 + .../client/RelayApiController.cs | 2 +- .../client/RelayClientTestTransfer.cs | 2 +- .../messenger/RelayMessenger.cs | 47 ++++++++++------- .../messenger/RelayMessengerIds.cs | 2 +- ...odeStore.cs => IRelayServerConfigStore.cs} | 24 ++++----- .../server/IRelayServerMasterStore.cs | 10 ++-- .../server/RelayServerMasterTransfer.cs | 20 +++---- .../server/RelayServerNodeReportTransfer.cs | 30 +++++++++++ .../server/RelayServerNodeTransfer.cs | 38 ++++++-------- .../server/validator/IRelayServerValidator.cs | 2 +- .../validator/RelayServerValidatorTransfer.cs | 2 +- .../transport/TransportRelay.cs | 49 +++++++++++------- .../RelaySerializer.cs | 6 +-- src/linker.messenger.store.file/Entry.cs | 1 + .../relay/Config.cs | 15 +----- .../relay/RelayServerConfigStore.cs | 34 ++++++++++++ .../relay/RelayServerNodeStore.cs | 32 ++++++------ 22 files changed, 194 insertions(+), 128 deletions(-) rename src/linker.messenger.relay/server/{IRelayServerNodeStore.cs => IRelayServerConfigStore.cs} (86%) create mode 100644 src/linker.messenger.relay/server/RelayServerNodeReportTransfer.cs create mode 100644 src/linker.messenger.store.file/relay/RelayServerConfigStore.cs diff --git a/install-package/fpk/docker/ICON.PNG b/install-package/fpk/docker/ICON.PNG index a6e6108c243d94c92b6fdd04f62943885920a616..7710a523df395c3b0af53896ffea77227f6fd03c 100644 GIT binary patch delta 19790 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 be*p#nZgG(qX5};U00000NkvXXu0mjf9;r>+ delta 4748 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 be*p#nZgG(qX5};U00000NkvXXu0mjf9;r>+ delta 4748 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 Validate(string userid, SignCacheInfo fromMachine, List nodes) + public async Task> Validate(string userid, SignCacheInfo fromMachine, List nodes) { if (string.IsNullOrWhiteSpace(actionServerStore.RelayNodeUrl) == false) { diff --git a/src/linker.messenger.relay/Entry.cs b/src/linker.messenger.relay/Entry.cs index a7c4e5a8..df12099b 100644 --- a/src/linker.messenger.relay/Entry.cs +++ b/src/linker.messenger.relay/Entry.cs @@ -55,6 +55,8 @@ namespace linker.messenger.relay { serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); diff --git a/src/linker.messenger.relay/client/RelayApiController.cs b/src/linker.messenger.relay/client/RelayApiController.cs index 23b98fdf..7cb5e35e 100644 --- a/src/linker.messenger.relay/client/RelayApiController.cs +++ b/src/linker.messenger.relay/client/RelayApiController.cs @@ -33,7 +33,7 @@ namespace linker.messenger.relay.client this.tunnelTransfer = tunnelTransfer; } - public List Subscribe(ApiControllerParamsInfo param) + public List Subscribe(ApiControllerParamsInfo param) { relayTestTransfer.Subscribe(); return relayTestTransfer.Nodes; diff --git a/src/linker.messenger.relay/client/RelayClientTestTransfer.cs b/src/linker.messenger.relay/client/RelayClientTestTransfer.cs index d778811e..3abe6765 100644 --- a/src/linker.messenger.relay/client/RelayClientTestTransfer.cs +++ b/src/linker.messenger.relay/client/RelayClientTestTransfer.cs @@ -16,7 +16,7 @@ namespace linker.messenger.relay.client private readonly TransportRelay transportRelay; private readonly SignInClientState signInClientState; - public List Nodes { get; private set; } = new List(); + public List Nodes { get; private set; } = new List(); public RelayClientTestTransfer(TransportRelay transportRelay, SignInClientState signInClientState) { diff --git a/src/linker.messenger.relay/messenger/RelayMessenger.cs b/src/linker.messenger.relay/messenger/RelayMessenger.cs index d54d7f6e..5681eab9 100644 --- a/src/linker.messenger.relay/messenger/RelayMessenger.cs +++ b/src/linker.messenger.relay/messenger/RelayMessenger.cs @@ -23,33 +23,34 @@ namespace linker.messenger.relay.messenger public class RelayServerMessenger : IMessenger { private readonly SignInServerCaching signCaching; - private readonly RelayServerMasterTransfer relayServerTransfer; private readonly RelayServerValidatorTransfer relayValidatorTransfer; private readonly ISerializer serializer; private readonly RelayServerReportResolver relayServerReportResolver; - private readonly IRelayServerNodeStore relayServerNodeStore; + private readonly RelayServerNodeTransfer relayServerNodeTransfer; + private readonly RelayServerMasterTransfer relayServerMasterTransfer; - public RelayServerMessenger(SignInServerCaching signCaching, ISerializer serializer, - RelayServerMasterTransfer relayServerTransfer, RelayServerValidatorTransfer relayValidatorTransfer, - RelayServerReportResolver relayServerReportResolver, IRelayServerNodeStore relayServerNodeStore) + + public RelayServerMessenger(SignInServerCaching signCaching, ISerializer serializer, RelayServerValidatorTransfer relayValidatorTransfer, + RelayServerReportResolver relayServerReportResolver, RelayServerNodeTransfer relayServerNodeTransfer, RelayServerMasterTransfer relayServerMasterTransfer) { this.signCaching = signCaching; - this.relayServerTransfer = relayServerTransfer; this.relayValidatorTransfer = relayValidatorTransfer; this.serializer = serializer; this.relayServerReportResolver = relayServerReportResolver; - this.relayServerNodeStore = relayServerNodeStore; + this.relayServerNodeTransfer = relayServerNodeTransfer; + this.relayServerMasterTransfer = relayServerMasterTransfer; } + [MessengerId((ushort)RelayMessengerIds.Nodes)] public async Task Nodes(IConnection connection) { if (signCaching.TryGet(connection.Id, out SignCacheInfo cache) == false) { - connection.Write(serializer.Serialize(new List { })); + connection.Write(serializer.Serialize(new List { })); return; } - List nodes = await GetNodes(cache); + List nodes = await GetNodes(cache); connection.Write(serializer.Serialize(nodes)); } @@ -66,25 +67,28 @@ namespace linker.messenger.relay.messenger var nodes = await GetNodes(from).ConfigureAwait(false); string error = await relayValidatorTransfer.Validate(from, to, info.transactionId); - if (string.IsNullOrWhiteSpace(error) == false || relayServerTransfer.AddRelay(from, to, info.flowId) == false) + if (string.IsNullOrWhiteSpace(error) == false || relayServerMasterTransfer.AddRelay(from, to, info.flowId) == false) { connection.Write(serializer.Serialize(new RelayAskResultInfo())); return; } - connection.Write(serializer.Serialize(new RelayAskResultInfo { Nodes = nodes, MasterId = relayServerNodeStore.Node.NodeId })); - } - private async Task> GetNodes(SignCacheInfo from) - { - return await relayServerTransfer.GetNodes(from.Super, from.UserId, from.MachineId); + connection.Write(serializer.Serialize(new RelayAskResultInfo { Nodes = nodes, MasterId = relayServerNodeTransfer.Node.NodeId })); } - [MessengerId((ushort)RelayMessengerIds.NodeGetCache)] - public async Task NodeGetCache(IConnection connection) + private async Task> GetNodes(SignCacheInfo from) + { + return await relayServerMasterTransfer.GetNodes(from.Super, from.UserId, from.MachineId); + } + + + + [MessengerId((ushort)RelayMessengerIds.GetCache)] + public async Task GetCache(IConnection connection) { relayServerReportResolver.Add(connection.ReceiveRequestWrap.Payload.Length, 0); ValueTuple key = serializer.Deserialize>(connection.ReceiveRequestWrap.Payload.Span); - RelayCacheInfo cache = await relayServerTransfer.TryGetRelayCache(key.Item1, key.Item2); + RelayCacheInfo cache = await relayServerMasterTransfer.TryGetRelayCache(key.Item1, key.Item2); if (cache != null) { byte[] sendt = serializer.Serialize(cache); @@ -99,12 +103,17 @@ namespace linker.messenger.relay.messenger [MessengerId((ushort)RelayMessengerIds.NodeReport)] - public void NodeReport188(IConnection connection) + public async Task NodeReport(IConnection connection) { try { relayServerReportResolver.Add(connection.ReceiveRequestWrap.Payload.Length, 0); RelayServerNodeReportInfoOld info = serializer.Deserialize(connection.ReceiveRequestWrap.Payload.Span); + await relayServerMasterTransfer.AddNode(new RelayServerNodeStoreInfo + { + NodeId = info.Id, + Name = info.Name, + }).ConfigureAwait(false); } catch (Exception) { diff --git a/src/linker.messenger.relay/messenger/RelayMessengerIds.cs b/src/linker.messenger.relay/messenger/RelayMessengerIds.cs index ea5071ca..3f69c9bd 100644 --- a/src/linker.messenger.relay/messenger/RelayMessengerIds.cs +++ b/src/linker.messenger.relay/messenger/RelayMessengerIds.cs @@ -8,7 +8,7 @@ Nodes = 2105, - NodeGetCache = 2112, + GetCache = 2112, NodeReport = 2128, Hosts = 2133, diff --git a/src/linker.messenger.relay/server/IRelayServerNodeStore.cs b/src/linker.messenger.relay/server/IRelayServerConfigStore.cs similarity index 86% rename from src/linker.messenger.relay/server/IRelayServerNodeStore.cs rename to src/linker.messenger.relay/server/IRelayServerConfigStore.cs index 03103165..bcc8a564 100644 --- a/src/linker.messenger.relay/server/IRelayServerNodeStore.cs +++ b/src/linker.messenger.relay/server/IRelayServerConfigStore.cs @@ -1,38 +1,33 @@ -using linker.libs; -using System.Net; +using System.Net; using linker.libs.extends; using System.Text.Json.Serialization; using linker.tunnel.connection; namespace linker.messenger.relay.server { - public interface IRelayServerNodeStore + public interface IRelayServerConfigStore { - /// - /// 服务端端口 - /// - public int ServicePort { get; } /// /// 节点信息 /// - public RelayServerNodeInfo Node { get; } + public RelayServerConfigInfo Config { get; } /// /// 设置 /// - /// - public void SetInfo(RelayServerNodeInfo node); + /// + public void SetInfo(RelayServerConfigInfo config); /// /// 设置月份 /// /// - public void SetMaxGbTotalMonth(int month); + public void DataMonth(int month); /// /// 设置剩余流量 /// /// - public void SetMaxGbTotalLastBytes(long value); + public void SetDataRemain(long value); /// /// 提交保存 /// @@ -58,6 +53,11 @@ namespace linker.messenger.relay.server public string Logo { get; set; } = "https://linker.snltty.com/img/logo.png"; } + public sealed class RelayServerConfigInfo : RelayServerNodeInfo + { + public string ShareKey { get; set; } = string.Empty; + } + public class RelayServerNodeReportInfo : RelayServerNodeInfo { public string Version { get; set; } = string.Empty; diff --git a/src/linker.messenger.relay/server/IRelayServerMasterStore.cs b/src/linker.messenger.relay/server/IRelayServerMasterStore.cs index 6fd28eab..8ac33cc4 100644 --- a/src/linker.messenger.relay/server/IRelayServerMasterStore.cs +++ b/src/linker.messenger.relay/server/IRelayServerMasterStore.cs @@ -1,11 +1,13 @@ namespace linker.messenger.relay.server { - public interface IRelayServerMasterStore + public interface IRelayServerNodeStore { - public Task> GetAll(); + public Task> GetAll(); + public Task GetByNodeId(string nodeId); + public Task Add(RelayServerNodeStoreInfo info); } - public sealed class RelayNodeStoreInfo : RelayServerNodeReportInfo + public sealed class RelayServerNodeStoreInfo : RelayServerNodeReportInfo { public int Id { get; set; } @@ -14,7 +16,5 @@ public int Delay { get; set; } public long LastTicks { get; set; } - - } } diff --git a/src/linker.messenger.relay/server/RelayServerMasterTransfer.cs b/src/linker.messenger.relay/server/RelayServerMasterTransfer.cs index 4cf12865..c7ab82ed 100644 --- a/src/linker.messenger.relay/server/RelayServerMasterTransfer.cs +++ b/src/linker.messenger.relay/server/RelayServerMasterTransfer.cs @@ -10,9 +10,9 @@ namespace linker.messenger.relay.server { private readonly IRelayServerCaching relayCaching; private readonly IRelayServerWhiteListStore relayServerWhiteListStore; - private readonly IRelayServerMasterStore relayServerMasterStore; + private readonly IRelayServerNodeStore relayServerMasterStore; - public RelayServerMasterTransfer(IRelayServerCaching relayCaching, IRelayServerWhiteListStore relayServerWhiteListStore, IRelayServerMasterStore relayServerMasterStore) + public RelayServerMasterTransfer(IRelayServerCaching relayCaching, IRelayServerWhiteListStore relayServerWhiteListStore, IRelayServerNodeStore relayServerMasterStore) { this.relayCaching = relayCaching; this.relayServerWhiteListStore = relayServerWhiteListStore; @@ -35,7 +35,6 @@ namespace linker.messenger.relay.server }; return relayCaching.TryAdd($"{cache.FromId}->{cache.ToId}->{flowid}", cache, 15000); } - public async Task TryGetRelayCache(string key, string nodeid) { if (relayCaching.TryGetValue(key, out RelayCacheInfo cache)) @@ -54,12 +53,9 @@ namespace linker.messenger.relay.server } return null; } - /// - /// 获取节点列表 - /// - /// 是否已认证 - /// - public async Task> GetNodes(bool validated, string userid, string machineId) + + + public async Task> GetNodes(bool validated, string userid, string machineId) { var nodes = (await relayServerWhiteListStore.GetNodes(userid, machineId)).Where(c => c.Bandwidth >= 0).SelectMany(c => c.Nodes); @@ -81,7 +77,7 @@ namespace linker.messenger.relay.server .ThenByDescending(x => x.DataRemain == 0 ? long.MaxValue : x.DataRemain) .ToList(); } - public async Task> GetPublicNodes() + public async Task> GetPublicNodes() { var result = (await relayServerMasterStore.GetAll()) .Where(c => Environment.TickCount64 - c.LastTicks < 15000) @@ -98,6 +94,10 @@ namespace linker.messenger.relay.server .ToList(); } + public async Task AddNode(RelayServerNodeStoreInfo info) + { + return await relayServerMasterStore.Add(info).ConfigureAwait(false); + } } public sealed partial class RelayCacheInfo diff --git a/src/linker.messenger.relay/server/RelayServerNodeReportTransfer.cs b/src/linker.messenger.relay/server/RelayServerNodeReportTransfer.cs new file mode 100644 index 00000000..b46a0a10 --- /dev/null +++ b/src/linker.messenger.relay/server/RelayServerNodeReportTransfer.cs @@ -0,0 +1,30 @@ +namespace linker.messenger.relay.server +{ + public sealed class RelayServerNodeReportTransfer + { + private uint connectionNum = 0; + private long bytes = 0; + + public uint ConnectionNum => connectionNum; + + /// + /// 增加连接数 + /// + public void IncrementConnectionNum() + { + Interlocked.Increment(ref connectionNum); + } + /// + /// 减少连接数 + /// + public void DecrementConnectionNum() + { + Interlocked.Decrement(ref connectionNum); + } + + public void AddBytes(long length) + { + Interlocked.Add(ref bytes, length); + } + } +} diff --git a/src/linker.messenger.relay/server/RelayServerNodeTransfer.cs b/src/linker.messenger.relay/server/RelayServerNodeTransfer.cs index 043c3875..b0b97a4d 100644 --- a/src/linker.messenger.relay/server/RelayServerNodeTransfer.cs +++ b/src/linker.messenger.relay/server/RelayServerNodeTransfer.cs @@ -4,7 +4,6 @@ using linker.messenger.relay.messenger; using linker.tunnel.connection; using linker.tunnel.transport; using System.Collections.Concurrent; -using System.Net; namespace linker.messenger.relay.server { @@ -13,31 +12,28 @@ namespace linker.messenger.relay.server /// public class RelayServerNodeTransfer { - /// - /// 配置了就用配置的,每配置就用一个默认的 - /// - public RelayServerNodeInfo Node => relayServerNodeStore.Node; + public RelayServerConfigInfo Node => relayServerNodeStore.Config; - private uint connectionNum = 0; - private long bytes = 0; private readonly RelaySpeedLimit limitTotal = new RelaySpeedLimit(); private readonly ConcurrentDictionary trafficDict = new(); private readonly ISerializer serializer; - private readonly IRelayServerNodeStore relayServerNodeStore; + private readonly IRelayServerConfigStore relayServerNodeStore; private readonly IMessengerSender messengerSender; private readonly RelayServerConnectionTransfer relayServerConnectionTransfer; + private readonly RelayServerNodeReportTransfer relayServerNodeReportTransfer; - public RelayServerNodeTransfer(ISerializer serializer, IRelayServerNodeStore relayServerNodeStore, IMessengerSender messengerSender, RelayServerConnectionTransfer relayServerConnectionTransfer) + public RelayServerNodeTransfer(ISerializer serializer, IRelayServerConfigStore relayServerNodeStore, IMessengerSender messengerSender, + RelayServerConnectionTransfer relayServerConnectionTransfer, RelayServerNodeReportTransfer relayServerNodeReportTransfer) { this.serializer = serializer; this.relayServerNodeStore = relayServerNodeStore; this.messengerSender = messengerSender; this.relayServerConnectionTransfer = relayServerConnectionTransfer; + this.relayServerNodeReportTransfer = relayServerNodeReportTransfer; limitTotal.SetLimit((uint)Math.Ceiling((Node.Bandwidth * 1024 * 1024) / 8.0)); - TrafficTask(); } @@ -55,7 +51,7 @@ namespace linker.messenger.relay.server MessageResponeInfo resp = await messengerSender.SendReply(new MessageRequestWrap { Connection = connection, - MessengerId = (ushort)RelayMessengerIds.NodeGetCache, + MessengerId = (ushort)RelayMessengerIds.GetCache, Payload = serializer.Serialize(new ValueTuple(key, Node.NodeId)), Timeout = 1000 }).ConfigureAwait(false); @@ -75,7 +71,7 @@ namespace linker.messenger.relay.server public bool Validate(TunnelProtocolType tunnelProtocolType) { - return true; + return (Node.Protocol & tunnelProtocolType) == tunnelProtocolType; } /// /// 无效请求 @@ -91,9 +87,9 @@ namespace linker.messenger.relay.server /// private bool ValidateConnection(RelayCacheInfo relayCache) { - bool res = Node.Connections == 0 || Node.Connections * 2 > connectionNum; + bool res = Node.Connections == 0 || Node.Connections * 2 > relayServerNodeReportTransfer.ConnectionNum; if (res == false && LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG) - LoggerHelper.Instance.Debug($"relay ValidateConnection false,{connectionNum}/{Node.Connections * 2}"); + LoggerHelper.Instance.Debug($"relay validate connection false,{relayServerNodeReportTransfer.ConnectionNum}/{Node.Connections * 2}"); return res; } @@ -117,14 +113,14 @@ namespace linker.messenger.relay.server /// public void IncrementConnectionNum() { - Interlocked.Increment(ref connectionNum); + relayServerNodeReportTransfer.IncrementConnectionNum(); } /// /// 减少连接数 /// public void DecrementConnectionNum() { - Interlocked.Decrement(ref connectionNum); + relayServerNodeReportTransfer.DecrementConnectionNum(); } /// @@ -179,7 +175,7 @@ namespace linker.messenger.relay.server /// public bool AddBytes(RelayTrafficCacheInfo cache, long length) { - Interlocked.Add(ref bytes, length); + relayServerNodeReportTransfer.AddBytes(length); if (Node.DataEachMonth == 0) return true; @@ -213,13 +209,13 @@ namespace linker.messenger.relay.server long length = Interlocked.Exchange(ref cache.Sendt, 0); if (Node.DataRemain >= length) - relayServerNodeStore.SetMaxGbTotalLastBytes(Node.DataRemain - length); - else relayServerNodeStore.SetMaxGbTotalLastBytes(0); + relayServerNodeStore.SetDataRemain(Node.DataRemain - length); + else relayServerNodeStore.SetDataRemain(0); } if (Node.DataMonth != DateTime.Now.Month) { - relayServerNodeStore.SetMaxGbTotalMonth(DateTime.Now.Month); - relayServerNodeStore.SetMaxGbTotalLastBytes((long)(Node.DataEachMonth * 1024 * 1024 * 1024)); + relayServerNodeStore.DataMonth(DateTime.Now.Month); + relayServerNodeStore.SetDataRemain((long)(Node.DataEachMonth * 1024 * 1024 * 1024)); } relayServerNodeStore.Confirm(); } diff --git a/src/linker.messenger.relay/server/validator/IRelayServerValidator.cs b/src/linker.messenger.relay/server/validator/IRelayServerValidator.cs index 54801822..f1958b5b 100644 --- a/src/linker.messenger.relay/server/validator/IRelayServerValidator.cs +++ b/src/linker.messenger.relay/server/validator/IRelayServerValidator.cs @@ -23,6 +23,6 @@ namespace linker.messenger.relay.server.validator /// /// /// - public Task> Validate(string userid, SignCacheInfo from, List nodes); + public Task> Validate(string userid, SignCacheInfo from, List nodes); } } diff --git a/src/linker.messenger.relay/server/validator/RelayServerValidatorTransfer.cs b/src/linker.messenger.relay/server/validator/RelayServerValidatorTransfer.cs index 474bb374..cee412c6 100644 --- a/src/linker.messenger.relay/server/validator/RelayServerValidatorTransfer.cs +++ b/src/linker.messenger.relay/server/validator/RelayServerValidatorTransfer.cs @@ -59,7 +59,7 @@ namespace linker.messenger.relay.server.validator } return string.Empty; } - public async Task> Validate(string userid, SignCacheInfo fromMachine, List nodes) + public async Task> Validate(string userid, SignCacheInfo fromMachine, List nodes) { foreach (var item in validators) { diff --git a/src/linker.messenger.relay/transport/TransportRelay.cs b/src/linker.messenger.relay/transport/TransportRelay.cs index a3a30243..212dadcb 100644 --- a/src/linker.messenger.relay/transport/TransportRelay.cs +++ b/src/linker.messenger.relay/transport/TransportRelay.cs @@ -6,7 +6,6 @@ using linker.messenger.relay.server; using linker.messenger.signin; using linker.tunnel.connection; using linker.tunnel.wanport; -using System; using System.Buffers; using System.Net; using System.Net.Security; @@ -55,6 +54,12 @@ namespace linker.tunnel.transport this.tunnelMessengerAdapter = tunnelMessengerAdapter; } + private X509Certificate certificate; + public void SetSSL(X509Certificate certificate) + { + this.certificate = certificate; + } + public virtual async Task ConnectAsync(TunnelTransportInfo tunnelTransportInfo) { byte[] buffer = ArrayPool.Shared.Rent(1024); @@ -62,25 +67,24 @@ namespace linker.tunnel.transport { //问一下能不能中继 RelayAskResultInfo ask = await RelayAsk(tunnelTransportInfo).ConfigureAwait(false); - List nodes = ask.Nodes; + List nodes = ask.Nodes; if (ask.Nodes.Count == 0) { - return null; + throw new Exception("relay ask fail,no relay nodes"); } //连接中继节点服务器 Socket socket = await ConnectNodeServer(tunnelTransportInfo, ask).ConfigureAwait(false); - if (socket == null) + if(socket == null) { - return null; + throw new Exception("connect relay node server fail"); } - tunnelTransportInfo.TransactionTag = ask.Info.ToJson(); //让对方确认中继 if (await tunnelMessengerAdapter.SendConnectBegin(tunnelTransportInfo).ConfigureAwait(false) == false) { - return null; + throw new Exception("relay begin fail"); } //成功建立连接, @@ -98,6 +102,8 @@ namespace linker.tunnel.transport #pragma warning restore SYSLIB0039 // 类型或成员已过时 } + await tunnelMessengerAdapter.SendConnectSuccess(tunnelTransportInfo).ConfigureAwait(false); + return new TunnelConnectionTcp { Direction = TunnelDirection.Forward, @@ -127,6 +133,7 @@ namespace linker.tunnel.transport { ArrayPool.Shared.Return(buffer); } + await tunnelMessengerAdapter.SendConnectFail(tunnelTransportInfo).ConfigureAwait(false); return null; } private async Task RelayAsk(TunnelTransportInfo tunnelTransportInfo) @@ -149,7 +156,7 @@ namespace linker.tunnel.transport }).ConfigureAwait(false); if (resp.Code != MessageResponeCodes.OK) { - return new RelayAskResultInfo { Info = relayInfo, Nodes = new List() }; + return new RelayAskResultInfo { Info = relayInfo, Nodes = new List() }; } RelayAskResultInfo ask = serializer.Deserialize(resp.Data.Span); ask.Info = relayInfo; @@ -184,7 +191,7 @@ namespace linker.tunnel.transport await socket.ConnectAsync(ep).WaitAsync(TimeSpan.FromMilliseconds(5000)).ConfigureAwait(false); if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG) { - LoggerHelper.Instance.Debug($"relay connected {ep}"); + LoggerHelper.Instance.Debug($"relay connected {ep}"); } //建立关联 @@ -231,7 +238,6 @@ namespace linker.tunnel.transport return true; } - private async Task SendMessage(Socket socket, RelayMessageInfo relayMessage) { try @@ -256,11 +262,17 @@ namespace linker.tunnel.transport return false; } - public virtual async Task OnBegin(TunnelTransportInfo tunnelTransportInfo) { try { + if (tunnelTransportInfo.SSL && certificate == null) + { + LoggerHelper.Instance.Error($"{Name}->ssl Certificate not found"); + await tunnelMessengerAdapter.SendConnectFail(tunnelTransportInfo).ConfigureAwait(false); + return; + } + RelayInfo relayInfo = tunnelTransportInfo.TransactionTag.DeJson(); IPEndPoint ep = relayInfo.Node == null || relayInfo.Node.Address.Equals(IPAddress.Any) ? signInClientState.Connection.Address : relayInfo.Node; @@ -281,6 +293,7 @@ namespace linker.tunnel.transport { ITunnelConnection connection = await WaitSSL(socket, tunnelTransportInfo, relayInfo); OnConnected(connection); + await tunnelMessengerAdapter.SendConnectSuccess(tunnelTransportInfo).ConfigureAwait(false); return; } } @@ -300,6 +313,7 @@ namespace linker.tunnel.transport } } OnConnected(null); + await tunnelMessengerAdapter.SendConnectFail(tunnelTransportInfo).ConfigureAwait(false); } private async Task WaitSSL(Socket socket, TunnelTransportInfo tunnelTransportInfo, RelayInfo relayInfo) { @@ -346,16 +360,11 @@ namespace linker.tunnel.transport public virtual void OnFail(TunnelTransportInfo tunnelTransportInfo) { } - public virtual void OnSuccess(TunnelTransportInfo tunnelTransportInfo) { } - public virtual void SetSSL(X509Certificate certificate) - { - } - - public async Task> RelayTestAsync() + public async Task> RelayTestAsync() { try { @@ -368,13 +377,13 @@ namespace linker.tunnel.transport if (resp.Code == MessageResponeCodes.OK) { - return serializer.Deserialize>(resp.Data.Span); + return serializer.Deserialize>(resp.Data.Span); } } catch (Exception) { } - return new List(); + return new List(); } } @@ -391,7 +400,7 @@ namespace linker.tunnel.transport { public RelayInfo Info { get; set; } public string MasterId { get; set; } - public List Nodes { get; set; } = new List(); + public List Nodes { get; set; } = new List(); } public sealed partial class RelayMessageInfo { diff --git a/src/linker.messenger.serializer.memorypack/RelaySerializer.cs b/src/linker.messenger.serializer.memorypack/RelaySerializer.cs index ca701f80..03c10afa 100644 --- a/src/linker.messenger.serializer.memorypack/RelaySerializer.cs +++ b/src/linker.messenger.serializer.memorypack/RelaySerializer.cs @@ -16,10 +16,10 @@ namespace linker.messenger.serializer.memorypack [MemoryPackInclude] string MasterId => info.MasterId; [MemoryPackInclude] - List Nodes => info.Nodes; + List Nodes => info.Nodes; [MemoryPackConstructor] - SerializableRelayAskResultInfo(string masterId, List nodes) + SerializableRelayAskResultInfo(string masterId, List nodes) { var info = new RelayAskResultInfo { MasterId = masterId, Nodes = nodes }; this.info = info; @@ -55,7 +55,7 @@ namespace linker.messenger.serializer.memorypack reader.TryReadObjectHeader(out byte count); value.MasterId = reader.ReadValue(); if (count > 1) - value.Nodes = reader.ReadValue>(); + value.Nodes = reader.ReadValue>(); } } diff --git a/src/linker.messenger.store.file/Entry.cs b/src/linker.messenger.store.file/Entry.cs index 1ee73be4..33b6d0a2 100644 --- a/src/linker.messenger.store.file/Entry.cs +++ b/src/linker.messenger.store.file/Entry.cs @@ -69,6 +69,7 @@ namespace linker.messenger.store.file serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); diff --git a/src/linker.messenger.store.file/relay/Config.cs b/src/linker.messenger.store.file/relay/Config.cs index 8c311558..ec711b44 100644 --- a/src/linker.messenger.store.file/relay/Config.cs +++ b/src/linker.messenger.store.file/relay/Config.cs @@ -9,7 +9,6 @@ namespace linker.messenger.store.file public RelayInfo Relay { get; set; } = new RelayInfo(); } - public sealed class RelayInfo { public string DefaultNodeId { get; set; } = string.Empty; @@ -18,19 +17,7 @@ namespace linker.messenger.store.file public partial class ConfigServerInfo { - /// - /// 中继配置 - /// - public RelayConfigServerInfo Relay { get; set; } = new RelayConfigServerInfo(); - } - public sealed class RelayConfigServerInfo - { - public DistributedInfo Distributed { get; set; } = new DistributedInfo { }; - } - - public sealed class DistributedInfo - { - public RelayServerNodeInfo Node { get; set; } = new RelayServerNodeInfo { }; + public RelayServerConfigInfo Relay { get; set; } = new RelayServerConfigInfo(); } } diff --git a/src/linker.messenger.store.file/relay/RelayServerConfigStore.cs b/src/linker.messenger.store.file/relay/RelayServerConfigStore.cs new file mode 100644 index 00000000..883e5dbb --- /dev/null +++ b/src/linker.messenger.store.file/relay/RelayServerConfigStore.cs @@ -0,0 +1,34 @@ +using linker.messenger.relay.server; + +namespace linker.messenger.store.file.relay +{ + public sealed class RelayServerConfigStore : IRelayServerConfigStore + { + public RelayServerConfigInfo Config => config.Data.Server.Relay; + + private readonly FileConfig config; + public RelayServerConfigStore(FileConfig config) + { + this.config = config; + } + + public void Confirm() + { + config.Data.Update(); + } + + public void SetInfo(RelayServerConfigInfo node) + { + config.Data.Server.Relay = node; + } + public void SetDataRemain(long value) + { + config.Data.Server.Relay.DataRemain = value; + } + + public void DataMonth(int month) + { + config.Data.Server.Relay.DataMonth = month; + } + } +} diff --git a/src/linker.messenger.store.file/relay/RelayServerNodeStore.cs b/src/linker.messenger.store.file/relay/RelayServerNodeStore.cs index f016dad6..d69f8341 100644 --- a/src/linker.messenger.store.file/relay/RelayServerNodeStore.cs +++ b/src/linker.messenger.store.file/relay/RelayServerNodeStore.cs @@ -1,37 +1,35 @@ using linker.messenger.relay.server; +using LiteDB; namespace linker.messenger.store.file.relay { public sealed class RelayServerNodeStore : IRelayServerNodeStore { - public int ServicePort => config.Data.Server.ServicePort; - public RelayServerNodeInfo Node => config.Data.Server.Relay.Distributed.Node; + private readonly ILiteCollection liteCollection; - private readonly FileConfig config; - public RelayServerNodeStore(FileConfig config) + public RelayServerNodeStore(Storefactory storefactory) { - this.config = config; + liteCollection = storefactory.GetCollection("relay_server_master"); } - public void Confirm() + public async Task Add(RelayServerNodeStoreInfo info) { - config.Data.Update(); + if(liteCollection.FindOne(c=>c.NodeId == info.NodeId) != null) + { + return false; + } + liteCollection.Insert(info); + return await Task.FromResult(true).ConfigureAwait(false); } - public void SetInfo(RelayServerNodeInfo node) + public async Task> GetAll() { - config.Data.Server.Relay.Distributed.Node = node; - } - public void SetMaxGbTotalLastBytes(long value) - { - config.Data.Server.Relay.Distributed.Node.DataRemain = value; + return await Task.FromResult(liteCollection.FindAll().ToList()).ConfigureAwait(false); } - public void SetMaxGbTotalMonth(int month) + public async Task GetByNodeId(string nodeId) { - config.Data.Server.Relay.Distributed.Node.DataMonth = month; + return await Task.FromResult(liteCollection.FindOne(c => c.NodeId == nodeId)).ConfigureAwait(false); } - - } }