using System; using System.Collections.Concurrent; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading.Tasks; using Newtonsoft.Json; using TeamAAS.Communication.Attributes; using TeamAAS.Communication.Base; using TeamAAS.Communication.Interfaces; namespace TeamAAS.Communication.Devices { /// /// UDP 通讯(数据报协议,无连接,广播/多播支持)。 /// [Communication("UDP", "基础通讯", "UDP 数据报通讯")] public class UdpCommunication : BindableCommunicationBase { [JsonIgnore] private UdpClient _udpClient; private readonly ConcurrentQueue _received = new ConcurrentQueue(); private string _localIp = "0.0.0.0"; public string LocalIp { get { return _localIp; } set { if (SetProperty(ref _localIp, value)) Notify(nameof(EndpointUrl)); } } private int _localPort = 8888; public int LocalPort { get { return _localPort; } set { if (SetProperty(ref _localPort, value)) Notify(nameof(EndpointUrl)); } } private string _remoteIp = "127.0.0.1"; public string RemoteIp { get { return _remoteIp; } set { if (SetProperty(ref _remoteIp, value)) Notify(nameof(EndpointUrl)); } } private int _remotePort = 9999; public int RemotePort { get { return _remotePort; } set { if (SetProperty(ref _remotePort, value)) Notify(nameof(EndpointUrl)); } } public override string EndpointUrl { get { return $"udp://{LocalIp}:{LocalPort} -> {RemoteIp}:{RemotePort}"; } set { if (!string.IsNullOrWhiteSpace(value) && value.StartsWith("udp://")) { var uri = new Uri(value); if (!string.IsNullOrWhiteSpace(uri.Host)) LocalIp = uri.Host; if (uri.Port > 0) LocalPort = uri.Port; } } } [JsonIgnore] public override bool IsConnected => _udpClient != null; public override event Action ConnectChangedEvent; public override event Action DataReceivedEvent; public override void Connect() { Disconnect(); IPAddress bindIp = IPAddress.Any; if (!string.IsNullOrWhiteSpace(LocalIp)) { try { bindIp = IPAddress.Parse(LocalIp.Trim()); } catch { bindIp = IPAddress.Any; } } _udpClient = new UdpClient(LocalPort) { ExclusiveAddressUse = false }; try { _udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); } catch { } _ = Task.Run(ReceiveLoop); ConnectChangedEvent?.Invoke(this, true); Notify(nameof(IsConnected)); } public override Task ConnectAsync() { return Task.Run(() => Connect()); } private async Task ReceiveLoop() { try { while (_udpClient != null) { var result = await _udpClient.ReceiveAsync(); var text = Encoding.UTF8.GetString(result.Buffer, 0, result.Buffer.Length); _received.Enqueue(text); DataReceivedEvent?.Invoke(this, text); } } catch { } } public void Send(string text) { if (_udpClient == null) throw new InvalidOperationException("UDP 未连接。"); var data = Encoding.UTF8.GetBytes(text ?? string.Empty); var endPoint = new IPEndPoint(IPAddress.Parse(RemoteIp), RemotePort); _udpClient.Send(data, data.Length, endPoint); } public void Send(byte[] data) { if (_udpClient == null) throw new InvalidOperationException("UDP 未连接。"); var endPoint = new IPEndPoint(IPAddress.Parse(RemoteIp), RemotePort); _udpClient.Send(data, data.Length, endPoint); } public override object ReadValue(string address) { _received.TryDequeue(out var text); return text; } public override Task ReadValueAsync(string address) { return Task.FromResult(ReadValue(address)); } public override void WriteValue(string address, object value) { Send(value?.ToString() ?? string.Empty); } public override Task WriteValueAsync(string address, object value) { Send(value?.ToString() ?? string.Empty); return Task.CompletedTask; } public override void Disconnect() { if (_udpClient != null) { try { _udpClient.Close(); } catch { } _udpClient.Dispose(); _udpClient = null; } ConnectChangedEvent?.Invoke(this, false); Notify(nameof(IsConnected)); } public override void Dispose() { Disconnect(); } } }