using linker.libs; using linker.libs.extends; using System.Buffers; using System.Net; using System.Net.Sockets; namespace linker.messenger.relay.server { /// /// 中继节点操作 /// public class RelayServerNodeTransfer { private uint connectionNum = 0; private readonly ICrypto cryptoNode; private readonly ICrypto cryptoMaster; private ulong bytes = 0; private ulong lastBytes = 0; RelaySpeedLimit limitTotal = new RelaySpeedLimit(); private readonly ISerializer serializer; private readonly IRelayServerNodeStore relayServerNodeStore; private readonly IRelayServerMasterStore relayServerMasterStore; public RelayServerNodeTransfer(ISerializer serializer, IRelayServerNodeStore relayServerNodeStore, IRelayServerMasterStore relayServerMasterStore) { this.serializer = serializer; this.relayServerNodeStore = relayServerNodeStore; this.relayServerMasterStore = relayServerMasterStore; limitTotal.SetLimit((uint)Math.Ceiling((relayServerNodeStore.Node.MaxBandwidthTotal * 1024 * 1024) / 8.0)); cryptoNode = CryptoFactory.CreateSymmetric(relayServerNodeStore.Node.MasterSecretKey); cryptoMaster = CryptoFactory.CreateSymmetric(relayServerMasterStore.Master.SecretKey); ReportTask(); } public async ValueTask TryGetRelayCache(string key, string nodeid) { byte[] buffer = ArrayPool.Shared.Rent(2 * 1024); try { IPEndPoint server = nodeid == RelayServerNodeInfo.MASTER_NODE_ID ? new IPEndPoint(IPAddress.Loopback, relayServerNodeStore.ServicePort) : await NetworkHelper.GetEndPointAsync(relayServerNodeStore.Node.MasterHost, 1802); ICrypto crypto = nodeid == RelayServerNodeInfo.MASTER_NODE_ID ? cryptoMaster : cryptoNode; Socket socket = new Socket(server.AddressFamily, SocketType.Stream, ProtocolType.Tcp); long start = Environment.TickCount64; await socket.ConnectAsync(server).ConfigureAwait(false); long time = Environment.TickCount64 - start; await socket.SendAsync(new byte[] { (byte)ResolverType.RelayReport }); await socket.SendAsync(key.ToBytes()); int length = await socket.ReceiveAsync(buffer.AsMemory(), SocketFlags.None).AsTask().WaitAsync(TimeSpan.FromMilliseconds(Math.Max(time * 2, 5000))).ConfigureAwait(false); socket.SafeClose(); RelayCacheInfo result = serializer.Deserialize(crypto.Decode(buffer.AsMemory(0, length).ToArray()).Span); return result; } catch (Exception ex) { if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG) LoggerHelper.Instance.Error($"{ex}"); } finally { ArrayPool.Shared.Return(buffer); } return null; } /// /// 无效请求 /// /// public bool Validate() { return ValidateConnection() && ValidateBytes(); } /// /// 增加连接数 /// public void IncrementConnectionNum() { Interlocked.Increment(ref connectionNum); } /// /// 减少连接数 /// public void DecrementConnectionNum() { Interlocked.Decrement(ref connectionNum); } /// /// 连接数是否够 /// /// public bool ValidateConnection() { bool res = relayServerNodeStore.Node.MaxConnection == 0 || relayServerNodeStore.Node.MaxConnection * 2 > connectionNum; if (res == false && LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG) LoggerHelper.Instance.Debug($"relay ValidateConnection false,{connectionNum}/{relayServerNodeStore.Node.MaxConnection * 2}"); return res; } /// /// 流量是否够 /// /// public bool ValidateBytes() { bool res = relayServerNodeStore.Node.MaxGbTotal == 0 || (relayServerNodeStore.Node.MaxGbTotal > 0 && relayServerNodeStore.Node.MaxGbTotalLastBytes > 0); if (res == false && LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG) LoggerHelper.Instance.Debug($"relay ValidateBytes false,{relayServerNodeStore.Node.MaxGbTotalLastBytes}bytes/{relayServerNodeStore.Node.MaxGbTotal}gb"); return res; } /// /// 添加流量 /// /// /// public bool AddBytes(ulong length) { bytes += length; if (relayServerNodeStore.Node.MaxGbTotal == 0) { return true; } if (relayServerNodeStore.Node.MaxGbTotalLastBytes >= length) relayServerNodeStore.SetMaxGbTotalLastBytes(relayServerNodeStore.Node.MaxGbTotalLastBytes - length); else relayServerNodeStore.SetMaxGbTotalLastBytes(0); return relayServerNodeStore.Node.MaxGbTotalLastBytes > 0; } /// /// 获取单个限速 /// /// public uint GetBandwidthLimit() { return (uint)Math.Ceiling((relayServerNodeStore.Node.MaxBandwidth * 1024 * 1024) / 8.0); } /// /// 是否需要总限速 /// /// public bool NeedLimit() { return limitTotal.NeedLimit(); } /// /// 总限速 /// /// /// public bool TryLimit(ref int length) { return limitTotal.TryLimit(ref length); } private void ResetBytes() { if (relayServerNodeStore.Node.MaxGbTotalMonth != DateTime.Now.Month) { relayServerNodeStore.SetMaxGbTotalMonth(DateTime.Now.Month); relayServerNodeStore.SetMaxGbTotalLastBytes((ulong)(relayServerNodeStore.Node.MaxGbTotal * 1024 * 1024 * 1024)); } relayServerNodeStore.Confirm(); } private void ReportTask() { TimerHelper.SetInterval(async () => { ResetBytes(); IEnumerable nodes = new List { //默认报告给自己,作为本服务器的一个默认中继节点 new RelayServerNodeInfo{ Id = RelayServerNodeInfo.MASTER_NODE_ID, Host = new IPEndPoint(IPAddress.Any, relayServerNodeStore.ServicePort).ToString(), MasterHost = new IPEndPoint(IPAddress.Loopback, relayServerNodeStore.ServicePort).ToString(), MasterSecretKey = relayServerMasterStore.Master.SecretKey, MaxBandwidth = 0, MaxConnection = 0, MaxBandwidthTotal=0, MaxGbTotal=0, MaxGbTotalLastBytes=0, MaxGbTotalMonth=0, Name = "default", Public = false }, //配置的中继节点 relayServerNodeStore.Node }.Where(c => string.IsNullOrWhiteSpace(c.MasterHost) == false && string.IsNullOrWhiteSpace(c.MasterSecretKey) == false) .Where(c => string.IsNullOrWhiteSpace(c.Name) == false && string.IsNullOrWhiteSpace(c.Id) == false); double diff = (bytes - lastBytes) * 8 / 1024.0 / 1024.0; lastBytes = bytes; foreach (var node in nodes) { try { ICrypto crypto = node.Id == RelayServerNodeInfo.MASTER_NODE_ID ? cryptoMaster : cryptoNode; IPEndPoint endPoint = await NetworkHelper.GetEndPointAsync(node.Host, relayServerNodeStore.ServicePort) ?? new IPEndPoint(IPAddress.Any, relayServerNodeStore.ServicePort); RelayServerNodeReportInfo relayNodeReportInfo = new RelayServerNodeReportInfo { Id = node.Id, Name = node.Name, Public = node.Public, MaxBandwidth = node.MaxBandwidth, BandwidthRatio = Math.Round(node.MaxBandwidthTotal == 0 ? 0 : diff / 5 / node.MaxBandwidthTotal, 2), MaxBandwidthTotal = node.MaxBandwidthTotal, MaxGbTotal = node.MaxGbTotal, MaxGbTotalLastBytes = node.MaxGbTotalLastBytes, MaxConnection = node.MaxConnection, ConnectionRatio = Math.Round(node.MaxConnection == 0 ? 0 : connectionNum / 2.0 / node.MaxConnection, 2), EndPoint = endPoint, }; IPEndPoint ep = await NetworkHelper.GetEndPointAsync(node.MasterHost, relayServerNodeStore.ServicePort); byte[] content = crypto.Encode(serializer.Serialize(relayNodeReportInfo)); byte[] data = new byte[content.Length + 1]; data[0] = (byte)ResolverType.RelayReport; content.AsMemory().CopyTo(data.AsMemory(1)); using UdpClient udpClient = new UdpClient(AddressFamily.InterNetwork); udpClient.Client.WindowsUdpBug(); await udpClient.SendAsync(data, ep); } catch (Exception) { } } return true; }, () => 5000); } } }