From a6dd859b2928bc60b5ed3d1af38d22e931731d7b Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Thu, 20 Apr 2023 14:48:49 +0800 Subject: [PATCH 01/16] =?UTF-8?q?=F0=9F=92=BE=20Feat(Interfaces):=20Init?= =?UTF-8?q?=20basic=20interfaces=20for=20network=20and=20module=20controll?= =?UTF-8?q?er.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Interfaces/IModuleController.cs | 13 +++++++++++++ Interfaces/Network/IKitXClient.cs | 10 ++++++++++ Interfaces/Network/IKitXServer.cs | 14 ++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 Interfaces/IModuleController.cs create mode 100644 Interfaces/Network/IKitXClient.cs create mode 100644 Interfaces/Network/IKitXServer.cs diff --git a/Interfaces/IModuleController.cs b/Interfaces/IModuleController.cs new file mode 100644 index 00000000..71f0733d --- /dev/null +++ b/Interfaces/IModuleController.cs @@ -0,0 +1,13 @@ +using System; +using System.Threading.Tasks; + +namespace KitX_Dashboard.Interfaces; + +internal interface IModuleController : IDisposable +{ + Task Start(); + + Task Stop(); + + Task Restart(); +} diff --git a/Interfaces/Network/IKitXClient.cs b/Interfaces/Network/IKitXClient.cs new file mode 100644 index 00000000..f3c565c2 --- /dev/null +++ b/Interfaces/Network/IKitXClient.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; + +namespace KitX_Dashboard.Interfaces.Network; + +internal interface IKitXClient : IModuleController +{ + Task Connect(); + + Task Disconnect(); +} diff --git a/Interfaces/Network/IKitXServer.cs b/Interfaces/Network/IKitXServer.cs new file mode 100644 index 00000000..9532748e --- /dev/null +++ b/Interfaces/Network/IKitXServer.cs @@ -0,0 +1,14 @@ +using System; +using System.Net.Sockets; +using System.Threading.Tasks; + +namespace KitX_Dashboard.Interfaces.Network; + +internal interface IKitXServer : IModuleController +{ + Task Broadcast(byte[] content); + + Task BroadCast(byte[] content, Func? pattern); + + Task Send(byte[] content, string target); +} From 12f240425962c86a9319bae17a17002416559d0c Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Thu, 20 Apr 2023 14:50:27 +0800 Subject: [PATCH 02/16] =?UTF-8?q?=F0=9F=92=BE=20Feat(TasksManager):=20Adde?= =?UTF-8?q?d=20`RunTask`=20and=20`RunTaskAsync`=20methods=20to=20execute?= =?UTF-8?q?=20codes=20with=20better=20log.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Managers/TasksManager.cs | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/Managers/TasksManager.cs b/Managers/TasksManager.cs index 167a4dab..ccfaa85b 100644 --- a/Managers/TasksManager.cs +++ b/Managers/TasksManager.cs @@ -1,6 +1,8 @@ using Common.BasicHelper.Utils.Extensions; +using Serilog; using System; using System.Collections.Generic; +using System.Threading.Tasks; namespace KitX_Dashboard.Managers; @@ -33,4 +35,40 @@ internal void SignalRun(string signal, Action action) SignalTasks[signal].Enqueue(action); else SignalTasks.Add(signal, new Queue().Push(action)); } + + /// + /// 执行任务, 并带有更好的日志显示 + /// + /// 要执行的动作 + /// 日志显示名称 + /// 日志提示 + public static void RunTask( + Action action, + string name = nameof(Action), + string prompt = ">>> ") + { + Log.Information($"{prompt}Task `{name}` began."); + + action(); + + Log.Information($"{prompt}Task `{name}` done."); + } + + /// + /// 异步执行任务, 并带有更好的日志显示 + /// + /// 要执行的动作 + /// 任务名称 + /// 日志提示 + public static async Task RunTaskAsync( + Action action, + string name = nameof(Action), + string prompt = ">>> ") + { + Log.Information($"{prompt}Task `{name}` began."); + + await Task.Run(action); + + Log.Information($"{prompt}Task `{name}` done."); + } } From 2bfdea21779a256f6dd68d73c4b87537e5a0f37a Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Thu, 20 Apr 2023 14:52:05 +0800 Subject: [PATCH 03/16] =?UTF-8?q?=F0=9F=A7=A9=20Refactor(DevicesNetwork):?= =?UTF-8?q?=20Move=20UDP=20parts=20to=20`DevicesDiscoveryServer.cs`=20and?= =?UTF-8?q?=20`NetworkHelper.cs`.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Managers/WebManager.cs | 8 +- Servers/DevicesDiscoveryServer.cs | 374 +++++++++++++++++++++ Servers/DevicesServer.cs | 525 +----------------------------- Servers/NetworkHelper.cs | 219 +++++++++++++ 4 files changed, 601 insertions(+), 525 deletions(-) create mode 100644 Servers/DevicesDiscoveryServer.cs create mode 100644 Servers/NetworkHelper.cs diff --git a/Managers/WebManager.cs b/Managers/WebManager.cs index 2583ccc2..0242f5bb 100644 --- a/Managers/WebManager.cs +++ b/Managers/WebManager.cs @@ -17,6 +17,8 @@ public WebManager() internal PluginsServer? pluginsServer; internal DevicesServer? devicesServer; + internal DevicesDiscoveryServer? devicesDiscoveryServer; + internal ObservableCollection? NetworkInterfaceRegistered; /// @@ -27,7 +29,7 @@ public WebManager() /// 网络管理器本身 public WebManager Start(bool startPluginsServer = true, bool startDevicesServer = true) { - new Thread(() => + new Thread(async () => { try { @@ -51,6 +53,8 @@ public WebManager Start(bool startPluginsServer = true, bool startDevicesServer pluginsServer = new(); pluginsServer.Start(); } + + devicesDiscoveryServer = await new DevicesDiscoveryServer().Start(); } catch (Exception ex) { @@ -81,6 +85,8 @@ public WebManager Stop(bool stopPluginsServer = true, bool stopDevicesServer = t devicesServer?.Dispose(); } + devicesDiscoveryServer?.Stop(); + return this; } diff --git a/Servers/DevicesDiscoveryServer.cs b/Servers/DevicesDiscoveryServer.cs new file mode 100644 index 00000000..78ad691f --- /dev/null +++ b/Servers/DevicesDiscoveryServer.cs @@ -0,0 +1,374 @@ +using Common.BasicHelper.Utils.Extensions; +using KitX.Web.Rules; +using KitX_Dashboard.Data; +using KitX_Dashboard.Interfaces.Network; +using KitX_Dashboard.Managers; +using KitX_Dashboard.Names; +using Serilog; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.NetworkInformation; +using System.Net.Sockets; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +namespace KitX_Dashboard.Servers; + +internal class DevicesDiscoveryServer : IKitXServer +{ + private static UdpClient? UdpSender = null; + + private static UdpClient? UdpReceiver = null; + + private static System.Timers.Timer? UdpSendTimer = null; + + private static int DeviceInfoStructUpdatedTimes = 0; + + private static int LastTimeToOSVersionUpdated = 0; + + private static bool CloseDevicesDiscoveryServerRequest = false; + + private static readonly List SupportedNetworkInterfacesIndexes = new(); + + internal static readonly Queue Messages2BroadCast = new(); + + internal static DeviceInfoStruct DefaultDeviceInfoStruct = NetworkHelper.GetDeviceInfo(); + + /// + /// 寻找受支持的网络适配器并把UDP客户端加入组播 + /// + private static void FindSupportNetworkInterfaces(List clients, IPAddress multicastAddress) + { + var multicastGroupJoinedInterfacesCount = 0; + + foreach (var adapter in NetworkInterface.GetAllNetworkInterfaces()) + { + var adapterProperties = adapter.GetIPProperties(); + if (adapterProperties is null) continue; + + try + { + var logs = adapter.Dump2Lines(); + for (int i = 0; i < logs.Length; i++) + Log.Information(logs[i]); + } + catch (Exception ex) + { + Log.Warning(ex, "Logging network interface items."); + } + + if (!NetworkHelper.CheckNetworkInterface(adapter, adapterProperties)) continue; + + var unicastIPAddresses = adapterProperties.UnicastAddresses; + if (unicastIPAddresses is null) continue; + + var p = adapterProperties.GetIPv4Properties(); + if (p is null) continue; // IPv4 is not configured on this adapter + + SupportedNetworkInterfacesIndexes.Add(IPAddress.HostToNetworkOrder(p.Index)); + + foreach (var ipAddress in unicastIPAddresses + .Select(x => x.Address) + .Where(x => x.AddressFamily == AddressFamily.InterNetwork)) + { + try + { + foreach (var udpClient in clients) + udpClient?.JoinMulticastGroup(multicastAddress, ipAddress); + + Program.WebManager?.NetworkInterfaceRegistered?.Add(adapter.Name); + + ++multicastGroupJoinedInterfacesCount; + } + catch (Exception ex) + { + var location = $"{nameof(DevicesServer)}.{nameof(FindSupportNetworkInterfaces)}"; + Log.Error(ex, $"In {location}: {ex.Message}"); + } + } + } + + Program.TasksManager?.RaiseSignal(nameof(SignalsNames.FinishedFindingNetworkInterfacesSignal)); + + Log.Information($"" + + $"Find {SupportedNetworkInterfacesIndexes.Count} supported network interfaces."); + Log.Information($"" + + $"Joined {multicastGroupJoinedInterfacesCount} multicast groups."); + } + + /// + /// 更新默认设备信息结构 + /// + private static void UpdateDefaultDeviceInfoStruct() + { + DefaultDeviceInfoStruct.IsMainDevice = GlobalInfo.IsMainMachine; + DefaultDeviceInfoStruct.SendTime = DateTime.UtcNow; + DefaultDeviceInfoStruct.IPv4 = NetworkHelper.GetInterNetworkIPv4(); + DefaultDeviceInfoStruct.IPv6 = NetworkHelper.GetInterNetworkIPv6(); + DefaultDeviceInfoStruct.PluginServerPort = GlobalInfo.PluginServerPort; + DefaultDeviceInfoStruct.PluginsCount = Program.PluginCards.Count; + DefaultDeviceInfoStruct.IsMainDevice = GlobalInfo.IsMainMachine; + DefaultDeviceInfoStruct.DeviceServerPort = GlobalInfo.DeviceServerPort; + DefaultDeviceInfoStruct.DeviceServerBuildTime = GlobalInfo.ServerBuildTime; + + if (LastTimeToOSVersionUpdated > Program.Config.IO.OperatingSystemVersionUpdateInterval) + { + LastTimeToOSVersionUpdated = 0; + DefaultDeviceInfoStruct.DeviceOSVersion = NetworkHelper.TryGetOSVersionString(); + } + + ++DeviceInfoStructUpdatedTimes; + ++LastTimeToOSVersionUpdated; + + if (DeviceInfoStructUpdatedTimes < 0) DeviceInfoStructUpdatedTimes = 0; + } + + /// + /// 多设备广播发送方法 + /// + private void MultiDevicesBroadCastSend() + { + var location = $"{nameof(DevicesDiscoveryServer)}.{nameof(MultiDevicesBroadCastSend)}"; + + IPEndPoint multicast = new( + IPAddress.Parse(Program.Config.Web.UDPBroadcastAddress), + Program.Config.Web.UDPPortReceive + ); + UdpSender?.Client.SetSocketOption( + SocketOptionLevel.Socket, + SocketOptionName.ReuseAddress, + true + ); + + var erroredInterfacesIndexes = new List(); + var erroredInterfacesIndexesTTL = 60; + + UdpSendTimer = new() + { + Interval = Program.Config.Web.UDPSendFrequency, + AutoReset = true + }; + UdpSendTimer.Elapsed += async (_, _) => + { + --erroredInterfacesIndexesTTL; + if (erroredInterfacesIndexesTTL <= 0) + { + erroredInterfacesIndexesTTL = 60; + erroredInterfacesIndexes.Clear(); + } + + UpdateDefaultDeviceInfoStruct(); + + if (CloseDevicesDiscoveryServerRequest) + DefaultDeviceInfoStruct.SendTime -= TimeSpan.FromSeconds(20); + + var sendText = JsonSerializer.Serialize(DefaultDeviceInfoStruct); + var sendBytes = Encoding.UTF8.GetBytes(sendText); + + foreach (var item in SupportedNetworkInterfacesIndexes) + { + // 如果错误网络适配器中存在当前项的记录, 跳过 + if (erroredInterfacesIndexes.Contains(item)) continue; + + try + { + UdpSender?.Client.SetSocketOption( + SocketOptionLevel.IP, + SocketOptionName.MulticastInterface, + item + ); + UdpSender?.Send(sendBytes, sendBytes.Length, multicast); + + while (Messages2BroadCast.Count > 0) + { + var messageBytes = Encoding.UTF8.GetBytes(Messages2BroadCast.Dequeue()); + UdpSender?.Send(messageBytes, messageBytes.Length, multicast); + } // 将自定义广播消息全部发送 + } + catch (Exception ex) + { + // 该网络适配器存在异常, 暂时记录到错误网络适配器中 + if (!erroredInterfacesIndexes.Contains(item)) + erroredInterfacesIndexes.Add(item); + + Log.Warning( + ex, + $"In {location}: Errored interface index: {item}, recorded." + ); + } + } + + if (CloseDevicesDiscoveryServerRequest) + CloseDevicesDiscoveryServerRequest = false; + + if (!GlobalInfo.Running || CloseDevicesDiscoveryServerRequest) await Stop(); + }; + UdpSendTimer.Start(); + } + + /// + /// 多设备广播接收方法 + /// + private void MultiDevicesBroadCastReceive() + { + var location = $"{nameof(DevicesDiscoveryServer)}.{nameof(MultiDevicesBroadCastReceive)}"; + + IPEndPoint multicast = new(IPAddress.Any, 0); + UdpReceiver?.Client.SetSocketOption( + SocketOptionLevel.Socket, + SocketOptionName.ReuseAddress, + true + ); + + new Thread(async () => + { + try + { + while (GlobalInfo.Running && !CloseDevicesDiscoveryServerRequest) + { + var bytes = UdpReceiver?.Receive(ref multicast); + if (bytes is null) continue; // null byte[] cause exception in next line. + var result = Encoding.UTF8.GetString(bytes); + var client = $"{multicast.Address}:{multicast.Port}"; + + Log.Information($"UDP From: {client,-21}, Receive: {result}"); + + try + { + DevicesManager.Update( + JsonSerializer.Deserialize(result) + ); + } + catch (Exception ex) + { + Log.Warning(ex, $"When trying to deserialize `{result}`"); + } + } + } + catch (Exception e) + { + Log.Error(e, $"In {location}: {e.Message}"); + } + + await Stop(); + + }).Start(); + } + + private static void Init() + { + DeviceInfoStructUpdatedTimes = 0; + LastTimeToOSVersionUpdated = 0; + CloseDevicesDiscoveryServerRequest = false; + SupportedNetworkInterfacesIndexes.Clear(); + Messages2BroadCast.Clear(); + DefaultDeviceInfoStruct = NetworkHelper.GetDeviceInfo(); + } + + public async Task Start() + { + Init(); + + UdpSender = new(Program.Config.Web.UDPPortSend, AddressFamily.InterNetwork) + { + EnableBroadcast = true, + MulticastLoopback = true + }; + + UdpReceiver = new( + new IPEndPoint(IPAddress.Any, Program.Config.Web.UDPPortReceive) + ); + + await TasksManager.RunTaskAsync(() => + { + try + { + FindSupportNetworkInterfaces( + new() + { + UdpSender, UdpReceiver + }, + IPAddress.Parse(Program.Config.Web.UDPBroadcastAddress) + ); // 寻找所有支持的网络适配器 + } + catch (Exception ex) + { + var location = $"{nameof(DevicesServer)}.{nameof(Start)}"; + Log.Warning(ex, $"In {location}: {ex.Message}"); + } + }, nameof(FindSupportNetworkInterfaces)); + + await TasksManager.RunTaskAsync( + MultiDevicesBroadCastSend, + nameof(MultiDevicesBroadCastSend) + ); // 开始发送组播报文 + + await TasksManager.RunTaskAsync( + MultiDevicesBroadCastReceive, + nameof(MultiDevicesBroadCastReceive) + ); // 开始接收组播报文 + + return this; + } + + public async Task Stop() + { + await Task.Run(() => + { + CloseDevicesDiscoveryServerRequest = true; + + while (CloseDevicesDiscoveryServerRequest) { } + + UdpSendTimer?.Close(); + + UdpSender?.Close(); + UdpReceiver?.Close(); + }); + + return this; + } + + public async Task Restart() + { + await Task.Run(async () => + { + await Stop(); + await Start(); + }); + + return this; + } + + public async Task Send(byte[] content, string target) + { + await Task.Run(() => { }); + + return this; + } + + public async Task Broadcast(byte[] content) + { + await Task.Run(() => { }); + + return this; + } + + public async Task BroadCast(byte[] content, Func? pattern) + { + await Task.Run(() => { }); + + return this; + } + + public void Dispose() + { + UdpSender?.Dispose(); + UdpReceiver?.Dispose(); + + GC.SuppressFinalize(this); + } +} diff --git a/Servers/DevicesServer.cs b/Servers/DevicesServer.cs index ced8ec74..99fe101b 100644 --- a/Servers/DevicesServer.cs +++ b/Servers/DevicesServer.cs @@ -1,21 +1,12 @@ -using Common.BasicHelper.Core.Shell; -using Common.BasicHelper.Utils.Extensions; -using KitX.Web.Rules; -using KitX_Dashboard.Converters; -using KitX_Dashboard.Data; +using KitX_Dashboard.Data; using KitX_Dashboard.Managers; -using KitX_Dashboard.Names; using KitX_Dashboard.Services; using Serilog; using System; using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Net; -using System.Net.NetworkInformation; using System.Net.Sockets; using System.Text; -using System.Text.Json; using System.Threading; namespace KitX_Dashboard.Servers; @@ -32,53 +23,6 @@ public void Start() { new Thread(() => { - try - { - CloseDevicesServerUDPNetworkRequest = false; - - UdpClient_Send = new(Program.Config.Web.UDPPortSend, - AddressFamily.InterNetwork) - { - EnableBroadcast = true, - MulticastLoopback = true - }; - - UdpClient_Receive = new(new IPEndPoint(IPAddress.Any, - Program.Config.Web.UDPPortReceive)); - - // 寻找所有支持的网络适配器 - Log.Information($"Start {nameof(FindSupportNetworkInterfaces)}"); - try - { - FindSupportNetworkInterfaces(new() - { - UdpClient_Send, UdpClient_Receive - }, IPAddress.Parse(Program.Config.Web.UDPBroadcastAddress)); - } - catch (Exception ex) - { - var location = $"{nameof(DevicesServer)}.{nameof(Start)}"; - Log.Warning(ex, $"In {location}: {ex.Message}"); - } - - #region 组播收发消息 - - // 开始组播发送本机信息 - Log.Information($"Start {nameof(MultiDevicesBroadCastSend)}"); - MultiDevicesBroadCastSend(); - - // 开始接收组播消息 - Log.Information($"Start {nameof(MultiDevicesBroadCastReceive)}"); - MultiDevicesBroadCastReceive(); - - #endregion - } - catch (Exception e) - { - var location = $"{nameof(DevicesServer)}.{nameof(Start)}()"; - Log.Error(e, $"In {location}: {e.Message}"); - } - try { Log.Information($"Start Init {nameof(DevicesHost)}"); @@ -95,8 +39,6 @@ public void Start() public void Stop() { - CloseDevicesServerUDPNetworkRequest = true; - keepListen = false; foreach (KeyValuePair item in clients) @@ -111,469 +53,6 @@ public void Stop() DevicesHost?.Dispose(); } - #region UDP Socket 服务于自发现自组网 - - private static readonly List SurpportedNetworkInterfaces = new(); - - internal static readonly Queue Messages2BroadCast = new(); - - internal static DeviceInfoStruct DefaultDeviceInfoStruct = GetDeviceInfo(); - - internal static bool CloseDevicesServerUDPNetworkRequest = false; - - /// - /// UDP 发包客户端 - /// - private static UdpClient? UdpClient_Send = null; - - /// - /// UDP 收包客户端 - /// - private static UdpClient? UdpClient_Receive = null; - - /// - /// 检查网络适配器是否符合要求 - /// - /// 网络适配器 - /// 是否符合要求 - private static bool CheckNetworkInterface( - NetworkInterface adapter, IPInterfaceProperties adapterProperties) - { - var userPointed = Program.Config.Web.AcceptedNetworkInterfaces; - if (userPointed is not null) - if (userPointed.Contains(adapter.Name)) - return true; - else return false; - - if ( - - adapter.NetworkInterfaceType != NetworkInterfaceType.Ethernet && - adapter.NetworkInterfaceType != NetworkInterfaceType.Wireless80211 - - && - ( - adapterProperties.MulticastAddresses.Count == 0 || - // most of VPN adapters will be skipped - !adapter.SupportsMulticast || - // multicast is meaningless for this type of connection - OperationalStatus.Up != adapter.OperationalStatus || - // this adapter is off or not connected - !adapter.Supports(NetworkInterfaceComponent.IPv4) - ) - ) return false; - return true; - } - - /// - /// 寻找受支持的网络适配器并把UDP客户端加入组播 - /// - private static void FindSupportNetworkInterfaces(List clients, IPAddress multicastAddress) - { - var multicastGroupJoinedInterfacesCount = 0; - - foreach (var adapter in NetworkInterface.GetAllNetworkInterfaces()) - { - var adapterProperties = adapter.GetIPProperties(); - if (adapterProperties is null) continue; - - try - { - var logs = adapter.Dump2Lines(); - for (int i = 0; i < logs.Length; i++) - Log.Information(logs[i]); - } - catch (Exception ex) - { - Log.Warning(ex, "Logging network interface items."); - } - - if (!CheckNetworkInterface(adapter, adapterProperties)) continue; - - var unicastIPAddresses = adapterProperties.UnicastAddresses; - if (unicastIPAddresses is null) continue; - - var p = adapterProperties.GetIPv4Properties(); - if (p is null) continue; // IPv4 is not configured on this adapter - - SurpportedNetworkInterfaces.Add(IPAddress.HostToNetworkOrder(p.Index)); - - foreach (var ipAddress in unicastIPAddresses - .Select(x => x.Address) - .Where(x => x.AddressFamily == AddressFamily.InterNetwork)) - { - try - { - foreach (var udpClient in clients) - udpClient?.JoinMulticastGroup(multicastAddress, ipAddress); - - Program.WebManager?.NetworkInterfaceRegistered?.Add(adapter.Name); - - ++multicastGroupJoinedInterfacesCount; - } - catch (Exception ex) - { - var location = $"{nameof(DevicesServer)}.{nameof(FindSupportNetworkInterfaces)}"; - Log.Error(ex, $"In {location}: {ex.Message}"); - } - } - } - - Program.TasksManager?.RaiseSignal(nameof(SignalsNames.FinishedFindingNetworkInterfacesSignal)); - - Log.Information($"" + - $"Find {SurpportedNetworkInterfaces.Count} supported network interfaces."); - Log.Information($"" + - $"Joined {multicastGroupJoinedInterfacesCount} multicast groups."); - } - - /// - /// 多设备广播发送方法 - /// - public static void MultiDevicesBroadCastSend() - { - #region 初始化 UDP 客户端 - - UdpClient? udpClient = UdpClient_Send; - IPEndPoint multicast = - new(IPAddress.Parse(Program.Config.Web.UDPBroadcastAddress), - Program.Config.Web.UDPPortReceive); - udpClient?.Client.SetSocketOption(SocketOptionLevel.Socket, - SocketOptionName.ReuseAddress, true); - - #endregion - - var erroredInterfacesIndexes = new List(); - var erroredInterfacesIndexesTTL = 60; - - System.Timers.Timer timer = new() - { - Interval = Program.Config.Web.UDPSendFrequency, - AutoReset = true - }; - void EndMultiDevicesBroadCastSend() - { - timer.Stop(); - timer.Dispose(); - udpClient?.Close(); - } - timer.Elapsed += (_, _) => - { - try - { - --erroredInterfacesIndexesTTL; - if (erroredInterfacesIndexesTTL <= 0) - { - erroredInterfacesIndexesTTL = 60; - erroredInterfacesIndexes.Clear(); - } - - UpdateDefaultDeviceInfoStruct(); - - if (CloseDevicesServerUDPNetworkRequest) - DefaultDeviceInfoStruct.SendTime -= TimeSpan.FromSeconds(20); - - string sendText = JsonSerializer.Serialize(DefaultDeviceInfoStruct); - byte[] sendBytes = Encoding.UTF8.GetBytes(sendText); - - foreach (var item in SurpportedNetworkInterfaces) - { - // 如果错误网络适配器中存在当前项的记录, 跳过 - if (erroredInterfacesIndexes.Contains(item)) continue; - - try - { - udpClient?.Client.SetSocketOption(SocketOptionLevel.IP, - SocketOptionName.MulticastInterface, item); - udpClient?.Send(sendBytes, sendBytes.Length, multicast); - - // 将自定义广播消息全部发送 - while (Messages2BroadCast.Count > 0) - { - byte[] messageBytes = Encoding.UTF8.GetBytes(Messages2BroadCast.Dequeue()); - udpClient?.Send(messageBytes, messageBytes.Length, multicast); - } - } - catch (Exception ex) - { - // 该网络适配器存在异常, 暂时记录到错误网络适配器中 - if (!erroredInterfacesIndexes.Contains(item)) - erroredInterfacesIndexes.Add(item); - - var location = $"{nameof(DevicesServer)}.{nameof(MultiDevicesBroadCastSend)}"; - Log.Warning(ex, $"In {location}: {ex.Message} - " + - $"On interface index: {item}, recorded."); - } - } - } - catch (Exception e) - { - Log.Error(e, $"In MultiDevicesBroadCastSend: {e.Message}"); - } - if (!GlobalInfo.Running || CloseDevicesServerUDPNetworkRequest) EndMultiDevicesBroadCastSend(); - }; - timer.Start(); - } - - /// - /// 多设备广播接收方法 - /// - public static void MultiDevicesBroadCastReceive() - { - #region 初始化 UDP 客户端 - - UdpClient? udpClient = UdpClient_Receive; - IPEndPoint multicast = new(IPAddress.Any, 0); - udpClient?.Client.SetSocketOption(SocketOptionLevel.Socket, - SocketOptionName.ReuseAddress, true); - - #endregion - - new Thread(() => - { - try - { - while (GlobalInfo.Running && !CloseDevicesServerUDPNetworkRequest) - { - var bytes = udpClient?.Receive(ref multicast); - if (bytes is null) continue; // null byte[] cause exception in next line. - var result = Encoding.UTF8.GetString(bytes); - if (result is null) continue; // null string skip. - var client = $"{multicast.Address}:{multicast.Port}"; - Log.Information($"UDP " + - $"From: {client,-21}, " + - $"Receive: {result}"); - try - { - DeviceInfoStruct deviceInfo - = JsonSerializer.Deserialize(result); - DevicesManager.Update(deviceInfo); - } - catch (Exception ex) - { - Log.Warning(ex, $"{ex.Message}"); - } - } - udpClient?.Close(); - } - catch (Exception e) - { - Log.Error(e, e.Message); - } - if (!GlobalInfo.Running) - { - udpClient?.Close(); - } - else - { - - } - }).Start(); - } - - /// - /// 将 IPv4 的十进制表示按点分制拆分 - /// - /// IPv4 的十进制表示 - /// 拆分 - private static (int, int, int, int) IPv4_2_4Parts(string ip) - { - string[] p = ip.Split('.'); - int a = int.Parse(p[0]), b = int.Parse(p[1]), c = int.Parse(p[2]), d = int.Parse(p[3]); - return (a, b, c, d); - } - - /// - /// 获取本机内网 IPv4 地址 - /// - /// 使用点分十进制表示法的本机内网IPv4地址 - private static string GetInterNetworkIPv4() - { - try - { - return (from ip in Dns.GetHostEntry(Dns.GetHostName()).AddressList - where ip.AddressFamily == AddressFamily.InterNetwork - && !ip.ToString().Equals("127.0.0.1") - && (ip.ToString().StartsWith("192.168") // 192.168.x.x - || ip.ToString().StartsWith("10") // 10.x.x.x - || IPv4_2_4Parts(ip.ToString()).Item1 == 172 // 172.16-31.x.x - && IPv4_2_4Parts(ip.ToString()).Item2 >= 16 - && IPv4_2_4Parts(ip.ToString()).Item2 <= 31) - && ip.ToString().StartsWith(Program.Config.Web.IPFilter) // 满足自定义规则 - select ip).First().ToString(); - } - catch (Exception ex) - { - var location = $"{nameof(DevicesServer)}.{nameof(GetInterNetworkIPv4)}"; - Log.Warning(ex, $"In {location}: {ex.Message}"); - return string.Empty; - } - } - - /// - /// 获取本机内网 IPv6 地址 - /// - /// IPv6 地址 - private static string GetInterNetworkIPv6() - { - try - { - return (from ip in Dns.GetHostEntry(Dns.GetHostName()).AddressList - where ip.AddressFamily == AddressFamily.InterNetworkV6 - && !ip.ToString().Equals("::1") - select ip).First().ToString(); - } - catch (Exception ex) - { - var location = $"{nameof(DevicesServer)}.{nameof(GetInterNetworkIPv6)}"; - Log.Warning(ex, $"In {location}: {ex.Message}"); - return string.Empty; - } - } - - /// - /// 尝试获取设备 MAC 地址 - /// - /// MAC 地址 - private static string? TryGetDeviceMacAddress() - { - try - { - var mac = NetworkInterface.GetAllNetworkInterfaces() - .Where(nic => nic.OperationalStatus == OperationalStatus.Up - && nic.NetworkInterfaceType != NetworkInterfaceType.Loopback) - .Select(nic => nic.GetPhysicalAddress().ToString()).FirstOrDefault(); - - return mac?.SeparateGroup(2, sb => sb.Append(':')); - } - catch (Exception ex) - { - var location = $"{nameof(DevicesServer)}.{nameof(TryGetDeviceMacAddress)}"; - Log.Warning(ex, $"In {location}: {ex.Message}"); - return string.Empty; - } - } - - /// - /// 尝试获取系统版本 - /// - /// 系统版本 - private static string? TryGetOSVersionString() - { - var result = Environment.OSVersion.VersionString; - try - { - switch (OperatingSystem2Enum.GetOSType()) - { - case OperatingSystems.Linux: - - var versionFilePath = "/etc/os-release"; - var versionSegament = "PRETTY_NAME"; - var needFindInIssue = false; - - if (File.Exists(versionFilePath)) - { - var osRelease = File.ReadAllLines(versionFilePath) - .Select(line => line.Split('=')) - .ToDictionary - ( - parts => parts[0], - parts => parts[1].Trim('"') - ); - - if (osRelease.TryGetValue(versionSegament, out var version)) - result = version; - else needFindInIssue = true; - } - - if (needFindInIssue) - { - var issueFilePath = "/etc/issue"; - if (File.Exists(issueFilePath)) - { - var issue = File.ReadAllText(issueFilePath); - var lines = issue.Split('\n'); - result = lines.First(x => !x.Equals(string.Empty)); - } - } - - break; - case OperatingSystems.MacOS: - var command = "sw_vers"; - - var productName = command.ExecuteAsCommand("-productName"); - var productVersion = command.ExecuteAsCommand("-productVersion"); - var buildVersion = command.ExecuteAsCommand("-buildVersion"); - - if (productName is not null && productVersion is not null && buildVersion is not null) - result = $"{productName} {productVersion} {buildVersion}" - .Replace("\n", ""); - - break; - } - } - catch (Exception ex) - { - var location = $"{nameof(DevicesServer)}.{nameof(TryGetOSVersionString)}"; - Log.Error(ex, $"In {location}: {ex.Message}"); - } - return result; - } - - /// - /// 获取设备信息 - /// - /// 设备信息结构体 - private static DeviceInfoStruct GetDeviceInfo() => new() - { - DeviceName = Environment.MachineName, - DeviceMacAddress = TryGetDeviceMacAddress(), - IsMainDevice = GlobalInfo.IsMainMachine, - SendTime = DateTime.UtcNow, - DeviceOSType = OperatingSystem2Enum.GetOSType(), - DeviceOSVersion = TryGetOSVersionString(), - IPv4 = GetInterNetworkIPv4(), - IPv6 = GetInterNetworkIPv6(), - PluginServerPort = GlobalInfo.PluginServerPort, - DeviceServerPort = GlobalInfo.DeviceServerPort, - DeviceServerBuildTime = new(), - PluginsCount = Program.PluginCards.Count, - }; - - private static int DeviceInfoStructUpdatedTimes = 0; - - private static int LastTimeToOSVersionUpdated = 0; - - /// - /// 更新默认设备信息结构 - /// - private static void UpdateDefaultDeviceInfoStruct() - { - DefaultDeviceInfoStruct.IsMainDevice = GlobalInfo.IsMainMachine; - DefaultDeviceInfoStruct.SendTime = DateTime.UtcNow; - DefaultDeviceInfoStruct.IPv4 = GetInterNetworkIPv4(); - DefaultDeviceInfoStruct.IPv6 = GetInterNetworkIPv6(); - DefaultDeviceInfoStruct.PluginServerPort = GlobalInfo.PluginServerPort; - DefaultDeviceInfoStruct.PluginsCount = Program.PluginCards.Count; - DefaultDeviceInfoStruct.IsMainDevice = GlobalInfo.IsMainMachine; - DefaultDeviceInfoStruct.DeviceServerPort = GlobalInfo.DeviceServerPort; - DefaultDeviceInfoStruct.DeviceServerBuildTime = GlobalInfo.ServerBuildTime; - - if (LastTimeToOSVersionUpdated > Program.Config.IO.OperatingSystemVersionUpdateInterval) - { - LastTimeToOSVersionUpdated = 0; - DefaultDeviceInfoStruct.DeviceOSVersion = TryGetOSVersionString(); - } - - ++DeviceInfoStructUpdatedTimes; - ++LastTimeToOSVersionUpdated; - - if (DeviceInfoStructUpdatedTimes < 0) DeviceInfoStructUpdatedTimes = 0; - } - - #endregion - - #region TCP Socket 服务于设备间组网 - internal Thread? acceptDeviceThread; internal Thread? receiveMessageThread; internal TcpClient? DevicesHost; @@ -879,8 +358,6 @@ internal async void ReceiveMessageFromHost() } } - #endregion - public void Dispose() { GC.SuppressFinalize(this); diff --git a/Servers/NetworkHelper.cs b/Servers/NetworkHelper.cs new file mode 100644 index 00000000..3741e398 --- /dev/null +++ b/Servers/NetworkHelper.cs @@ -0,0 +1,219 @@ +using Common.BasicHelper.Core.Shell; +using Common.BasicHelper.Utils.Extensions; +using KitX.Web.Rules; +using KitX_Dashboard.Converters; +using KitX_Dashboard.Data; +using Serilog; +using System; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.NetworkInformation; +using System.Net.Sockets; + +namespace KitX_Dashboard.Servers; + +internal static class NetworkHelper +{ + /// + /// 检查网络适配器是否符合要求 + /// + /// 网络适配器 + /// 是否符合要求 + internal static bool CheckNetworkInterface + ( + NetworkInterface adapter, + IPInterfaceProperties adapterProperties + ) + { + var userPointed = Program.Config.Web.AcceptedNetworkInterfaces; + if (userPointed is not null) + if (userPointed.Contains(adapter.Name)) + return true; + else return false; + + if (adapter.NetworkInterfaceType != NetworkInterfaceType.Ethernet && + adapter.NetworkInterfaceType != NetworkInterfaceType.Wireless80211 + && + ( + adapterProperties.MulticastAddresses.Count == 0 || + // most of VPN adapters will be skipped + !adapter.SupportsMulticast || + // multicast is meaningless for this type of connection + OperationalStatus.Up != adapter.OperationalStatus || + // this adapter is off or not connected + !adapter.Supports(NetworkInterfaceComponent.IPv4) + ) + ) return false; + return true; + } + + /// + /// 将 IPv4 的十进制表示按点分制拆分 + /// + /// IPv4 的十进制表示 + /// 拆分 + internal static (int, int, int, int) IPv4_2_4Parts(string ip) + { + string[] p = ip.Split('.'); + int a = int.Parse(p[0]), b = int.Parse(p[1]), c = int.Parse(p[2]), d = int.Parse(p[3]); + return (a, b, c, d); + } + + /// + /// 获取本机内网 IPv4 地址 + /// + /// 使用点分十进制表示法的本机内网IPv4地址 + internal static string GetInterNetworkIPv4() + { + try + { + return (from ip in Dns.GetHostEntry(Dns.GetHostName()).AddressList + where ip.AddressFamily == AddressFamily.InterNetwork + && !ip.ToString().Equals("127.0.0.1") + && (ip.ToString().StartsWith("192.168") // 192.168.x.x + || ip.ToString().StartsWith("10") // 10.x.x.x + || IPv4_2_4Parts(ip.ToString()).Item1 == 172 // 172.16-31.x.x + && IPv4_2_4Parts(ip.ToString()).Item2 >= 16 + && IPv4_2_4Parts(ip.ToString()).Item2 <= 31) + && ip.ToString().StartsWith(Program.Config.Web.IPFilter) // 满足自定义规则 + select ip).First().ToString(); + } + catch (Exception ex) + { + var location = $"{nameof(NetworkHelper)}.{nameof(GetInterNetworkIPv4)}"; + Log.Warning(ex, $"In {location}: {ex.Message}"); + return string.Empty; + } + } + + /// + /// 获取本机内网 IPv6 地址 + /// + /// IPv6 地址 + internal static string GetInterNetworkIPv6() + { + try + { + return (from ip in Dns.GetHostEntry(Dns.GetHostName()).AddressList + where ip.AddressFamily == AddressFamily.InterNetworkV6 + && !ip.ToString().Equals("::1") + select ip).First().ToString(); + } + catch (Exception ex) + { + var location = $"{nameof(NetworkHelper)}.{nameof(GetInterNetworkIPv6)}"; + Log.Warning(ex, $"In {location}: {ex.Message}"); + return string.Empty; + } + } + + /// + /// 尝试获取设备 MAC 地址 + /// + /// MAC 地址 + internal static string? TryGetDeviceMacAddress() + { + try + { + var mac = NetworkInterface.GetAllNetworkInterfaces() + .Where(nic => nic.OperationalStatus == OperationalStatus.Up + && nic.NetworkInterfaceType != NetworkInterfaceType.Loopback) + .Select(nic => nic.GetPhysicalAddress().ToString()).FirstOrDefault(); + + return mac?.SeparateGroup(2, sb => sb.Append(':')); + } + catch (Exception ex) + { + var location = $"{nameof(NetworkHelper)}.{nameof(TryGetDeviceMacAddress)}"; + Log.Warning(ex, $"In {location}: {ex.Message}"); + return string.Empty; + } + } + + /// + /// 尝试获取系统版本 + /// + /// 系统版本 + internal static string? TryGetOSVersionString() + { + var result = Environment.OSVersion.VersionString; + try + { + switch (OperatingSystem2Enum.GetOSType()) + { + case OperatingSystems.Linux: + + var versionFilePath = "/etc/os-release"; + var versionSegament = "PRETTY_NAME"; + var needFindInIssue = false; + + if (File.Exists(versionFilePath)) + { + var osRelease = File.ReadAllLines(versionFilePath) + .Select(line => line.Split('=')) + .ToDictionary + ( + parts => parts[0], + parts => parts[1].Trim('"') + ); + + if (osRelease.TryGetValue(versionSegament, out var version)) + result = version; + else needFindInIssue = true; + } + + if (needFindInIssue) + { + var issueFilePath = "/etc/issue"; + if (File.Exists(issueFilePath)) + { + var issue = File.ReadAllText(issueFilePath); + var lines = issue.Split('\n'); + result = lines.First(x => !x.Equals(string.Empty)); + } + } + + break; + case OperatingSystems.MacOS: + var command = "sw_vers"; + + var productName = command.ExecuteAsCommand("-productName"); + var productVersion = command.ExecuteAsCommand("-productVersion"); + var buildVersion = command.ExecuteAsCommand("-buildVersion"); + + if (productName is not null && productVersion is not null && buildVersion is not null) + result = $"{productName} {productVersion} {buildVersion}" + .Replace("\n", ""); + + break; + } + } + catch (Exception ex) + { + var location = $"{nameof(NetworkHelper)}.{nameof(TryGetOSVersionString)}"; + Log.Error(ex, $"In {location}: {ex.Message}"); + } + return result; + } + + /// + /// 获取设备信息 + /// + /// 设备信息结构体 + internal static DeviceInfoStruct GetDeviceInfo() => new() + { + DeviceName = Environment.MachineName, + DeviceMacAddress = TryGetDeviceMacAddress(), + IsMainDevice = GlobalInfo.IsMainMachine, + SendTime = DateTime.UtcNow, + DeviceOSType = OperatingSystem2Enum.GetOSType(), + DeviceOSVersion = TryGetOSVersionString(), + IPv4 = GetInterNetworkIPv4(), + IPv6 = GetInterNetworkIPv6(), + PluginServerPort = GlobalInfo.PluginServerPort, + DeviceServerPort = GlobalInfo.DeviceServerPort, + DeviceServerBuildTime = new(), + PluginsCount = Program.PluginCards.Count, + }; +} From 4b36f0ac6922bc0218155ca8a2a538292e4a7e2d Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Thu, 20 Apr 2023 14:52:46 +0800 Subject: [PATCH 04/16] =?UTF-8?q?=F0=9F=94=A7=20Fix:=20Udp=20part=20of=20`?= =?UTF-8?q?DevicesNetwork`=20moved=20to=20new=20class.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Managers/DevicesManager.cs | 2 +- Services/DebugService.cs | 2 +- ViewModels/Pages/Controls/PluginBarViewModel.cs | 8 +++++--- ViewModels/Pages/Controls/Settings_UpdateViewModel.cs | 4 ++-- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Managers/DevicesManager.cs b/Managers/DevicesManager.cs index 3d7c1927..252a08e4 100644 --- a/Managers/DevicesManager.cs +++ b/Managers/DevicesManager.cs @@ -53,7 +53,7 @@ private static bool CheckDeviceIsOffline(DeviceInfoStruct info) /// 是否是本机卡片 private static bool CheckIsCurrentMachine(DeviceInfoStruct info) { - var self = DevicesServer.DefaultDeviceInfoStruct; + var self = DevicesDiscoveryServer.DefaultDeviceInfoStruct; return info.DeviceMacAddress.Equals(self.DeviceMacAddress) && info.DeviceName.Equals(self.DeviceName); } diff --git a/Services/DebugService.cs b/Services/DebugService.cs index 4480c933..71a87b56 100644 --- a/Services/DebugService.cs +++ b/Services/DebugService.cs @@ -127,7 +127,7 @@ private static string SaveConfig() case "deviceudppack": if (args.ContainsKey("--value")) { - DevicesServer.Messages2BroadCast.Enqueue(args["--value"]); + DevicesDiscoveryServer.Messages2BroadCast.Enqueue(args["--value"]); return "Appended value to broadcast list."; } else return "Missing value of `--value`."; diff --git a/ViewModels/Pages/Controls/PluginBarViewModel.cs b/ViewModels/Pages/Controls/PluginBarViewModel.cs index 39940496..adf18b40 100644 --- a/ViewModels/Pages/Controls/PluginBarViewModel.cs +++ b/ViewModels/Pages/Controls/PluginBarViewModel.cs @@ -147,12 +147,14 @@ internal void Launch(object _) { try { - string? loaderName = PluginDetail?.RequiredLoaderStruct.LoaderName; + var loaderName = PluginDetail?.RequiredLoaderStruct.LoaderName; var pd = PluginDetail?.PluginDetails; string pluginPath = $"{PluginDetail?.InstallPath}/{pd?.RootStartupFileName}"; string pluginFile = Path.GetFullPath(pluginPath); - string connectStr = $"{DevicesServer.DefaultDeviceInfoStruct.IPv4}" + - $":{GlobalInfo.PluginServerPort}"; + string connectStr = "" + + $"{DevicesDiscoveryServer.DefaultDeviceInfoStruct.IPv4}" + + $":" + + $"{GlobalInfo.PluginServerPort}"; if (PluginDetail != null && PluginDetail.RequiredLoaderStruct.SelfLoad) Process.Start(pluginFile, $"--connect {connectStr}"); else diff --git a/ViewModels/Pages/Controls/Settings_UpdateViewModel.cs b/ViewModels/Pages/Controls/Settings_UpdateViewModel.cs index 52044ef7..66af7b4b 100644 --- a/ViewModels/Pages/Controls/Settings_UpdateViewModel.cs +++ b/ViewModels/Pages/Controls/Settings_UpdateViewModel.cs @@ -310,7 +310,7 @@ HttpClient client string link = "https://" + Program.Config.Web.UpdateServer + Program.Config.Web.UpdatePath.Replace("%platform%", - DevicesServer.DefaultDeviceInfoStruct.DeviceOSType switch + DevicesDiscoveryServer.DefaultDeviceInfoStruct.DeviceOSType switch { OperatingSystems.Windows => "win", OperatingSystems.Linux => "linux", @@ -521,7 +521,7 @@ ref HttpClient client string downloadLinkBase = "https://" + Program.Config.Web.UpdateServer + Program.Config.Web.UpdateDownloadPath.Replace("%platform%", - DevicesServer.DefaultDeviceInfoStruct.DeviceOSType switch + DevicesDiscoveryServer.DefaultDeviceInfoStruct.DeviceOSType switch { OperatingSystems.Windows => "win", OperatingSystems.Linux => "linux", From 981807dcaee1d7503ec013b47765d9e588e984de Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Thu, 20 Apr 2023 15:11:17 +0800 Subject: [PATCH 05/16] =?UTF-8?q?=F0=9F=92=BE=20Feat(DevicesDiscoveryServe?= =?UTF-8?q?r):=20Added=20status=20support.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Servers/DevicesDiscoveryServer.cs | 21 +++++++++++++++++++++ Servers/Status.cs | 20 ++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 Servers/Status.cs diff --git a/Servers/DevicesDiscoveryServer.cs b/Servers/DevicesDiscoveryServer.cs index 78ad691f..47947663 100644 --- a/Servers/DevicesDiscoveryServer.cs +++ b/Servers/DevicesDiscoveryServer.cs @@ -34,10 +34,21 @@ internal class DevicesDiscoveryServer : IKitXServer private static readonly List SupportedNetworkInterfacesIndexes = new(); + private static ServerStatus status = ServerStatus.Unknown; + internal static readonly Queue Messages2BroadCast = new(); internal static DeviceInfoStruct DefaultDeviceInfoStruct = NetworkHelper.GetDeviceInfo(); + internal static ServerStatus Status + { + get => status; + set + { + status = value; + } + } + /// /// 寻找受支持的网络适配器并把UDP客户端加入组播 /// @@ -252,6 +263,8 @@ private void MultiDevicesBroadCastReceive() catch (Exception e) { Log.Error(e, $"In {location}: {e.Message}"); + + Status = ServerStatus.Errored; } await Stop(); @@ -271,6 +284,8 @@ private static void Init() public async Task Start() { + Status = ServerStatus.Starting; + Init(); UdpSender = new(Program.Config.Web.UDPPortSend, AddressFamily.InterNetwork) @@ -312,11 +327,15 @@ await TasksManager.RunTaskAsync( nameof(MultiDevicesBroadCastReceive) ); // 开始接收组播报文 + Status = ServerStatus.Running; + return this; } public async Task Stop() { + Status = ServerStatus.Stopping; + await Task.Run(() => { CloseDevicesDiscoveryServerRequest = true; @@ -329,6 +348,8 @@ await Task.Run(() => UdpReceiver?.Close(); }); + Status = ServerStatus.Pending; + return this; } diff --git a/Servers/Status.cs b/Servers/Status.cs new file mode 100644 index 00000000..70b56e65 --- /dev/null +++ b/Servers/Status.cs @@ -0,0 +1,20 @@ +namespace KitX_Dashboard.Servers; + +internal enum NetworkStatus +{ + Unknown = 0, + Connecting = 1, + Connected = 2, + Disconnected = 3, + Errored = 4, +} + +internal enum ServerStatus +{ + Unknown = 0, + Starting = 1, + Running = 2, + Stopping = 3, + Pending = 4, + Errored = 5, +} From d249c623f02e38a972cb98cd58dfc4c3b6588203 Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Thu, 20 Apr 2023 15:41:45 +0800 Subject: [PATCH 06/16] =?UTF-8?q?=F0=9F=A7=A9=20Refactor(Network):=20Bette?= =?UTF-8?q?r=20`WebManager`=20usage.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Helper.cs | 4 +- Managers/WebManager.cs | 77 +++++++++++++++++++------ Servers/DevicesDiscoveryServer.cs | 2 +- ViewModels/Pages/DevicePageViewModel.cs | 11 +++- 4 files changed, 70 insertions(+), 24 deletions(-) diff --git a/Helper.cs b/Helper.cs index 27e03a9f..8c383abb 100644 --- a/Helper.cs +++ b/Helper.cs @@ -105,10 +105,10 @@ public static void StartUpCheck() Program.TasksManager.SignalRun(nameof(SignalsNames.MainWindowInitSignal), () => { - new Thread(() => + new Thread(async () => { Thread.Sleep(Program.Config.Web.DelayStartSeconds * 1000); - Program.WebManager = new WebManager().Start(); + Program.WebManager = await new WebManager().Start(); }).Start(); }); diff --git a/Managers/WebManager.cs b/Managers/WebManager.cs index 0242f5bb..e4cf4f6f 100644 --- a/Managers/WebManager.cs +++ b/Managers/WebManager.cs @@ -2,7 +2,6 @@ using Serilog; using System; using System.Collections.ObjectModel; -using System.Threading; using System.Threading.Tasks; namespace KitX_Dashboard.Managers; @@ -16,7 +15,6 @@ public WebManager() internal PluginsServer? pluginsServer; internal DevicesServer? devicesServer; - internal DevicesDiscoveryServer? devicesDiscoveryServer; internal ObservableCollection? NetworkInterfaceRegistered; @@ -24,18 +22,30 @@ public WebManager() /// /// 开始执行网络相关服务 /// + /// 是否启动全部 /// 是否启动插件服务器 /// 是否启动设备服务器 + /// 是否启动设备自发现服务器 /// 网络管理器本身 - public WebManager Start(bool startPluginsServer = true, bool startDevicesServer = true) + public async Task Start + ( + bool startAll = true, + + bool startPluginsServer = false, + bool startDevicesServer = false, + bool startDevicesDiscoveryServer = false + ) { - new Thread(async () => + var location = $"{nameof(WebManager)}.{nameof(Start)}"; + + await TasksManager.RunTaskAsync(async () => { try { - Log.Information("WebManager: Starting..."); + if (startAll || startDevicesDiscoveryServer) + devicesDiscoveryServer = await new DevicesDiscoveryServer().Start(); - if (startDevicesServer) + if (startAll || startDevicesServer) { DevicesManager.InitEvents(); DevicesManager.KeepCheckAndRemove(); @@ -45,7 +55,7 @@ public WebManager Start(bool startPluginsServer = true, bool startDevicesServer devicesServer.Start(); } - if (startPluginsServer) + if (startAll || startPluginsServer) { PluginsManager.KeepCheckAndRemove(); PluginsManager.KeepCheckAndRemoveOrDelete(); @@ -53,14 +63,15 @@ public WebManager Start(bool startPluginsServer = true, bool startDevicesServer pluginsServer = new(); pluginsServer.Start(); } - - devicesDiscoveryServer = await new DevicesDiscoveryServer().Start(); } catch (Exception ex) { - Log.Error(ex, "In WebManager Start"); + Log.Error(ex, $"In {location}: " + + $"{nameof(startPluginsServer)}: {startPluginsServer}," + + $"{nameof(startDevicesServer)}: {startDevicesServer}," + + $"{nameof(startDevicesDiscoveryServer)}: {startDevicesDiscoveryServer}"); } - }).Start(); + }, "WebManager.Start"); return this; } @@ -71,21 +82,29 @@ public WebManager Start(bool startPluginsServer = true, bool startDevicesServer /// 是否停止插件服务器 /// 是否停止设备服务器 /// 网络管理器本身 - public WebManager Stop(bool stopPluginsServer = true, bool stopDevicesServer = true) + public WebManager Stop + ( + bool stopAll = true, + + bool stopPluginsServer = true, + bool stopDevicesServer = true, + bool stopDevicesDiscoveryServer = true + ) { - if (stopPluginsServer) + if (stopAll || stopPluginsServer) { pluginsServer?.Stop(); pluginsServer?.Dispose(); } - if (stopDevicesServer) + if (stopAll || stopDevicesServer) { devicesServer?.Stop(); devicesServer?.Dispose(); } - devicesDiscoveryServer?.Stop(); + if (stopAll || stopDevicesDiscoveryServer) + devicesDiscoveryServer?.Stop(); return this; } @@ -97,10 +116,23 @@ public WebManager Stop(bool stopPluginsServer = true, bool stopDevicesServer = t /// 是否重启设备服务器 /// 重新启动前要执行的操作 /// 网络管理器本身 - public WebManager Restart(bool restartPluginsServer = true, bool restartDevicesServer = true, - Action? actionBeforeStarting = null) + public WebManager Restart + ( + bool restartAll = true, + + bool restartPluginsServer = false, + bool restartDevicesServer = false, + bool restartDevicesDiscoveryServer = false, + + Action? actionBeforeStarting = null + ) { - Stop(stopPluginsServer: restartPluginsServer, stopDevicesServer: restartDevicesServer); + Stop( + restartAll, + restartPluginsServer, + restartDevicesServer, + restartDevicesDiscoveryServer + ); Task.Run(async () => { @@ -108,7 +140,12 @@ public WebManager Restart(bool restartPluginsServer = true, bool restartDevicesS actionBeforeStarting?.Invoke(); - Start(startPluginsServer: restartPluginsServer, startDevicesServer: restartDevicesServer); + await Start( + restartAll, + restartPluginsServer, + restartDevicesServer, + restartDevicesDiscoveryServer + ); }); return this; } @@ -120,6 +157,8 @@ public void Dispose() { pluginsServer?.Dispose(); devicesServer?.Dispose(); + devicesDiscoveryServer?.Dispose(); + GC.SuppressFinalize(this); } } diff --git a/Servers/DevicesDiscoveryServer.cs b/Servers/DevicesDiscoveryServer.cs index 47947663..a39482eb 100644 --- a/Servers/DevicesDiscoveryServer.cs +++ b/Servers/DevicesDiscoveryServer.cs @@ -50,7 +50,7 @@ internal static ServerStatus Status } /// - /// 寻找受支持的网络适配器并把UDP客户端加入组播 + /// 寻找受支持的网络适配器并把 UDP 客户端加入组播 /// private static void FindSupportNetworkInterfaces(List clients, IPAddress multicastAddress) { diff --git a/ViewModels/Pages/DevicePageViewModel.cs b/ViewModels/Pages/DevicePageViewModel.cs index 2b8ba55b..d4f1627a 100644 --- a/ViewModels/Pages/DevicePageViewModel.cs +++ b/ViewModels/Pages/DevicePageViewModel.cs @@ -55,18 +55,25 @@ internal double NoDevice_TipHeight internal static void RestartDevicvesServer(object _) { Program.WebManager?.Restart( - restartPluginsServer: false, + restartAll: false, + restartDevicesServer: false, + restartDevicesDiscoveryServer: true, actionBeforeStarting: () => DeviceCards.Clear() ); } internal static void StopDevicvesServer(object _) { - Program.WebManager?.Stop(stopPluginsServer: false); + Program.WebManager?.Stop( + stopAll: false, + stopDevicesServer: true, + stopDevicesDiscoveryServer: true + ); Task.Run(async () => { await Task.Delay(Program.Config.Web.UDPSendFrequency + 200); + DeviceCards.Clear(); }); } From ad1790b199726f594df6d9bbb841a7818c83c4f1 Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Sat, 22 Apr 2023 18:08:07 +0800 Subject: [PATCH 07/16] =?UTF-8?q?=F0=9F=92=BE=20Feat(Interfaces):=20Requir?= =?UTF-8?q?e=20`OnReceive`=20function.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Interfaces/Network/IKitXServer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Interfaces/Network/IKitXServer.cs b/Interfaces/Network/IKitXServer.cs index 9532748e..fae08ad7 100644 --- a/Interfaces/Network/IKitXServer.cs +++ b/Interfaces/Network/IKitXServer.cs @@ -11,4 +11,6 @@ internal interface IKitXServer : IModuleController Task BroadCast(byte[] content, Func? pattern); Task Send(byte[] content, string target); + + Task OnReceive(Action action); } From a0df74df4f0a77c4c9f276b774f4cf71caadba37 Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Sat, 22 Apr 2023 18:08:55 +0800 Subject: [PATCH 08/16] =?UTF-8?q?=F0=9F=92=BE=20Feat(Interfaces):=20`IKitX?= =?UTF-8?q?Client`=20requires=20`Send`=20and=20`OnReceive`=20functions.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Interfaces/Network/IKitXClient.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Interfaces/Network/IKitXClient.cs b/Interfaces/Network/IKitXClient.cs index f3c565c2..7a620b45 100644 --- a/Interfaces/Network/IKitXClient.cs +++ b/Interfaces/Network/IKitXClient.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; namespace KitX_Dashboard.Interfaces.Network; @@ -7,4 +8,8 @@ internal interface IKitXClient : IModuleController Task Connect(); Task Disconnect(); + + Task Send(byte[] content); + + Task OnReceive(Action action); } From 88e307797805f23851395b628c0077f1d13b9d48 Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Sat, 22 Apr 2023 18:10:24 +0800 Subject: [PATCH 09/16] =?UTF-8?q?=F0=9F=A7=A9=20=F0=9F=93=A6=20Refactor,?= =?UTF-8?q?=20Struct:=20Rename=20`Server`=20folder=20to=20`Network`=20fold?= =?UTF-8?q?er.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Managers/DevicesManager.cs | 2 +- Managers/WebManager.cs | 5 +- .../DevicesDiscoveryServer.cs | 25 +++- {Servers => Network}/DevicesServer.cs | 115 +++++++++++------- {Servers => Network}/NetworkHelper.cs | 2 +- {Servers => Network}/PluginsServer.cs | 2 +- {Servers => Network}/Status.cs | 9 +- Services/DebugService.cs | 2 +- .../Pages/Controls/PluginBarViewModel.cs | 2 +- .../Controls/Settings_UpdateViewModel.cs | 2 +- 10 files changed, 113 insertions(+), 53 deletions(-) rename {Servers => Network}/DevicesDiscoveryServer.cs (94%) rename {Servers => Network}/DevicesServer.cs (85%) rename {Servers => Network}/NetworkHelper.cs (99%) rename {Servers => Network}/PluginsServer.cs (99%) rename {Servers => Network}/Status.cs (69%) diff --git a/Managers/DevicesManager.cs b/Managers/DevicesManager.cs index 252a08e4..1963247d 100644 --- a/Managers/DevicesManager.cs +++ b/Managers/DevicesManager.cs @@ -1,7 +1,7 @@ using Avalonia.Threading; using KitX.Web.Rules; using KitX_Dashboard.Data; -using KitX_Dashboard.Servers; +using KitX_Dashboard.Network; using KitX_Dashboard.Services; using KitX_Dashboard.Views.Pages.Controls; using Serilog; diff --git a/Managers/WebManager.cs b/Managers/WebManager.cs index e4cf4f6f..a0fd0ee2 100644 --- a/Managers/WebManager.cs +++ b/Managers/WebManager.cs @@ -1,4 +1,4 @@ -using KitX_Dashboard.Servers; +using KitX_Dashboard.Network; using Serilog; using System; using System.Collections.ObjectModel; @@ -51,8 +51,7 @@ await TasksManager.RunTaskAsync(async () => DevicesManager.KeepCheckAndRemove(); DevicesManager.Watch4MainDevice(); - devicesServer = new(); - devicesServer.Start(); + devicesServer = await new DevicesServer().Start(); } if (startAll || startPluginsServer) diff --git a/Servers/DevicesDiscoveryServer.cs b/Network/DevicesDiscoveryServer.cs similarity index 94% rename from Servers/DevicesDiscoveryServer.cs rename to Network/DevicesDiscoveryServer.cs index a39482eb..e6044542 100644 --- a/Servers/DevicesDiscoveryServer.cs +++ b/Network/DevicesDiscoveryServer.cs @@ -16,8 +16,11 @@ using System.Threading; using System.Threading.Tasks; -namespace KitX_Dashboard.Servers; +namespace KitX_Dashboard.Network; +/// +/// 设备自发现网络服务器 +/// internal class DevicesDiscoveryServer : IKitXServer { private static UdpClient? UdpSender = null; @@ -36,6 +39,8 @@ internal class DevicesDiscoveryServer : IKitXServer private static ServerStatus status = ServerStatus.Unknown; + private static Action? onReceive = null; + internal static readonly Queue Messages2BroadCast = new(); internal static DeviceInfoStruct DefaultDeviceInfoStruct = NetworkHelper.GetDeviceInfo(); @@ -242,9 +247,13 @@ private void MultiDevicesBroadCastReceive() while (GlobalInfo.Running && !CloseDevicesDiscoveryServerRequest) { var bytes = UdpReceiver?.Receive(ref multicast); + var client = $"{multicast.Address}:{multicast.Port}"; + if (bytes is null) continue; // null byte[] cause exception in next line. + + onReceive?.Invoke(bytes, client); + var result = Encoding.UTF8.GetString(bytes); - var client = $"{multicast.Address}:{multicast.Port}"; Log.Information($"UDP From: {client,-21}, Receive: {result}"); @@ -385,6 +394,18 @@ public async Task BroadCast(byte[] content, Func + /// 设定当接收到数据时的处理代码 + /// + /// 处理代码, 参数一为接收到的数据 (byte[]), 参数二是数据发送者, ip:port + /// 设备自发现网络服务器本身 + public async Task OnReceive(Action action) + { + await Task.Run(() => onReceive = action); + + return this; + } + public void Dispose() { UdpSender?.Dispose(); diff --git a/Servers/DevicesServer.cs b/Network/DevicesServer.cs similarity index 85% rename from Servers/DevicesServer.cs rename to Network/DevicesServer.cs index 99fe101b..7cc606d5 100644 --- a/Servers/DevicesServer.cs +++ b/Network/DevicesServer.cs @@ -1,4 +1,5 @@ using KitX_Dashboard.Data; +using KitX_Dashboard.Interfaces.Network; using KitX_Dashboard.Managers; using KitX_Dashboard.Services; using Serilog; @@ -8,50 +9,13 @@ using System.Net.Sockets; using System.Text; using System.Threading; +using System.Threading.Tasks; -namespace KitX_Dashboard.Servers; +namespace KitX_Dashboard.Network; -internal class DevicesServer : IDisposable +internal class DevicesServer : IKitXServer { - - public DevicesServer() - { - - } - - public void Start() - { - new Thread(() => - { - try - { - Log.Information($"Start Init {nameof(DevicesHost)}"); - // 初始化自组网 - DevicesHost = new(); - } - catch (Exception ex) - { - Log.Error(ex, $"In {nameof(DevicesServer)}, " + - $"Init {nameof(DevicesHost)}"); - } - }).Start(); - } - - public void Stop() - { - keepListen = false; - - foreach (KeyValuePair item in clients) - { - item.Value.Close(); - item.Value.Dispose(); - } - - acceptDeviceThread?.Join(); - - DevicesHost?.Close(); - DevicesHost?.Dispose(); - } + public NetworkType Type { get; set; } = NetworkType.Unknown; internal Thread? acceptDeviceThread; internal Thread? receiveMessageThread; @@ -363,4 +327,73 @@ public void Dispose() GC.SuppressFinalize(this); } + public async Task Broadcast(byte[] content) + { + await Task.Run(() => { }); + + return this; + } + + public async Task BroadCast(byte[] content, Func? pattern) + { + await Task.Run(() => { }); + + return this; + } + + public async Task Send(byte[] content, string target) + { + await Task.Run(() => { }); + + return this; + } + + public async Task OnReceive(Action action) + { + await Task.Run(() => { }); + + return this; + } + + public async Task Start() + { + await TasksManager.RunTaskAsync(() => + { + DevicesHost = new(); + }, $"{nameof(DevicesServer)}.{nameof(Start)}"); + + return this; + } + + public async Task Stop() + { + await Task.Run(() => + { + keepListen = false; + + foreach (KeyValuePair item in clients) + { + item.Value.Close(); + item.Value.Dispose(); + } + + acceptDeviceThread?.Join(); + + DevicesHost?.Close(); + DevicesHost?.Dispose(); + }); + + return this; + } + + public async Task Restart() + { + await Task.Run(async () => + { + await Start(); + await Stop(); + }); + + return this; + } } diff --git a/Servers/NetworkHelper.cs b/Network/NetworkHelper.cs similarity index 99% rename from Servers/NetworkHelper.cs rename to Network/NetworkHelper.cs index 3741e398..ef463c55 100644 --- a/Servers/NetworkHelper.cs +++ b/Network/NetworkHelper.cs @@ -11,7 +11,7 @@ using System.Net.NetworkInformation; using System.Net.Sockets; -namespace KitX_Dashboard.Servers; +namespace KitX_Dashboard.Network; internal static class NetworkHelper { diff --git a/Servers/PluginsServer.cs b/Network/PluginsServer.cs similarity index 99% rename from Servers/PluginsServer.cs rename to Network/PluginsServer.cs index e43d3f3e..fccbff80 100644 --- a/Servers/PluginsServer.cs +++ b/Network/PluginsServer.cs @@ -14,7 +14,7 @@ #pragma warning disable CS8602 // 解引用可能出现空引用。 #pragma warning disable CS8604 // 引用类型参数可能为 null。 -namespace KitX_Dashboard.Servers; +namespace KitX_Dashboard.Network; internal class PluginsServer : IDisposable { diff --git a/Servers/Status.cs b/Network/Status.cs similarity index 69% rename from Servers/Status.cs rename to Network/Status.cs index 70b56e65..3e923a9c 100644 --- a/Servers/Status.cs +++ b/Network/Status.cs @@ -1,4 +1,4 @@ -namespace KitX_Dashboard.Servers; +namespace KitX_Dashboard.Network; internal enum NetworkStatus { @@ -18,3 +18,10 @@ internal enum ServerStatus Pending = 4, Errored = 5, } + +internal enum NetworkType +{ + Unknown = 0, + Server = 1, + Client = 2, +} diff --git a/Services/DebugService.cs b/Services/DebugService.cs index 71a87b56..495d89cf 100644 --- a/Services/DebugService.cs +++ b/Services/DebugService.cs @@ -1,4 +1,4 @@ -using KitX_Dashboard.Servers; +using KitX_Dashboard.Network; using Serilog; using System; using System.Collections.Generic; diff --git a/ViewModels/Pages/Controls/PluginBarViewModel.cs b/ViewModels/Pages/Controls/PluginBarViewModel.cs index adf18b40..8483656e 100644 --- a/ViewModels/Pages/Controls/PluginBarViewModel.cs +++ b/ViewModels/Pages/Controls/PluginBarViewModel.cs @@ -4,7 +4,7 @@ using KitX_Dashboard.Data; using KitX_Dashboard.Managers; using KitX_Dashboard.Models; -using KitX_Dashboard.Servers; +using KitX_Dashboard.Network; using KitX_Dashboard.Services; using KitX_Dashboard.Views; using KitX_Dashboard.Views.Pages.Controls; diff --git a/ViewModels/Pages/Controls/Settings_UpdateViewModel.cs b/ViewModels/Pages/Controls/Settings_UpdateViewModel.cs index 66af7b4b..8d125921 100644 --- a/ViewModels/Pages/Controls/Settings_UpdateViewModel.cs +++ b/ViewModels/Pages/Controls/Settings_UpdateViewModel.cs @@ -6,7 +6,7 @@ using KitX_Dashboard.Commands; using KitX_Dashboard.Converters; using KitX_Dashboard.Data; -using KitX_Dashboard.Servers; +using KitX_Dashboard.Network; using KitX_Dashboard.Services; using MessageBox.Avalonia; using MessageBox.Avalonia.Enums; From 6586addde5c70a03d61d568b76ba97cebecbaf80 Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Sat, 22 Apr 2023 18:10:52 +0800 Subject: [PATCH 10/16] =?UTF-8?q?=F0=9F=92=BE=20Feat(Network):=20Init=20`D?= =?UTF-8?q?evicesClient.cs`=20file.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Network/DevicesClient.cs | 67 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 Network/DevicesClient.cs diff --git a/Network/DevicesClient.cs b/Network/DevicesClient.cs new file mode 100644 index 00000000..0d63ba0c --- /dev/null +++ b/Network/DevicesClient.cs @@ -0,0 +1,67 @@ +using KitX_Dashboard.Interfaces.Network; +using System; +using System.Threading.Tasks; + +namespace KitX_Dashboard.Network; + +internal class DevicesClient : IKitXClient +{ + public async Task Connect() + { + await Task.Run(() => { }); + + return this; + } + + public async Task Disconnect() + { + await Task.Run(() => { }); + + return this; + } + + public async Task OnReceive(Action action) + { + await Task.Run(() => { }); + + return this; + } + + public async Task Send(byte[] content) + { + await Task.Run(() => { }); + + return this; + } + + public async Task Start() + { + await Task.Run(() => { }); + + return this; + } + + public async Task Stop() + { + await Task.Run(() => { }); + + return this; + } + + public async Task Restart() + { + await Task.Run(async () => + { + await Start(); + + await Stop(); + }); + + return this; + } + + public void Dispose() + { + + } +} From 648dce20b35334c5f86152799f880fb30de4ef2b Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Sun, 23 Apr 2023 00:01:52 +0800 Subject: [PATCH 11/16] =?UTF-8?q?=F0=9F=92=BE=20Feat(Interfaces.Network):?= =?UTF-8?q?=20`OnReceive`=20callback=20supports=20length,=20and=20run=20sy?= =?UTF-8?q?nchronizly.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Interfaces/Network/IKitXClient.cs | 2 +- Interfaces/Network/IKitXServer.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Interfaces/Network/IKitXClient.cs b/Interfaces/Network/IKitXClient.cs index 7a620b45..86a6f658 100644 --- a/Interfaces/Network/IKitXClient.cs +++ b/Interfaces/Network/IKitXClient.cs @@ -11,5 +11,5 @@ internal interface IKitXClient : IModuleController Task Send(byte[] content); - Task OnReceive(Action action); + T OnReceive(Action action); } diff --git a/Interfaces/Network/IKitXServer.cs b/Interfaces/Network/IKitXServer.cs index fae08ad7..4a432be0 100644 --- a/Interfaces/Network/IKitXServer.cs +++ b/Interfaces/Network/IKitXServer.cs @@ -12,5 +12,5 @@ internal interface IKitXServer : IModuleController Task Send(byte[] content, string target); - Task OnReceive(Action action); + T OnReceive(Action action); } From 1580bd7113d6391bd1b269fba5feda3394870407 Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Sun, 23 Apr 2023 00:03:36 +0800 Subject: [PATCH 12/16] =?UTF-8?q?=F0=9F=A7=A9=20Refactor(Network):=20Adapt?= =?UTF-8?q?=20to=20updated=20interfaces.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Network/DevicesClient.cs | 4 +--- Network/DevicesDiscoveryServer.cs | 11 +++++++---- Network/DevicesServer.cs | 4 +--- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Network/DevicesClient.cs b/Network/DevicesClient.cs index 0d63ba0c..80c40c5b 100644 --- a/Network/DevicesClient.cs +++ b/Network/DevicesClient.cs @@ -20,10 +20,8 @@ public async Task Disconnect() return this; } - public async Task OnReceive(Action action) + public DevicesClient OnReceive(Action action) { - await Task.Run(() => { }); - return this; } diff --git a/Network/DevicesDiscoveryServer.cs b/Network/DevicesDiscoveryServer.cs index e6044542..25938f0d 100644 --- a/Network/DevicesDiscoveryServer.cs +++ b/Network/DevicesDiscoveryServer.cs @@ -39,7 +39,7 @@ internal class DevicesDiscoveryServer : IKitXServer private static ServerStatus status = ServerStatus.Unknown; - private static Action? onReceive = null; + private static Action? onReceive = null; internal static readonly Queue Messages2BroadCast = new(); @@ -171,6 +171,7 @@ private void MultiDevicesBroadCastSend() UdpSendTimer.Elapsed += async (_, _) => { --erroredInterfacesIndexesTTL; + if (erroredInterfacesIndexesTTL <= 0) { erroredInterfacesIndexesTTL = 60; @@ -187,6 +188,8 @@ private void MultiDevicesBroadCastSend() foreach (var item in SupportedNetworkInterfacesIndexes) { + if (!GlobalInfo.Running || CloseDevicesDiscoveryServerRequest) break; + // 如果错误网络适配器中存在当前项的记录, 跳过 if (erroredInterfacesIndexes.Contains(item)) continue; @@ -251,7 +254,7 @@ private void MultiDevicesBroadCastReceive() if (bytes is null) continue; // null byte[] cause exception in next line. - onReceive?.Invoke(bytes, client); + onReceive?.Invoke(bytes, null, client); var result = Encoding.UTF8.GetString(bytes); @@ -399,9 +402,9 @@ public async Task BroadCast(byte[] content, Func /// 处理代码, 参数一为接收到的数据 (byte[]), 参数二是数据发送者, ip:port /// 设备自发现网络服务器本身 - public async Task OnReceive(Action action) + public DevicesDiscoveryServer OnReceive(Action action) { - await Task.Run(() => onReceive = action); + onReceive = action; return this; } diff --git a/Network/DevicesServer.cs b/Network/DevicesServer.cs index 7cc606d5..97a80651 100644 --- a/Network/DevicesServer.cs +++ b/Network/DevicesServer.cs @@ -348,10 +348,8 @@ public async Task Send(byte[] content, string target) return this; } - public async Task OnReceive(Action action) + public DevicesServer OnReceive(Action action) { - await Task.Run(() => { }); - return this; } From 5dc99b928eb63a227cf59feeb87e875f012c0fbf Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Sun, 23 Apr 2023 00:05:12 +0800 Subject: [PATCH 13/16] =?UTF-8?q?=F0=9F=A7=A9=20Refactor(Network):=20Made?= =?UTF-8?q?=20`PluginsServer`=20adapted=20to=20new=20interface.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Managers/WebManager.cs | 22 +-- Network/PluginsServer.cs | 283 ++++++++++++++++++++++----------------- 2 files changed, 168 insertions(+), 137 deletions(-) diff --git a/Managers/WebManager.cs b/Managers/WebManager.cs index a0fd0ee2..5718b5d5 100644 --- a/Managers/WebManager.cs +++ b/Managers/WebManager.cs @@ -59,8 +59,7 @@ await TasksManager.RunTaskAsync(async () => PluginsManager.KeepCheckAndRemove(); PluginsManager.KeepCheckAndRemoveOrDelete(); - pluginsServer = new(); - pluginsServer.Start(); + pluginsServer = await new PluginsServer().Start(); } } catch (Exception ex) @@ -91,19 +90,19 @@ public WebManager Stop ) { if (stopAll || stopPluginsServer) - { - pluginsServer?.Stop(); - pluginsServer?.Dispose(); - } + pluginsServer?.Stop().ContinueWith( + server => server.Dispose() + ); if (stopAll || stopDevicesServer) - { - devicesServer?.Stop(); - devicesServer?.Dispose(); - } + devicesServer?.Stop().ContinueWith( + server => server.Dispose() + ); if (stopAll || stopDevicesDiscoveryServer) - devicesDiscoveryServer?.Stop(); + devicesDiscoveryServer?.Stop().ContinueWith( + server => server.Dispose() + ); return this; } @@ -146,6 +145,7 @@ await Start( restartDevicesDiscoveryServer ); }); + return this; } diff --git a/Network/PluginsServer.cs b/Network/PluginsServer.cs index fccbff80..a208c56a 100644 --- a/Network/PluginsServer.cs +++ b/Network/PluginsServer.cs @@ -1,110 +1,63 @@ -using KitX_Dashboard.Data; +using Common.BasicHelper.Utils.Extensions; +using KitX_Dashboard.Data; +using KitX_Dashboard.Interfaces.Network; using KitX_Dashboard.Managers; using KitX_Dashboard.Services; using Serilog; using System; using System.Collections.Generic; -using System.IO; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; +using System.Threading.Tasks; -#pragma warning disable CS8600 // 将 null 字面量或可能为 null 的值转换为非 null 类型。 #pragma warning disable CS8602 // 解引用可能出现空引用。 #pragma warning disable CS8604 // 引用类型参数可能为 null。 namespace KitX_Dashboard.Network; -internal class PluginsServer : IDisposable +internal class PluginsServer : IKitXServer { - public PluginsServer() - { - var port = Program.Config.Web.UserSpecifiedPluginsServerPort; - if (port < 0 || port >= 65536) port = null; - listener = new(IPAddress.Any, port ?? 0); - acceptPluginThread = new(AcceptClient); - } - - #region TCP Socket 服务于 Loaders 的服务器 - - /// - /// 开始执行 - /// - public void Start() - { - listener.Start(); - - int port = ((IPEndPoint)listener.LocalEndpoint).Port; // 取服务端口号 - GlobalInfo.PluginServerPort = port; // 全局端口号标明 - EventService.Invoke(nameof(EventService.PluginsServerPortChanged)); - - Log.Information($"PluginsServer Port: {port}"); - - acceptPluginThread.Start(); - } + private static TcpListener? listener = null; - /// - /// 停止进程 - /// - public void Stop() - { - keepListen = false; + private static bool keepListen = true; - foreach (KeyValuePair item in clients) - { - item.Value.Close(); - item.Value.Dispose(); - } + private static readonly Dictionary clients = new(); - acceptPluginThread.Join(); - } - - public Thread acceptPluginThread; - public TcpListener listener; - public bool keepListen = true; - - public readonly Dictionary clients = new(); + private static Action? onReceive = null; /// /// 接收客户端 /// - private void AcceptClient() + private static void AcceptClient() { + var location = $"{nameof(PluginsServer)}.{nameof(AcceptClient)}"; + try { while (keepListen) { if (listener.Pending()) { - TcpClient client = listener.AcceptTcpClient(); - IPEndPoint endpoint = client.Client.RemoteEndPoint as IPEndPoint; + var client = listener.AcceptTcpClient(); + var endpoint = client.Client.RemoteEndPoint as IPEndPoint; + clients.Add(endpoint.ToString(), client); Log.Information($"New plugin connection: {endpoint}"); - // 新建并运行接收消息线程 - new Thread(() => - { - try - { - ReciveMessage(client); - } - catch (Exception ex) - { - Log.Error(ex, "In WebManager.AcceptClient().ReceiveMessageFromHost()", ex); - } - }).Start(); - } - else - { - Thread.Sleep(100); + ReciveMessage(client); } + //else + //{ + // Thread.Sleep(100); + //} } } catch (Exception ex) { - Log.Error(ex, $"In AcceptClient() : {ex.Message}"); + Log.Error(ex, $"In {location}: {ex.Message}"); } } @@ -112,89 +65,167 @@ private void AcceptClient() /// 接收消息 /// /// TcpClient - private async void ReciveMessage(object obj) + private static void ReciveMessage(TcpClient client) { - TcpClient client = obj as TcpClient; - IPEndPoint endpoint = null; - NetworkStream stream = null; + var location = $"{nameof(PluginsServer)}.{nameof(ReciveMessage)}"; - try - { - endpoint = client.Client.RemoteEndPoint as IPEndPoint; - stream = client.GetStream(); + IPEndPoint? endpoint = null; + NetworkStream? stream = null; - while (keepListen) + new Thread(async () => + { + try { - byte[] data = new byte[Program.Config.Web.SocketBufferSize]; - //如果远程主机已关闭连接,Read将立即返回零字节 - //int length = await stream.ReadAsync(data, 0, data.Length); - int length = await stream.ReadAsync(data); - if (length > 0) + endpoint = client.Client.RemoteEndPoint as IPEndPoint; + stream = client.GetStream(); + + while (keepListen) { - string msg = Encoding.UTF8.GetString(data, 0, length); + var buffer = new byte[Program.Config.Web.SocketBufferSize]; - Log.Information($"From: {endpoint}\tReceive: {msg}"); + var length = await stream.ReadAsync(buffer); - if (false) + if (length > 0) { + onReceive?.Invoke(buffer, length, endpoint.ToString()); - } - else if (msg.StartsWith("PluginStruct: ")) - { - PluginsManager.Execute(msg[14..], endpoint); - string workPath = Path.GetFullPath(Program.Config.App.LocalPluginsDataFolder); - string sendtxt = $"WorkPath: {workPath}"; - byte[] bytes = Encoding.UTF8.GetBytes(sendtxt); - stream.Write(bytes, 0, bytes.Length); - } + var msg = Encoding.UTF8.GetString(buffer, 0, length); - //发送到其他客户端 - //foreach (KeyValuePair kvp in clients) - //{ - // if (kvp.Value != client) - // { - // byte[] writeData = Encoding.UTF8.GetBytes(msg); - // NetworkStream writeStream = kvp.Value.GetStream(); - // writeStream.Write(writeData, 0, writeData.Length); - // } - //} - } - else - { + Log.Information($"From: {endpoint}\tReceive: {msg}"); + + if (msg.StartsWith("PluginStruct: ")) + { + PluginsManager.Execute(msg[14..], endpoint); + + var workPath = Program.Config.App.LocalPluginsDataFolder.GetFullPath(); + var sendtxt = $"WorkPath: {workPath}"; + var bytes = Encoding.UTF8.GetBytes(sendtxt); + + stream?.Write(bytes, 0, bytes.Length); + } - break; //客户端断开连接 跳出循环 + //发送到其他客户端 + //foreach (KeyValuePair kvp in clients) + //{ + // if (kvp.Value != client) + // { + // byte[] writeData = Encoding.UTF8.GetBytes(msg); + // NetworkStream writeStream = kvp.Value.GetStream(); + // writeStream.Write(writeData, 0, writeData.Length); + // } + //} + } + else break; //客户端断开连接 跳出循环 } } - } - catch (Exception ex) + catch (Exception ex) + { + Log.Error(ex, $"In {location}: {ex.Message}"); + Log.Information($"Connection broke from: {endpoint}"); + } + finally + { + PluginsManager.Disconnect(endpoint); + + stream?.CloseAndDispose(); + + clients.Remove(endpoint.ToString()); + + client.Dispose(); + } + }).Start(); + } + + private static void Init() + { + var port = Program.Config.Web.UserSpecifiedPluginsServerPort; + + if (port < 0 || port > 65535) port = null; + + listener = new(IPAddress.Any, port ?? 0); + } + + public async Task Broadcast(byte[] content) + { + await Task.Run(() => { }); + + return this; + } + + public async Task BroadCast(byte[] content, Func? pattern) + { + await Task.Run(() => { }); + + return this; + } + + public async Task Send(byte[] content, string target) + { + await Task.Run(() => { }); + + return this; + } + + public PluginsServer OnReceive(Action action) + { + onReceive = action; + + return this; + } + + public async Task Start() + { + await TasksManager.RunTaskAsync(() => { - Log.Error(ex, $"Error: In ReceiveMessageFromHost() : {ex.Message}"); - Log.Information($"Connection broke from: {endpoint}"); + Init(); - //Read是阻塞方法 客户端退出是会引发异常 释放资源 结束此线程 - } - finally + listener.Start(); + + var port = ((IPEndPoint)listener.LocalEndpoint).Port; // 取服务端口号 + + GlobalInfo.PluginServerPort = port; // 全局端口号标明 + + EventService.Invoke(nameof(EventService.PluginsServerPortChanged)); + + Log.Information($"PluginsServer Port: {port}"); + + new Thread(AcceptClient).Start(); + }); + + return this; + } + + public async Task Stop() + { + keepListen = false; + + await Task.Run(() => { - //释放资源 - PluginsManager.Disconnect(endpoint); //注销插件 - stream.Close(); - stream.Dispose(); - clients.Remove(endpoint.ToString()); - client.Dispose(); - } + foreach (KeyValuePair item in clients) + { + item.Value.Close(); + item.Value.Dispose(); + } + }); + + return this; + } + + public async Task Restart() + { + await Task.Run(() => { }); + + return this; } - #endregion public void Dispose() { keepListen = false; listener.Stop(); - acceptPluginThread.Join(); + GC.SuppressFinalize(this); } - } #pragma warning restore CS8604 // 引用类型参数可能为 null。 #pragma warning restore CS8602 // 解引用可能出现空引用。 -#pragma warning restore CS8600 // 将 null 字面量或可能为 null 的值转换为非 null 类型。 From f3f4bfb1ae572d1bba691f15aff3e115be6680e2 Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Sun, 23 Apr 2023 00:06:25 +0800 Subject: [PATCH 14/16] =?UTF-8?q?=F0=9F=93=84=20Docs(Network):=20Docs=20fo?= =?UTF-8?q?r=20enums=20in=20`Status.cs`.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Network/Status.cs | 104 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 101 insertions(+), 3 deletions(-) diff --git a/Network/Status.cs b/Network/Status.cs index 3e923a9c..6d582130 100644 --- a/Network/Status.cs +++ b/Network/Status.cs @@ -1,27 +1,125 @@ namespace KitX_Dashboard.Network; +/// +/// 网络状态 +/// internal enum NetworkStatus { + /// + /// 未知状态 + /// Unknown = 0, + + /// + /// 正在连接 + /// Connecting = 1, + + /// + /// 已经连接 + /// Connected = 2, + + /// + /// 连接断开 + /// Disconnected = 3, + + /// + /// 发生错误 + /// Errored = 4, } +/// +/// 网络类型 +/// +internal enum NetworkType +{ + /// + /// 未知类型 + /// + Unknown = 0, + + /// + /// 服务器 + /// + Server = 1, + + /// + /// 客户端 + /// + Client = 2, +} + +/// +/// 服务器状态 +/// internal enum ServerStatus { + /// + /// 未知状态 + /// Unknown = 0, + + /// + /// 启动中 + /// Starting = 1, + + /// + /// 运行中 + /// Running = 2, + + /// + /// 停止中 + /// Stopping = 3, + + /// + /// 等待中 + /// Pending = 4, + + /// + /// 发生错误 + /// Errored = 5, } -internal enum NetworkType +/// +/// 客户端状态 +/// +internal enum ClientStatus { + /// + /// 未知状态 + /// Unknown = 0, - Server = 1, - Client = 2, + + /// + /// 连接中 + /// + Connecting = 1, + + /// + /// 运行中 + /// + Running = 2, + + /// + /// 断开连接中 + /// + Disconnecting = 3, + + /// + /// 等待中 + /// + Pending = 4, + + /// + /// 发生错误 + /// + Errored = 5, } From a68eceea248ec8fd3e2ad9af9ebfe53e3cdf82c7 Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Mon, 24 Apr 2023 00:41:32 +0800 Subject: [PATCH 15/16] =?UTF-8?q?=F0=9F=A7=A9=20Refactor(Network):=20Depar?= =?UTF-8?q?t=20client=20part=20in=20devices=20network=20to=20`DevicesClien?= =?UTF-8?q?t.cs`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Network/DevicesClient.cs | 201 +++++++++++++++++++- Network/DevicesServer.cs | 398 +++++++++++++-------------------------- 2 files changed, 318 insertions(+), 281 deletions(-) diff --git a/Network/DevicesClient.cs b/Network/DevicesClient.cs index 80c40c5b..8c1744a0 100644 --- a/Network/DevicesClient.cs +++ b/Network/DevicesClient.cs @@ -1,65 +1,246 @@ -using KitX_Dashboard.Interfaces.Network; +using Common.BasicHelper.Utils.Extensions; +using KitX_Dashboard.Interfaces.Network; +using KitX_Dashboard.Managers; +using Serilog; using System; +using System.Net.Sockets; +using System.Threading; using System.Threading.Tasks; namespace KitX_Dashboard.Network; internal class DevicesClient : IKitXClient { + private static TcpClient? client = null; + + private static bool keepReceiving = false; + + private static ClientStatus status = ClientStatus.Unknown; + + public static string ServerAddress { get; set; } = string.Empty; + + public static int ServerPort { get; set; } = 0; + + public static ClientStatus Status + { + get => status; + set + { + status = value; + + if (status == ClientStatus.Errored) + DevicesManager.Watch4MainDevice(); + } + } + + public static Action? onReceive = null; + + public DevicesClient SetServerAddress(string address) + { + ServerAddress = address; + + return this; + } + + public DevicesClient SetServerPort(int port) + { + if (port <= 0 || port >= 65536) + throw new ArgumentOutOfRangeException(nameof(port), "0 < port < 65536"); + + ServerPort = port; + + return this; + } + + public async void Receive() + { + var location = $"{nameof(DevicesClient)}.{nameof(Receive)}"; + + if (client is null) return; + + var stream = client?.GetStream(); + + if (stream is null) return; + + var buffer = new byte[Program.Config.Web.SocketBufferSize]; // Default 10 MB buffer + + try + { + while (keepReceiving) + { + if (buffer is null) break; + + var length = await stream.ReadAsync(buffer); + + if (length > 0) + { + var msg = buffer.ToUTF8(0, length); + + Log.Information($"Receive from Host: {msg}"); + } + else + { + keepReceiving = false; + break; + } + } + } + catch (Exception e) + { + Log.Error(e, $"In {location}: {e.Message}"); + + Status = ClientStatus.Errored; + } + + stream.CloseAndDispose(); + + client?.CloseAndDispose(); + } + public async Task Connect() { - await Task.Run(() => { }); + var location = $"{nameof(DevicesClient)}.{nameof(Connect)}"; + + if (client is null) return this; + + Status = ClientStatus.Connecting; + + await TasksManager.RunTaskAsync(async () => + { + try + { + await client.ConnectAsync(ServerAddress, ServerPort); + + new Thread(Receive).Start(); + } + catch (Exception ex) + { + Log.Error(ex, $"In {location}: {ex.Message}"); + + await Stop(); + + Status = ClientStatus.Errored; + } + + }, location); + + Status = ClientStatus.Running; return this; } public async Task Disconnect() { - await Task.Run(() => { }); + var location = $"{nameof(DevicesClient)}.{nameof(Disconnect)}"; + + Status = ClientStatus.Disconnecting; + + await TasksManager.RunTaskAsync(() => + { + keepReceiving = false; + + client?.Close(); + + }, location); + + Status = ClientStatus.Pending; return this; } public DevicesClient OnReceive(Action action) { + onReceive = action; + return this; } public async Task Send(byte[] content) { - await Task.Run(() => { }); + var location = $"{nameof(DevicesClient)}.{nameof(Send)}"; + + await TasksManager.RunTaskAsync(async () => + { + try + { + var stream = client?.GetStream(); + + if (stream is null) return; + + stream.Write(content, 0, content.Length); + stream.Flush(); + + Log.Information($"Sent Message to Host, msg: {content.ToUTF8()}"); + } + catch (Exception e) + { + Log.Error(e, $"In {location}: {e.Message}"); + + await Stop(); + + Status = ClientStatus.Errored; + } + }, location); return this; } + private static void Init() + { + keepReceiving = true; + + client = new(); + } + public async Task Start() { - await Task.Run(() => { }); + var location = $"{nameof(DevicesClient)}.{nameof(Start)}"; + + await TasksManager.RunTaskAsync(async () => + { + Status = ClientStatus.Pending; + + Init(); + + await Connect(); + + }, location); return this; } public async Task Stop() { - await Task.Run(() => { }); + var location = $"{nameof(DevicesClient)}.{nameof(Stop)}"; + + await TasksManager.RunTaskAsync(async () => + { + keepReceiving = false; + + await Disconnect(); + + }, location); return this; } public async Task Restart() { - await Task.Run(async () => + var location = $"{nameof(DevicesClient)}.{nameof(Restart)}"; + + await TasksManager.RunTaskAsync(async () => { + await Stop(); + await Start(); - await Stop(); - }); + }, location); return this; } public void Dispose() { - + client?.Dispose(); } } diff --git a/Network/DevicesServer.cs b/Network/DevicesServer.cs index 97a80651..15d8c73d 100644 --- a/Network/DevicesServer.cs +++ b/Network/DevicesServer.cs @@ -1,4 +1,5 @@ -using KitX_Dashboard.Data; +using Common.BasicHelper.Utils.Extensions; +using KitX_Dashboard.Data; using KitX_Dashboard.Interfaces.Network; using KitX_Dashboard.Managers; using KitX_Dashboard.Services; @@ -7,7 +8,6 @@ using System.Collections.Generic; using System.Net; using System.Net.Sockets; -using System.Text; using System.Threading; using System.Threading.Tasks; @@ -15,358 +15,204 @@ namespace KitX_Dashboard.Network; internal class DevicesServer : IKitXServer { - public NetworkType Type { get; set; } = NetworkType.Unknown; + private static TcpListener? listener = null; - internal Thread? acceptDeviceThread; - internal Thread? receiveMessageThread; - internal TcpClient? DevicesHost; - internal TcpListener? listener; - internal bool keepListen = true; + private static bool keepListen = true; - public readonly Dictionary clients = new(); + private static readonly Dictionary clients = new(); - /// - /// 建立主控网络 - /// - internal void BuildServer() - { - listener = new(IPAddress.Any, 0); - acceptDeviceThread = new(AcceptClient); - - listener.Start(); - - var port = ((IPEndPoint)listener.LocalEndpoint).Port; // 取服务端口号 + private static Action? onReceive = null; - GlobalInfo.DeviceServerPort = port; // 全局端口号标明 - GlobalInfo.ServerBuildTime = DateTime.UtcNow; - GlobalInfo.IsMainMachine = true; + private static ServerStatus status = ServerStatus.Unknown; - Log.Information($"DevicesServer Port: {port}"); - - acceptDeviceThread.Start(); - - EventService.Invoke(nameof(EventService.DevicesServerPortChanged)); - } - - /// - /// 取消建立主控网络 - /// - internal void CancleBuildServer() + public static ServerStatus Status { - GlobalInfo.IsMainMachine = false; - GlobalInfo.DeviceServerPort = -1; - - EventService.Invoke(nameof(EventService.DevicesServerPortChanged)); - - keepListen = false; - acceptDeviceThread?.Join(); - - DevicesHost?.Close(); - DevicesHost?.Dispose(); + get => status; + set + { + status = value; - DevicesManager.Watch4MainDevice(); // 取消建立之后重新寻找并加入主控网络 + if (status == ServerStatus.Errored) + DevicesManager.Watch4MainDevice(); + } } /// /// 接收客户端 /// - internal void AcceptClient() + private static void AcceptClient() { + var location = $"{nameof(DevicesServer)}.{nameof(AcceptClient)}"; + try { - while (keepListen) + while (keepListen && listener is not null) { - if (listener != null && listener.Pending()) - { - TcpClient client = listener.AcceptTcpClient(); - if (client.Client.RemoteEndPoint is IPEndPoint endpoint) - { - clients.Add(endpoint.ToString(), client); - - Log.Information($"New device connection: {endpoint}"); - - // 新建并运行接收消息线程 - new Thread(() => - { - try - { - ReceiveMessage(client); - } - catch (Exception e) - { - Log.Error(e, - "In DevicesServer.AcceptClient().ReceiveMessageFromHost()"); - } - }).Start(); - } - } - else + if (listener.Pending()) { - Thread.Sleep(100); + var client = listener.AcceptTcpClient(); + + if (client.Client.RemoteEndPoint is not IPEndPoint endpoint) continue; + + clients.Add(endpoint.ToString(), client); + + Log.Information($"New device connection: {endpoint}"); + + ReceiveMessage(client); } } } catch (Exception ex) { - Log.Error($"In AcceptClient() : {ex.Message}"); + Log.Error(ex, $"In {nameof(location)}: {ex.Message}"); } } - /// - /// 向客户端发送消息 - /// - /// 消息内容 - /// 客户端 - internal void SendMessage(string msg, string client) - { - if (clients.ContainsKey(client)) - clients[client].Client.Send(Encoding.UTF8.GetBytes(msg)); - } - /// /// 接收客户端消息 /// /// TcpClient - private async void ReceiveMessage(object obj) + private static void ReceiveMessage(TcpClient client) { - TcpClient? client = obj as TcpClient; + var location = $"{nameof(DevicesServer)}.{nameof(ReceiveMessage)}"; + IPEndPoint? endpoint = null; NetworkStream? stream = null; - try + new Thread(async () => { - endpoint = client?.Client.RemoteEndPoint as IPEndPoint; - stream = client?.GetStream(); - - while (keepListen && stream != null) + try { - byte[] data = new byte[Program.Config.Web.SocketBufferSize]; + endpoint = client?.Client.RemoteEndPoint as IPEndPoint; - //如果远程主机已关闭连接,Read将立即返回零字节 - //int length = await stream.ReadAsync(data, 0, data.Length); + stream = client?.GetStream(); - int length = await stream.ReadAsync(data); + if (endpoint is null || stream is null) return; - if (length > 0) + while (keepListen) { - string msg = Encoding.UTF8.GetString(data, 0, length); + var buffer = new byte[Program.Config.Web.SocketBufferSize]; - Log.Information($"From: {endpoint}\tReceive: {msg}"); + var length = await stream.ReadAsync(buffer); + if (length > 0) + { + onReceive?.Invoke(buffer, length, endpoint.ToString()); - //发送到其他客户端 - //foreach (KeyValuePair kvp in clients) - //{ - // if (kvp.Value != client) - // { - // byte[] writeData = Encoding.UTF8.GetBytes(msg); - // NetworkStream writeStream = kvp.Value.GetStream(); - // writeStream.Write(writeData, 0, writeData.Length); - // } - //} - } - else - { + var msg = buffer.ToUTF8(0, length); - break; //客户端断开连接 跳出循环 + Log.Information($"From: {endpoint}\tReceive: {msg}"); + } + else break; // 客户端断开连接 跳出循环 } } - } - catch (Exception ex) - { - Log.Error($"Error: In ReceiveMessageFromHost() : {ex.Message}"); - Log.Information($"Connection broke from: {endpoint}"); - - //Read是阻塞方法 客户端退出是会引发异常 释放资源 结束此线程 - } - finally - { - //释放资源 - if (endpoint != null) + catch (Exception ex) { - clients.Remove(endpoint.ToString()); + Log.Error($"In {location}: {ex.Message}"); + Log.Information($"Connection broke from: {endpoint}"); } + finally + { + if (endpoint is not null) + { + clients.Remove(endpoint.ToString()); + } - stream?.Close(); - stream?.Dispose(); - client?.Dispose(); - } - } + stream?.CloseAndDispose(); - /// - /// 向所有客户端广播消息 - /// - /// 消息 - internal void BroadCastMessage(string msg, Func? pattern) - { - foreach (var client in clients) - { - if (pattern is not null && pattern.Invoke(client.Value)) - client.Value.Client.Send(Encoding.UTF8.GetBytes(msg)); - else client.Value.Client.Send(Encoding.UTF8.GetBytes(msg)); - } + client.Dispose(); + } + }).Start(); } - /// - /// 加入主控网络 - /// - /// 主控地址 - /// 主控端口 - internal void AttendServer(string serverAddress, int serverPort) + public async Task Broadcast(byte[] content) { - try + await Task.Run(() => { - DevicesHost?.Connect(serverAddress, serverPort); - - keepListen = true; - - receiveMessageThread = new(ReceiveMessageFromHost); + foreach (var client in clients) + client.Value.Client.Send(content); + }); - receiveMessageThread.Start(); + return this; + } - Log.Information($"Attending Server -> {serverAddress}:{serverPort}"); - } - catch (Exception ex) + public async Task BroadCast(byte[] content, Func? pattern) + { + await Task.Run(() => { - var location = $"{nameof(DevicesServer)}.{nameof(AttendServer)}"; + foreach (var client in clients) + { + if (pattern is not null && pattern.Invoke(client.Value)) + client.Value.Client.Send(content); + else client.Value.Client.Send(content); + } + }); - Log.Error(ex, $"In {location}: {ex.Message}"); - } + return this; } - /// - /// 向主控发送消息 - /// - /// 消息内容 - internal void SendMessageToHost(string msg) + public async Task Send(byte[] content, string target) { - try + await Task.Run(() => { - var stream = DevicesHost?.GetStream(); - - if (stream is null) return; - - var data = Encoding.UTF8.GetBytes(msg); - - stream.Write(data, 0, data.Length); - stream.Flush(); - - //DevicesHost?.Client.Send(Encoding.UTF8.GetBytes(msg)); + if (clients.ContainsKey(target)) + clients[target].Client.Send(content); + }); - Log.Information($"Sent Message to Host, msg: {msg}"); - } - catch (Exception e) - { - var location = $"{nameof(DevicesServer)}.{nameof(SendMessageToHost)}"; + return this; + } - Log.Error(e, $"In {location}: {e.Message}"); + public DevicesServer OnReceive(Action action) + { + onReceive = action; - Program.WebManager?.Restart(restartPluginsServer: false); - } + return this; } - /// - /// 从主控接收消息 - /// - internal async void ReceiveMessageFromHost() + public async Task Start() { - if (DevicesHost is null) return; + var location = $"{nameof(DevicesServer)}.{nameof(Start)}"; - var stream = DevicesHost?.GetStream(); + Status = ServerStatus.Pending; - if (stream is null) return; + keepListen = true; - var buffer = new byte[Program.Config.Web.SocketBufferSize]; // Default 10 MB buffer - - try + await TasksManager.RunTaskAsync(() => { - while (keepListen) - { - - if (buffer is null) break; + clients.Clear(); - var length = await stream.ReadAsync(buffer); + listener = new(IPAddress.Any, 0); + listener.Start(); - if (length > 0) - { - var msg = Encoding.UTF8.GetString(buffer, 0, length); + var port = ((IPEndPoint)listener.LocalEndpoint).Port; // 取服务端口号 - Log.Information($"Receive from Host: {msg}"); - } - else - { - keepListen = false; - break; - } - } + GlobalInfo.DeviceServerPort = port; // 全局端口号标明 + GlobalInfo.ServerBuildTime = DateTime.UtcNow; + GlobalInfo.IsMainMachine = true; - stream.Close(); - stream.Dispose(); - - Log.Information($"Closing `{nameof(ReceiveMessageFromHost)}` thread."); - - Program.WebManager?.Restart(restartPluginsServer: false); - } - catch (Exception e) - { - var location = $"{nameof(DevicesServer)}.{nameof(ReceiveMessageFromHost)}"; - Log.Error(e, $"In {location}: {e.Message}"); - - stream.Close(); - stream.Dispose(); - - DevicesHost?.Close(); - DevicesHost?.Dispose(); - - Program.WebManager?.Restart(restartPluginsServer: false); - } - } + Log.Information($"DevicesServer Port: {port}"); - public void Dispose() - { - GC.SuppressFinalize(this); - } + EventService.Invoke(nameof(EventService.DevicesServerPortChanged)); - public async Task Broadcast(byte[] content) - { - await Task.Run(() => { }); + Status = ServerStatus.Running; - return this; - } + new Thread(AcceptClient).Start(); - public async Task BroadCast(byte[] content, Func? pattern) - { - await Task.Run(() => { }); + }, location); return this; } - public async Task Send(byte[] content, string target) + public async Task Stop() { - await Task.Run(() => { }); - - return this; - } + var location = $"{nameof(DevicesServer)}.{nameof(Stop)}"; - public DevicesServer OnReceive(Action action) - { - return this; - } + Status = ServerStatus.Stopping; - public async Task Start() - { await TasksManager.RunTaskAsync(() => { - DevicesHost = new(); - }, $"{nameof(DevicesServer)}.{nameof(Start)}"); - - return this; - } + listener?.Stop(); - public async Task Stop() - { - await Task.Run(() => - { keepListen = false; foreach (KeyValuePair item in clients) @@ -375,23 +221,33 @@ await Task.Run(() => item.Value.Dispose(); } - acceptDeviceThread?.Join(); + GlobalInfo.IsMainMachine = false; + GlobalInfo.DeviceServerPort = -1; - DevicesHost?.Close(); - DevicesHost?.Dispose(); - }); + EventService.Invoke(nameof(EventService.DevicesServerPortChanged)); + + Status = ServerStatus.Pending; + + }, location); return this; } public async Task Restart() { - await Task.Run(async () => + var location = $"{nameof(DevicesServer)}.{nameof(Restart)}"; + + await TasksManager.RunTaskAsync(async () => { await Start(); await Stop(); - }); + }, location); return this; } + + public void Dispose() + { + GC.SuppressFinalize(this); + } } From 48d906f44c6c95a34266ffec7f4b95376774d7e8 Mon Sep 17 00:00:00 2001 From: Dynesshely Date: Mon, 24 Apr 2023 00:47:09 +0800 Subject: [PATCH 16/16] =?UTF-8?q?=F0=9F=92=BE=20Feat:=20Updated=20call=20s?= =?UTF-8?q?ite.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Managers/DevicesManager.cs | 123 ++++++++++++++---------- Managers/WebManager.cs | 57 ++++++----- Network/DevicesDiscoveryServer.cs | 12 +-- Network/PluginsServer.cs | 49 +++++----- Network/Status.cs | 52 ---------- Services/DebugService.cs | 47 +++++---- Services/EventService.cs | 8 +- ViewModels/Pages/DevicePageViewModel.cs | 4 +- 8 files changed, 170 insertions(+), 182 deletions(-) diff --git a/Managers/DevicesManager.cs b/Managers/DevicesManager.cs index 1963247d..b0fee46b 100644 --- a/Managers/DevicesManager.cs +++ b/Managers/DevicesManager.cs @@ -16,19 +16,23 @@ internal class DevicesManager { internal static void InitEvents() { - EventService.OnReceivingDeviceInfoStruct4DeviceNet += dis => + EventService.OnReceivingDeviceInfoStruct += dis => { - if (GlobalInfo.IsMainMachine) - if (dis.DeviceServerBuildTime < GlobalInfo.ServerBuildTime) - { - Program.WebManager?.devicesServer?.CancleBuildServer(); - Log.Information($"In DevicesManager: Watched for earlier built server. " + - $"DeviceServerAddress: {dis.IPv4}:{dis.DeviceServerPort} " + - $"DeviceServerBuildTime: {dis.DeviceServerBuildTime}"); - } + if (dis.IsMainDevice && dis.DeviceServerBuildTime < GlobalInfo.ServerBuildTime) + { + Program.WebManager?.devicesServer?.Stop(); + + Watch4MainDevice(); + + Log.Information($"In DevicesManager: Watched earlier built server. " + + $"DeviceServerAddress: {dis.IPv4}:{dis.DeviceServerPort} " + + $"DeviceServerBuildTime: {dis.DeviceServerBuildTime}"); + } }; } + private static readonly object _receivedDeviceInfoStruct4WatchLock = new(); + internal static List? receivedDeviceInfoStruct4Watch; internal static readonly Queue deviceInfoStructs = new(); @@ -266,11 +270,16 @@ internal static void KeepCheckAndRemove() internal static void Update(DeviceInfoStruct deviceInfo) { deviceInfoStructs.Enqueue(deviceInfo); - receivedDeviceInfoStruct4Watch?.Add(deviceInfo); - if (deviceInfo.IsMainDevice) - EventService.Invoke(nameof(EventService.OnReceivingDeviceInfoStruct4DeviceNet), - deviceInfo); + if (receivedDeviceInfoStruct4Watch is not null) + { + lock (_receivedDeviceInfoStruct4WatchLock) + { + receivedDeviceInfoStruct4Watch.Add(deviceInfo); + } + } + + EventService.Invoke(nameof(EventService.OnReceivingDeviceInfoStruct), deviceInfo); } /// @@ -278,30 +287,31 @@ internal static void Update(DeviceInfoStruct deviceInfo) /// internal static void Watch4MainDevice() { + var location = $"{nameof(DevicesManager)}.{nameof(Watch4MainDevice)}"; + new Thread(() => { - try - { - receivedDeviceInfoStruct4Watch = new(); + receivedDeviceInfoStruct4Watch = new(); - var checkedTime = 0; - var hadMainDevice = false; - var earliestBuiltServerTime = DateTime.UtcNow; - var serverPort = 0; - var serverAddress = string.Empty; + var checkedTime = 0; + var hadMainDevice = false; + var earliestBuiltServerTime = DateTime.UtcNow; + var serverPort = 0; + var serverAddress = string.Empty; - while (checkedTime < 7) + while (checkedTime < 7) + { + try { - try - { - if (receivedDeviceInfoStruct4Watch is null) continue; + if (receivedDeviceInfoStruct4Watch is null) continue; + lock (_receivedDeviceInfoStruct4WatchLock) + { foreach (var item in receivedDeviceInfoStruct4Watch) { if (item.IsMainDevice) { - if (item.DeviceServerBuildTime.ToUniversalTime() - < earliestBuiltServerTime) + if (item.DeviceServerBuildTime.ToUniversalTime() < earliestBuiltServerTime) { serverPort = item.DeviceServerPort; serverAddress = item.IPv4; @@ -309,36 +319,31 @@ internal static void Watch4MainDevice() hadMainDevice = true; } } + } - ++checkedTime; - - Log.Information($"In Watch4MainDevice: " + - $"Watched for {checkedTime} times."); + ++checkedTime; - if (checkedTime == 7) - { - receivedDeviceInfoStruct4Watch?.Clear(); - receivedDeviceInfoStruct4Watch = null; - WatchingOver(hadMainDevice, serverAddress, serverPort); - } + Log.Information($"In {location}: Watched for {checkedTime} times."); - Thread.Sleep(1 * 1000); // Sleep 1 second. - } - catch (Exception e) + if (checkedTime == 7) { receivedDeviceInfoStruct4Watch?.Clear(); receivedDeviceInfoStruct4Watch = null; - Log.Error(e, "In Watch4MainDevice"); + WatchingOver(hadMainDevice, serverAddress, serverPort); } + + Thread.Sleep(1 * 1000); // Sleep 1 second. } - } - catch (Exception ex) - { - receivedDeviceInfoStruct4Watch?.Clear(); - receivedDeviceInfoStruct4Watch = null; + catch (Exception e) + { + receivedDeviceInfoStruct4Watch?.Clear(); + receivedDeviceInfoStruct4Watch = null; + + Log.Error(e, $"In {location}: {e.Message} Rewatch."); - Log.Error(ex, "In Watch4MainDevice"); + Watch4MainDevice(); + } } }).Start(); } @@ -346,18 +351,30 @@ internal static void Watch4MainDevice() /// /// 观察结束 /// - internal static void WatchingOver(bool hadMainDevice, string serverAddress, int serverPort) + internal static async void WatchingOver(bool foundMainDevice, string serverAddress, int serverPort) { - Log.Information($"In WatchingOver: hadMainDevice: {hadMainDevice}, " + - $"serverAddress: {serverAddress}, serverPort: {serverPort}"); + var location = $"{nameof(DevicesManager)}.{nameof(WatchingOver)}"; + + Log.Information($"In {location}: " + + $"{nameof(foundMainDevice)} -> {foundMainDevice}, " + + $"{nameof(serverAddress)} -> {serverAddress}, " + + $"{nameof(serverPort)} -> {serverPort}"); - if (hadMainDevice) + if (foundMainDevice) { - Program.WebManager?.devicesServer?.AttendServer(serverAddress, serverPort); + var client = Program.WebManager?.devicesClient; + + if (client is null) return; + + await client + .SetServerAddress(serverAddress) + .SetServerPort(serverPort) + .Start() + ; } else { - Program.WebManager?.devicesServer?.BuildServer(); + Program.WebManager?.devicesServer?.Start(); } } } diff --git a/Managers/WebManager.cs b/Managers/WebManager.cs index 5718b5d5..99906e6a 100644 --- a/Managers/WebManager.cs +++ b/Managers/WebManager.cs @@ -15,6 +15,7 @@ public WebManager() internal PluginsServer? pluginsServer; internal DevicesServer? devicesServer; + internal DevicesClient? devicesClient; internal DevicesDiscoveryServer? devicesDiscoveryServer; internal ObservableCollection? NetworkInterfaceRegistered; @@ -23,7 +24,7 @@ public WebManager() /// 开始执行网络相关服务 /// /// 是否启动全部 - /// 是否启动插件服务器 + /// 是否启动插件服务器 /// 是否启动设备服务器 /// 是否启动设备自发现服务器 /// 网络管理器本身 @@ -31,8 +32,8 @@ public async Task Start ( bool startAll = true, - bool startPluginsServer = false, - bool startDevicesServer = false, + bool startPluginsServices = false, + bool startDevicesServices = false, bool startDevicesDiscoveryServer = false ) { @@ -45,16 +46,17 @@ await TasksManager.RunTaskAsync(async () => if (startAll || startDevicesDiscoveryServer) devicesDiscoveryServer = await new DevicesDiscoveryServer().Start(); - if (startAll || startDevicesServer) + if (startAll || startDevicesServices) { + devicesServer = new(); + devicesClient = new(); + DevicesManager.InitEvents(); DevicesManager.KeepCheckAndRemove(); DevicesManager.Watch4MainDevice(); - - devicesServer = await new DevicesServer().Start(); } - if (startAll || startPluginsServer) + if (startAll || startPluginsServices) { PluginsManager.KeepCheckAndRemove(); PluginsManager.KeepCheckAndRemoveOrDelete(); @@ -65,11 +67,11 @@ await TasksManager.RunTaskAsync(async () => catch (Exception ex) { Log.Error(ex, $"In {location}: " + - $"{nameof(startPluginsServer)}: {startPluginsServer}," + - $"{nameof(startDevicesServer)}: {startDevicesServer}," + + $"{nameof(startPluginsServices)}: {startPluginsServices}," + + $"{nameof(startDevicesServices)}: {startDevicesServices}," + $"{nameof(startDevicesDiscoveryServer)}: {startDevicesDiscoveryServer}"); } - }, "WebManager.Start"); + }, location); return this; } @@ -77,28 +79,34 @@ await TasksManager.RunTaskAsync(async () => /// /// 停止执行网络相关服务 /// - /// 是否停止插件服务器 - /// 是否停止设备服务器 + /// 是否停止插件服务器 + /// 是否停止设备服务器 /// 网络管理器本身 public WebManager Stop ( bool stopAll = true, - bool stopPluginsServer = true, - bool stopDevicesServer = true, + bool stopPluginsServices = true, + bool stopDevicesServices = true, bool stopDevicesDiscoveryServer = true ) { - if (stopAll || stopPluginsServer) + if (stopAll || stopPluginsServices) pluginsServer?.Stop().ContinueWith( server => server.Dispose() ); - if (stopAll || stopDevicesServer) + if (stopAll || stopDevicesServices) + { devicesServer?.Stop().ContinueWith( server => server.Dispose() ); + devicesClient?.Stop().ContinueWith( + client => client.Dispose() + ); + } + if (stopAll || stopDevicesDiscoveryServer) devicesDiscoveryServer?.Stop().ContinueWith( server => server.Dispose() @@ -110,16 +118,16 @@ public WebManager Stop /// /// 重启网络相关服务 /// - /// 是否重启插件服务器 - /// 是否重启设备服务器 + /// 是否重启插件服务器 + /// 是否重启设备服务器 /// 重新启动前要执行的操作 /// 网络管理器本身 public WebManager Restart ( bool restartAll = true, - bool restartPluginsServer = false, - bool restartDevicesServer = false, + bool restartPluginsServices = false, + bool restartDevicesServices = false, bool restartDevicesDiscoveryServer = false, Action? actionBeforeStarting = null @@ -127,8 +135,8 @@ public WebManager Restart { Stop( restartAll, - restartPluginsServer, - restartDevicesServer, + restartPluginsServices, + restartDevicesServices, restartDevicesDiscoveryServer ); @@ -140,8 +148,8 @@ public WebManager Restart await Start( restartAll, - restartPluginsServer, - restartDevicesServer, + restartPluginsServices, + restartDevicesServices, restartDevicesDiscoveryServer ); }); @@ -156,6 +164,7 @@ public void Dispose() { pluginsServer?.Dispose(); devicesServer?.Dispose(); + devicesClient?.Dispose(); devicesDiscoveryServer?.Dispose(); GC.SuppressFinalize(this); diff --git a/Network/DevicesDiscoveryServer.cs b/Network/DevicesDiscoveryServer.cs index 25938f0d..90ee473d 100644 --- a/Network/DevicesDiscoveryServer.cs +++ b/Network/DevicesDiscoveryServer.cs @@ -11,7 +11,6 @@ using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; -using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -184,13 +183,13 @@ private void MultiDevicesBroadCastSend() DefaultDeviceInfoStruct.SendTime -= TimeSpan.FromSeconds(20); var sendText = JsonSerializer.Serialize(DefaultDeviceInfoStruct); - var sendBytes = Encoding.UTF8.GetBytes(sendText); + var sendBytes = sendText.FromUTF8(); foreach (var item in SupportedNetworkInterfacesIndexes) { if (!GlobalInfo.Running || CloseDevicesDiscoveryServerRequest) break; - // 如果错误网络适配器中存在当前项的记录, 跳过 + // 如果错误网络适配器中存在当前项的记录, 跳过 if (erroredInterfacesIndexes.Contains(item)) continue; try @@ -202,11 +201,12 @@ private void MultiDevicesBroadCastSend() ); UdpSender?.Send(sendBytes, sendBytes.Length, multicast); + // 将自定义广播消息全部发送 while (Messages2BroadCast.Count > 0) { - var messageBytes = Encoding.UTF8.GetBytes(Messages2BroadCast.Dequeue()); + var messageBytes = Messages2BroadCast.Dequeue().FromUTF8(); UdpSender?.Send(messageBytes, messageBytes.Length, multicast); - } // 将自定义广播消息全部发送 + } } catch (Exception ex) { @@ -256,7 +256,7 @@ private void MultiDevicesBroadCastReceive() onReceive?.Invoke(bytes, null, client); - var result = Encoding.UTF8.GetString(bytes); + var result = bytes.ToUTF8(); Log.Information($"UDP From: {client,-21}, Receive: {result}"); diff --git a/Network/PluginsServer.cs b/Network/PluginsServer.cs index a208c56a..ace9a0bf 100644 --- a/Network/PluginsServer.cs +++ b/Network/PluginsServer.cs @@ -8,13 +8,9 @@ using System.Collections.Generic; using System.Net; using System.Net.Sockets; -using System.Text; using System.Threading; using System.Threading.Tasks; -#pragma warning disable CS8602 // 解引用可能出现空引用。 -#pragma warning disable CS8604 // 引用类型参数可能为 null。 - namespace KitX_Dashboard.Network; internal class PluginsServer : IKitXServer @@ -27,6 +23,8 @@ internal class PluginsServer : IKitXServer private static Action? onReceive = null; + public static ServerStatus? Status { get; set; } = ServerStatus.Unknown; + /// /// 接收客户端 /// @@ -36,23 +34,20 @@ private static void AcceptClient() try { - while (keepListen) + while (keepListen && listener is not null) { if (listener.Pending()) { var client = listener.AcceptTcpClient(); - var endpoint = client.Client.RemoteEndPoint as IPEndPoint; + + if (client.Client.RemoteEndPoint is not IPEndPoint endpoint) continue; clients.Add(endpoint.ToString(), client); Log.Information($"New plugin connection: {endpoint}"); - ReciveMessage(client); + ReceiveMessage(client); } - //else - //{ - // Thread.Sleep(100); - //} } } catch (Exception ex) @@ -65,9 +60,9 @@ private static void AcceptClient() /// 接收消息 /// /// TcpClient - private static void ReciveMessage(TcpClient client) + private static void ReceiveMessage(TcpClient client) { - var location = $"{nameof(PluginsServer)}.{nameof(ReciveMessage)}"; + var location = $"{nameof(PluginsServer)}.{nameof(ReceiveMessage)}"; IPEndPoint? endpoint = null; NetworkStream? stream = null; @@ -77,19 +72,22 @@ private static void ReciveMessage(TcpClient client) try { endpoint = client.Client.RemoteEndPoint as IPEndPoint; + stream = client.GetStream(); + if (endpoint is null || stream is null) return; + while (keepListen) { var buffer = new byte[Program.Config.Web.SocketBufferSize]; - var length = await stream.ReadAsync(buffer); + var length = stream is null ? 0 : await stream.ReadAsync(buffer); if (length > 0) { onReceive?.Invoke(buffer, length, endpoint.ToString()); - var msg = Encoding.UTF8.GetString(buffer, 0, length); + var msg = buffer.ToUTF8(0, length); Log.Information($"From: {endpoint}\tReceive: {msg}"); @@ -99,7 +97,7 @@ private static void ReciveMessage(TcpClient client) var workPath = Program.Config.App.LocalPluginsDataFolder.GetFullPath(); var sendtxt = $"WorkPath: {workPath}"; - var bytes = Encoding.UTF8.GetBytes(sendtxt); + var bytes = sendtxt.FromUTF8(); stream?.Write(bytes, 0, bytes.Length); } @@ -125,11 +123,14 @@ private static void ReciveMessage(TcpClient client) } finally { - PluginsManager.Disconnect(endpoint); + if (endpoint is not null) + { + PluginsManager.Disconnect(endpoint); - stream?.CloseAndDispose(); + clients.Remove(endpoint.ToString()); + } - clients.Remove(endpoint.ToString()); + stream?.CloseAndDispose(); client.Dispose(); } @@ -138,6 +139,8 @@ private static void ReciveMessage(TcpClient client) private static void Init() { + clients.Clear(); + var port = Program.Config.Web.UserSpecifiedPluginsServerPort; if (port < 0 || port > 65535) port = null; @@ -179,6 +182,8 @@ await TasksManager.RunTaskAsync(() => { Init(); + if (listener is null) return; + listener.Start(); var port = ((IPEndPoint)listener.LocalEndpoint).Port; // 取服务端口号 @@ -221,11 +226,9 @@ public async Task Restart() public void Dispose() { keepListen = false; - listener.Stop(); + + listener?.Stop(); GC.SuppressFinalize(this); } } - -#pragma warning restore CS8604 // 引用类型参数可能为 null。 -#pragma warning restore CS8602 // 解引用可能出现空引用。 diff --git a/Network/Status.cs b/Network/Status.cs index 6d582130..e451badb 100644 --- a/Network/Status.cs +++ b/Network/Status.cs @@ -1,57 +1,5 @@ namespace KitX_Dashboard.Network; -/// -/// 网络状态 -/// -internal enum NetworkStatus -{ - /// - /// 未知状态 - /// - Unknown = 0, - - /// - /// 正在连接 - /// - Connecting = 1, - - /// - /// 已经连接 - /// - Connected = 2, - - /// - /// 连接断开 - /// - Disconnected = 3, - - /// - /// 发生错误 - /// - Errored = 4, -} - -/// -/// 网络类型 -/// -internal enum NetworkType -{ - /// - /// 未知类型 - /// - Unknown = 0, - - /// - /// 服务器 - /// - Server = 1, - - /// - /// 客户端 - /// - Client = 2, -} - /// /// 服务器状态 /// diff --git a/Services/DebugService.cs b/Services/DebugService.cs index 495d89cf..88fe8ae6 100644 --- a/Services/DebugService.cs +++ b/Services/DebugService.cs @@ -1,4 +1,5 @@ -using KitX_Dashboard.Network; +using Common.BasicHelper.Utils.Extensions; +using KitX_Dashboard.Network; using Serilog; using System; using System.Collections.Generic; @@ -135,7 +136,7 @@ private static string SaveConfig() case "clientmessage": if (args.ContainsKey("--value")) { - Program.WebManager?.devicesServer?.SendMessageToHost(args["--value"]); + Program.WebManager?.devicesClient?.Send(args["--value"].FromUTF8()); return $"Sent msg: {args["--value"]}"; } else return "Missing value of `--value`."; @@ -145,8 +146,10 @@ private static string SaveConfig() { if (args.ContainsKey("--to")) { - Program.WebManager?.devicesServer - ?.SendMessage(args["--value"], args["--to"]); + Program.WebManager?.devicesServer?.Send( + args["--value"].FromUTF8(), + args["--to"] + ); return $"Sent msg: {args["--value"]}, to: {args["--to"]}"; } else return "Missing value of `--to`."; @@ -156,8 +159,10 @@ private static string SaveConfig() case "hostbroadcast": if (args.ContainsKey("--value")) { - Program.WebManager?.devicesServer - ?.BroadCastMessage(args["--value"], null); + Program.WebManager?.devicesServer?.BroadCast( + args["--value"].FromUTF8(), + null + ); return $"Broadcast msg: {args["--value"]}"; } else return "Missing value of `--value`"; @@ -247,14 +252,17 @@ private static string SaveConfig() { if (args.ContainsKey("help")) return "" + - "- plugins-server\n" + - "- devices-server\n" + + "- plugins-services\n" + + "- devices-services\n" + + "- devices-discovery-server\n" + "- all\n"; - if (args.ContainsKey("plugins-server")) - Program.WebManager?.Start(startDevicesServer: false); - if (args.ContainsKey("devices-server")) - Program.WebManager?.Start(startPluginsServer: false); + if (args.ContainsKey("plugins-services")) + Program.WebManager?.Start(startAll: false, startPluginsServices: true); + if (args.ContainsKey("devices-services")) + Program.WebManager?.Start(startAll: false, startDevicesServices: true); + if (args.ContainsKey("devices-discovery-server")) + Program.WebManager?.Start(startAll: false, startDevicesDiscoveryServer: true); if (args.ContainsKey("all")) Program.WebManager?.Start(); @@ -265,14 +273,17 @@ private static string SaveConfig() { if (args.ContainsKey("help")) return "" + - "- plugins-server\n" + - "- devices-server\n" + + "- plugins-services\n" + + "- devices-services\n" + + "- devices-discovery-server\n" + "- all"; - if (args.ContainsKey("plugins-server")) - Program.WebManager?.Stop(stopDevicesServer: false); - if (args.ContainsKey("devices-server")) - Program.WebManager?.Stop(stopPluginsServer: false); + if (args.ContainsKey("plugins-services")) + Program.WebManager?.Stop(stopAll: false, stopPluginsServices: true); + if (args.ContainsKey("devices-services")) + Program.WebManager?.Stop(stopAll: false, stopDevicesServices: true); + if (args.ContainsKey("devices-discovery-server")) + Program.WebManager?.Stop(stopAll: false, stopDevicesDiscoveryServer: true); if (args.ContainsKey("all")) Program.WebManager?.Stop(); diff --git a/Services/EventService.cs b/Services/EventService.cs index 3fe474a6..effd9613 100644 --- a/Services/EventService.cs +++ b/Services/EventService.cs @@ -59,7 +59,7 @@ internal static class EventService internal static event DevicesServerPortChangedHandler? DevicesServerPortChanged; - internal static event OnReceivingDeviceInfoStructHandler? OnReceivingDeviceInfoStruct4DeviceNet; + internal static event OnReceivingDeviceInfoStructHandler? OnReceivingDeviceInfoStruct; internal static event OnConfigHotReloadedHandler? OnConfigHotReloaded; @@ -80,7 +80,7 @@ internal static void Init() UseStatisticsChanged += () => { }; OnExiting += () => { }; DevicesServerPortChanged += () => { }; - OnReceivingDeviceInfoStruct4DeviceNet += dis => { }; + OnReceivingDeviceInfoStruct += dis => { }; OnConfigHotReloaded += () => { }; PluginsServerPortChanged += () => { }; } @@ -144,8 +144,8 @@ internal static void Invoke(string eventName, object arg) { switch (eventName) { - case nameof(OnReceivingDeviceInfoStruct4DeviceNet): - OnReceivingDeviceInfoStruct4DeviceNet?.Invoke((DeviceInfoStruct)arg); + case nameof(OnReceivingDeviceInfoStruct): + OnReceivingDeviceInfoStruct?.Invoke((DeviceInfoStruct)arg); break; } } diff --git a/ViewModels/Pages/DevicePageViewModel.cs b/ViewModels/Pages/DevicePageViewModel.cs index d4f1627a..e4159736 100644 --- a/ViewModels/Pages/DevicePageViewModel.cs +++ b/ViewModels/Pages/DevicePageViewModel.cs @@ -56,7 +56,7 @@ internal static void RestartDevicvesServer(object _) { Program.WebManager?.Restart( restartAll: false, - restartDevicesServer: false, + restartDevicesServices: false, restartDevicesDiscoveryServer: true, actionBeforeStarting: () => DeviceCards.Clear() ); @@ -66,7 +66,7 @@ internal static void StopDevicvesServer(object _) { Program.WebManager?.Stop( stopAll: false, - stopDevicesServer: true, + stopDevicesServices: true, stopDevicesDiscoveryServer: true );