using AuroraDesk.Presentation.ViewModels.Base; using Microsoft.Extensions.Logging; using ReactiveUI; using System; using System.Collections.ObjectModel; 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 UdpClientPageViewModel : RoutableViewModel { private readonly ILogger? _logger; private UdpClient? _udpClient; private CancellationTokenSource? _receiveCancellationTokenSource; private bool _isConnected; private string _serverIp = "192.168.14.58"; private int _serverPort = 50505; private int _localPort = 0; // 0 表示系统自动分配 private string _message = string.Empty; private string _statusMessage = "未连接"; private readonly ObservableCollection _receivedMessages = new(); private readonly ObservableCollection _sentMessages = new(); private int _receivedMessageIndex; private bool _isAutoScrollToBottom = true; public UdpClientPageViewModel( IScreen hostScreen, ILogger? logger = null) : base(hostScreen, "UdpClient") { _logger = logger; // 创建命令 ConnectCommand = ReactiveCommand.CreateFromTask(ConnectAsync, this.WhenAnyValue(x => x.IsConnected, isConnected => !isConnected)); DisconnectCommand = ReactiveCommand.Create(Disconnect, this.WhenAnyValue(x => x.IsConnected)); SendMessageCommand = ReactiveCommand.CreateFromTask(SendMessageAsync, this.WhenAnyValue(x => x.IsConnected, x => x.Message, (isConnected, message) => isConnected && !string.IsNullOrWhiteSpace(message))); ClearMessagesCommand = ReactiveCommand.Create(ClearMessages); } /// /// 是否已连接 /// public bool IsConnected { get => _isConnected; set => this.RaiseAndSetIfChanged(ref _isConnected, value); } /// /// 服务器 IP 地址 /// public string ServerIp { get => _serverIp; set => this.RaiseAndSetIfChanged(ref _serverIp, value); } /// /// 服务器端口 /// public int ServerPort { get => _serverPort; set => this.RaiseAndSetIfChanged(ref _serverPort, value); } /// /// 本地端口 /// public int LocalPort { get => _localPort; set => this.RaiseAndSetIfChanged(ref _localPort, value); } /// /// 要发送的消息 /// public string Message { get => _message; set => this.RaiseAndSetIfChanged(ref _message, value); } /// /// 状态消息 /// public string StatusMessage { get => _statusMessage; set => this.RaiseAndSetIfChanged(ref _statusMessage, value); } /// /// 是否自动滚动到底部 /// public bool IsAutoScrollToBottom { get => _isAutoScrollToBottom; set => this.RaiseAndSetIfChanged(ref _isAutoScrollToBottom, value); } /// /// 接收到的消息列表 /// public ObservableCollection ReceivedMessages => _receivedMessages; /// /// 已发送的消息列表 /// public ObservableCollection SentMessages => _sentMessages; /// /// 连接命令 /// public ReactiveCommand ConnectCommand { get; } /// /// 断开连接命令 /// public ReactiveCommand DisconnectCommand { get; } /// /// 发送消息命令 /// public ReactiveCommand SendMessageCommand { get; } /// /// 清空消息命令 /// public ReactiveCommand ClearMessagesCommand { get; } /// /// 连接到服务器 /// private async Task ConnectAsync() { try { if (!IPAddress.TryParse(ServerIp, out var ipAddress)) { StatusMessage = $"无效的 IP 地址: {ServerIp}"; _logger?.LogWarning("无效的 IP 地址: {Ip}", ServerIp); return; } if (ServerPort < 1 || ServerPort > 65535) { StatusMessage = $"无效的端口: {ServerPort}"; _logger?.LogWarning("无效的端口: {Port}", ServerPort); return; } // 创建 UDP 客户端 _udpClient = new UdpClient(LocalPort); _udpClient.EnableBroadcast = true; var localEndPoint = (IPEndPoint)_udpClient.Client.LocalEndPoint!; LocalPort = localEndPoint.Port; IsConnected = true; StatusMessage = $"已连接到 {ServerIp}:{ServerPort} (本地端口: {LocalPort})"; _logger?.LogInformation("UDP 客户端已连接到 {ServerIp}:{ServerPort}, 本地端口: {LocalPort}", ServerIp, ServerPort, LocalPort); // 启动接收任务 _receiveCancellationTokenSource = new CancellationTokenSource(); _ = Task.Run(() => ReceiveMessagesAsync(_receiveCancellationTokenSource.Token)); } catch (Exception ex) { _logger?.LogError(ex, "连接失败"); StatusMessage = $"连接失败: {ex.Message}"; Disconnect(); } } /// /// 断开连接 /// private void Disconnect() { try { _receiveCancellationTokenSource?.Cancel(); _receiveCancellationTokenSource?.Dispose(); _receiveCancellationTokenSource = null; _udpClient?.Close(); _udpClient?.Dispose(); _udpClient = null; IsConnected = false; StatusMessage = "已断开连接"; _logger?.LogInformation("UDP 客户端已断开连接"); } catch (Exception ex) { _logger?.LogError(ex, "断开连接时出错"); } } /// /// 发送消息 /// private async Task SendMessageAsync() { if (_udpClient == null || string.IsNullOrWhiteSpace(Message)) return; try { if (!IPAddress.TryParse(ServerIp, out var ipAddress)) { StatusMessage = $"无效的 IP 地址: {ServerIp}"; return; } var remoteEndPoint = new IPEndPoint(ipAddress, ServerPort); var data = Encoding.UTF8.GetBytes(Message); await _udpClient.SendAsync(data, data.Length, remoteEndPoint); var timestamp = DateTime.Now.ToString("HH:mm:ss"); var displayMessage = $"[{timestamp}] 发送: {Message}"; await Dispatcher.UIThread.InvokeAsync(() => { SentMessages.Add(displayMessage); }); _logger?.LogInformation("发送消息到 {ServerIp}:{ServerPort}: {Message}", ServerIp, ServerPort, Message); Message = string.Empty; // 清空输入框 } catch (Exception ex) { _logger?.LogError(ex, "发送消息失败"); StatusMessage = $"发送失败: {ex.Message}"; } } /// /// 接收消息(后台任务) /// private async Task ReceiveMessagesAsync(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested && _udpClient != null) { try { var result = await _udpClient.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}"; var index = Interlocked.Increment(ref _receivedMessageIndex); await Dispatcher.UIThread.InvokeAsync(() => { ReceivedMessages.Add(new ReceivedMessageEntry(index, displayMessage)); }); _logger?.LogDebug("收到消息: {Message} 来自 {EndPoint}", receivedData, 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(); Interlocked.Exchange(ref _receivedMessageIndex, 0); SentMessages.Clear(); _logger?.LogInformation("已清空消息列表"); } /// /// 清理资源 /// public void Dispose() { Disconnect(); } } public sealed record ReceivedMessageEntry(int Index, string DisplayText);