From a6087f98a6dfd34f1bbfb0cb159b19a3aaf3d5a2 Mon Sep 17 00:00:00 2001 From: yeziuku <53083103+yeziuku@users.noreply.github.com> Date: Sun, 5 Apr 2026 22:53:33 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BD=91=E7=BB=9C=E5=B1=82=E4=BC=98=E5=8C=96?= =?UTF-8?q?=20(#152)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Api/Transmittal/DataRequest.cs | 12 ++- Api/Utility/TaskScheduler.cs | 4 +- Controller/RunTimeController.cs | 2 +- Library/Common/Architecture/HeartBeat.cs | 93 ++++++++++++++++-------- Library/Common/Network/HTTPClient.cs | 3 +- Library/Common/Network/Socket.cs | 7 +- 6 files changed, 79 insertions(+), 42 deletions(-) diff --git a/Api/Transmittal/DataRequest.cs b/Api/Transmittal/DataRequest.cs index 0bdcc6f..9a3f624 100644 --- a/Api/Transmittal/DataRequest.cs +++ b/Api/Transmittal/DataRequest.cs @@ -27,10 +27,14 @@ namespace Milimoe.FunGame.Core.Api.Transmittal /// public bool IsDisposed => _isDisposed; - // 获取ResultData中key值对应的Json字符串 - // -- 此索引器仅返回Json字符串,对象类型请使用反序列化方法GetResult() -- - // -- 当然也可以自己反序列化 -- - // -- 基本类型可能有效,但仍建议使用反序列化方法 -- + /// + /// 获取ResultData中key值对应的Json字符串 + /// -- 此索引器仅返回Json字符串,对象类型请使用反序列化方法GetResult{T}() -- + /// -- 当然也可以自己反序列化 -- + /// -- 基本类型可能有效,但仍建议使用反序列化方法 -- + /// + /// + /// public object? this[string key] { get diff --git a/Api/Utility/TaskScheduler.cs b/Api/Utility/TaskScheduler.cs index 5ca3381..da4d6ea 100644 --- a/Api/Utility/TaskScheduler.cs +++ b/Api/Utility/TaskScheduler.cs @@ -21,14 +21,14 @@ namespace Milimoe.FunGame.Core.Api.Utility /// public TaskScheduler() { - Task.Factory.StartNew(async () => + Task.Run(async () => { while (true) { CheckAndRunTasks(); await Task.Delay(1000); } - }, TaskCreationOptions.LongRunning); + }); } /// diff --git a/Controller/RunTimeController.cs b/Controller/RunTimeController.cs index dc786e8..ccc5f57 100644 --- a/Controller/RunTimeController.cs +++ b/Controller/RunTimeController.cs @@ -520,7 +520,7 @@ namespace Milimoe.FunGame.Core.Controller /// protected void StartReceiving() { - _ReceivingTask = Task.Factory.StartNew(() => + _ReceivingTask = Task.Run(() => { Thread.Sleep(100); _IsReceiving = true; diff --git a/Library/Common/Architecture/HeartBeat.cs b/Library/Common/Architecture/HeartBeat.cs index 02181ea..9389f07 100644 --- a/Library/Common/Architecture/HeartBeat.cs +++ b/Library/Common/Architecture/HeartBeat.cs @@ -1,20 +1,25 @@ -using Milimoe.FunGame.Core.Interface.Sockets; +using System.Diagnostics; +using Milimoe.FunGame.Core.Interface.Sockets; using Milimoe.FunGame.Core.Library.Common.Network; using Milimoe.FunGame.Core.Library.Constant; namespace Milimoe.FunGame.Core.Library.Common.Architecture { - public class HeartBeat : ISocketHeartBeat + internal class HeartBeat : ISocketHeartBeat { public TransmittalType TransmittalType { get; } = TransmittalType.Socket; public bool SendingHeartBeat => _sendingHeartBeat; public int HeartBeatFaileds => _heartBeatFaileds; + public int Ping => Math.Min(_ping, 999); private Task? _sendingHeartBeatTask; private bool _sendingHeartBeat = false; - private bool _lastHeartbeatReceived = false; private int _heartBeatFaileds = 0; + private string _currentProbeId = ""; + private long _lastSentTimestamp; + private int _ping = 0; + private readonly Lock _probeLock = new(); private readonly Socket? _socket = null; private readonly HTTPClient? _httpClient = null; @@ -37,7 +42,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Architecture _sendingHeartBeat = true; _socket?.AddSocketObjectHandler(SocketObject_Handler); _httpClient?.AddSocketObjectHandler(SocketObject_Handler); - _sendingHeartBeatTask = Task.Factory.StartNew(SendHeartBeat); + _sendingHeartBeatTask = Task.Run(SendHeartBeat); } } @@ -55,39 +60,45 @@ namespace Milimoe.FunGame.Core.Library.Common.Architecture try { await Task.Delay(100); - if (_socket != null) + while (_sendingHeartBeat) { - while (_socket.Connected) + // 发送心跳包 + string probeId = Guid.NewGuid().ToString(); + lock (_probeLock) { - if (!SendingHeartBeat) _sendingHeartBeat = true; - // 发送心跳包 - _lastHeartbeatReceived = false; - if (_socket.Send(SocketMessageType.HeartBeat) == SocketResult.Success) + _currentProbeId = probeId; + _lastSentTimestamp = Stopwatch.GetTimestamp(); + } + try + { + bool sendSuccess = false; + if (_socket != null) + { + sendSuccess = _socket.Connected && _socket.Send(SocketMessageType.HeartBeat, _currentProbeId) == SocketResult.Success; + } + else if (_httpClient != null) + { + sendSuccess = _httpClient.WebSocket?.State == System.Net.WebSockets.WebSocketState.Open && await _httpClient.Send(SocketMessageType.HeartBeat, _currentProbeId) == SocketResult.Success; + } + if (!sendSuccess) + { + throw new ConnectFailedException(); + } + else { await Task.Delay(4 * 1000); - if (!_lastHeartbeatReceived) AddHeartBeatFaileds(); + lock (_probeLock) + { + if (_currentProbeId == probeId) throw new TimeOutException(); + } } - else AddHeartBeatFaileds(); - await Task.Delay(20 * 1000); } - } - else if (_httpClient != null) - { - while (_httpClient.WebSocket?.State == System.Net.WebSockets.WebSocketState.Open) + catch { - if (!SendingHeartBeat) _sendingHeartBeat = true; - // 发送心跳包 - _lastHeartbeatReceived = false; - if (await _httpClient.Send(SocketMessageType.HeartBeat) == SocketResult.Success) - { - await Task.Delay(4 * 1000); - if (!_lastHeartbeatReceived) AddHeartBeatFaileds(); - } - else AddHeartBeatFaileds(); - await Task.Delay(20 * 1000); + AddHeartBeatFaileds(); } + await Task.Delay(20 * 1000); } - _sendingHeartBeat = false; } catch (System.Exception e) { @@ -102,12 +113,18 @@ namespace Milimoe.FunGame.Core.Library.Common.Architecture _httpClient.Close(); } } + finally + { + _sendingHeartBeat = false; + } } private void AddHeartBeatFaileds() { + _currentProbeId = ""; + _ping = 999; // 超过三次没回应心跳,服务器连接失败。 - if (_heartBeatFaileds++ >= 3) + if (++_heartBeatFaileds >= 3) { _socket?.Close(); _httpClient?.Close(); @@ -119,8 +136,22 @@ namespace Milimoe.FunGame.Core.Library.Common.Architecture { if (obj.SocketType == SocketMessageType.HeartBeat) { - _lastHeartbeatReceived = true; - _heartBeatFaileds = 0; + string receivedId = ""; + if (obj.Length > 0) + { + receivedId = obj.GetParam(0) ?? ""; + } + lock (_probeLock) + { + if (receivedId == _currentProbeId && !string.IsNullOrEmpty(_currentProbeId)) + { + long elapsedTicks = Stopwatch.GetTimestamp() - _lastSentTimestamp; + double rttMs = elapsedTicks * 1000.0 / Stopwatch.Frequency; + _ping = (int)Math.Round(rttMs); + _heartBeatFaileds = 0; + _currentProbeId = ""; + } + } } } } diff --git a/Library/Common/Network/HTTPClient.cs b/Library/Common/Network/HTTPClient.cs index 5d01337..cc4c9eb 100644 --- a/Library/Common/Network/HTTPClient.cs +++ b/Library/Common/Network/HTTPClient.cs @@ -20,6 +20,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Network public string ServerNotice { get; } = ""; public bool Connected => WebSocket != null && WebSocket.State == WebSocketState.Open; public bool Receiving => _receiving; + public int Ping => HeartBeat.Ping; private HeartBeat HeartBeat { get; } public event Action? ConnectionLost; @@ -36,7 +37,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Network ServerPort = serverPort; HeartBeat = new(this); HeartBeat.StartSendingHeartBeat(); - Task.Factory.StartNew(async () => await StartListening(args)); + Task.Run(async () => await StartListening(args)); } public static async Task Connect(string serverAddress, bool ssl, int serverPort = 0, string subUrl = "", params object[] args) diff --git a/Library/Common/Network/Socket.cs b/Library/Common/Network/Socket.cs index 1c4aced..1cda36b 100644 --- a/Library/Common/Network/Socket.cs +++ b/Library/Common/Network/Socket.cs @@ -17,6 +17,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Network public string ServerNotice { get; } = ""; public bool Connected => Instance != null && Instance.Connected; public bool Receiving => _receiving; + public int Ping => HeartBeat.Ping; private HeartBeat HeartBeat { get; } public event Action? ConnectionLost; @@ -28,9 +29,9 @@ namespace Milimoe.FunGame.Core.Library.Common.Network private Socket(System.Net.Sockets.Socket instance, string serverAddress, int serverPort) { - this.Instance = instance; - this.ServerAddress = serverAddress; - this.ServerPort = serverPort; + Instance = instance; + ServerAddress = serverAddress; + ServerPort = serverPort; HeartBeat = new(this); HeartBeat.StartSendingHeartBeat(); }