using AuroraDesk.Presentation.ViewModels.Base; using Microsoft.Extensions.Logging; using ReactiveUI; using System; using System.Collections.ObjectModel; using System.Linq; using System.Net; using System.Net.Sockets; using System.Reactive; using System.Text; using System.Threading; using System.Threading.Tasks; using Avalonia.Threading; namespace AuroraDesk.Presentation.ViewModels.Pages; /// /// UDP 服务端页面 ViewModel /// public class UdpServerPageViewModel : RoutableViewModel { private readonly ILogger? _logger; private UdpClient? _udpServer; private CancellationTokenSource? _listenCancellationTokenSource; private bool _isListening; private int _listenPort = 8080; private string _statusMessage = "未启动"; private readonly ObservableCollection _receivedMessages = new(); private readonly ObservableCollection _clients = new(); public UdpServerPageViewModel( IScreen hostScreen, ILogger? logger = null) : base(hostScreen, "UdpServer") { _logger = logger; // 创建命令 StartListeningCommand = ReactiveCommand.CreateFromTask(StartListeningAsync, this.WhenAnyValue(x => x.IsListening, isListening => !isListening)); StopListeningCommand = ReactiveCommand.Create(StopListening, this.WhenAnyValue(x => x.IsListening)); ClearMessagesCommand = ReactiveCommand.Create(ClearMessages); ClearClientsCommand = ReactiveCommand.Create(ClearClients); } /// /// 是否正在监听 /// public bool IsListening { get => _isListening; set => this.RaiseAndSetIfChanged(ref _isListening, value); } /// /// 监听端口 /// public int ListenPort { get => _listenPort; set => this.RaiseAndSetIfChanged(ref _listenPort, value); } /// /// 状态消息 /// public string StatusMessage { get => _statusMessage; set => this.RaiseAndSetIfChanged(ref _statusMessage, value); } /// /// 接收到的消息列表 /// public ObservableCollection ReceivedMessages => _receivedMessages; /// /// 客户端列表 /// public ObservableCollection Clients => _clients; /// /// 开始监听命令 /// public ReactiveCommand StartListeningCommand { get; } /// /// 停止监听命令 /// public ReactiveCommand StopListeningCommand { get; } /// /// 清空消息命令 /// public ReactiveCommand ClearMessagesCommand { get; } /// /// 清空客户端命令 /// public ReactiveCommand ClearClientsCommand { get; } /// /// 开始监听 /// private async Task StartListeningAsync() { try { if (ListenPort < 1 || ListenPort > 65535) { StatusMessage = $"无效的端口: {ListenPort}"; _logger?.LogWarning("无效的端口: {Port}", ListenPort); return; } // 创建 UDP 服务器 var localEndPoint = new IPEndPoint(IPAddress.Any, ListenPort); _udpServer = new UdpClient(localEndPoint); _udpServer.EnableBroadcast = true; IsListening = true; StatusMessage = $"正在监听端口 {ListenPort}..."; _logger?.LogInformation("UDP 服务器开始监听端口 {Port}", ListenPort); // 启动接收任务 _listenCancellationTokenSource = new CancellationTokenSource(); _ = Task.Run(() => ListenForMessagesAsync(_listenCancellationTokenSource.Token)); } catch (SocketException ex) when (ex.SocketErrorCode == SocketError.AddressAlreadyInUse) { StatusMessage = $"端口 {ListenPort} 已被占用"; _logger?.LogWarning("端口 {Port} 已被占用", ListenPort); IsListening = false; } catch (Exception ex) { _logger?.LogError(ex, "启动监听失败"); StatusMessage = $"启动失败: {ex.Message}"; IsListening = false; } } /// /// 停止监听 /// private void StopListening() { try { _listenCancellationTokenSource?.Cancel(); _listenCancellationTokenSource?.Dispose(); _listenCancellationTokenSource = null; _udpServer?.Close(); _udpServer?.Dispose(); _udpServer = null; IsListening = false; StatusMessage = "已停止监听"; _logger?.LogInformation("UDP 服务器已停止监听"); } catch (Exception ex) { _logger?.LogError(ex, "停止监听时出错"); } } /// /// 监听消息(后台任务) /// private async Task ListenForMessagesAsync(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested && _udpServer != null) { try { var result = await _udpServer.ReceiveAsync(); var receivedData = Encoding.UTF8.GetString(result.Buffer); var remoteEndPoint = result.RemoteEndPoint; var timestamp = DateTime.Now.ToString("HH:mm:ss"); var displayMessage = $"[{timestamp}] 来自 {remoteEndPoint.Address}:{remoteEndPoint.Port}: {receivedData}"; await Dispatcher.UIThread.InvokeAsync(() => { ReceivedMessages.Add(displayMessage); // 更新客户端列表 var clientAddress = remoteEndPoint.Address.ToString(); var clientPort = remoteEndPoint.Port; var clientKey = $"{clientAddress}:{clientPort}"; var existingClient = Clients.FirstOrDefault(c => c.Address == clientAddress && c.Port == clientPort); if (existingClient == null) { Clients.Add(new ClientInfo { Address = clientAddress, Port = clientPort, FirstSeen = DateTime.Now, LastSeen = DateTime.Now, MessageCount = 1 }); } else { existingClient.LastSeen = DateTime.Now; existingClient.MessageCount++; } }); _logger?.LogDebug("收到消息: {Message} 来自 {EndPoint}", receivedData, remoteEndPoint); // 自动回复(可选) var responseMessage = $"收到: {receivedData}"; var responseData = Encoding.UTF8.GetBytes(responseMessage); await _udpServer.SendAsync(responseData, responseData.Length, remoteEndPoint); } catch (ObjectDisposedException) { // UDP 服务器已关闭,正常退出 break; } catch (SocketException ex) when (ex.SocketErrorCode == SocketError.Interrupted) { // 操作被取消,正常退出 break; } catch (Exception ex) { _logger?.LogError(ex, "接收消息时出错"); await Task.Delay(1000, cancellationToken); // 等待后重试 } } } /// /// 清空消息 /// private void ClearMessages() { ReceivedMessages.Clear(); _logger?.LogInformation("已清空消息列表"); } /// /// 清空客户端列表 /// private void ClearClients() { Clients.Clear(); _logger?.LogInformation("已清空客户端列表"); } /// /// 清理资源 /// public void Dispose() { StopListening(); } } /// /// 客户端信息 /// public class ClientInfo : ReactiveObject { private string _address = string.Empty; private int _port; private DateTime _firstSeen; private DateTime _lastSeen; private int _messageCount; public string Address { get => _address; set => this.RaiseAndSetIfChanged(ref _address, value); } public int Port { get => _port; set => this.RaiseAndSetIfChanged(ref _port, value); } public DateTime FirstSeen { get => _firstSeen; set => this.RaiseAndSetIfChanged(ref _firstSeen, value); } public DateTime LastSeen { get => _lastSeen; set => this.RaiseAndSetIfChanged(ref _lastSeen, value); } public int MessageCount { get => _messageCount; set => this.RaiseAndSetIfChanged(ref _messageCount, value); } public string DisplayName => $"{Address}:{Port}"; }