using Common.Logging; using Common.Messaging; using CommunityToolkit.Mvvm.Messaging; using MultiTerm.Protocols.Helpers; using MultiTerm.Protocols.Model; using MultiTerm.Protocols.Network; using System.Net.Sockets; namespace MultiTerm.Protocols.Udp; public class UdpProtocol : CommunicationProtocol { public override Types.ProtocolType ProtocolType => Types.ProtocolType.Udp; public override string InstanceIdentifier { get; protected set; } = string.Empty; private INetworkProtocolSettings? settings; private UdpClient? receivingUdpClient; private UdpClient? sendingUdpClient; public UdpProtocol(ILogger logger, IMessenger messenger) : base(logger, messenger) { } protected override bool InternalConnect(IProtocolSettings settings) { // check if settings are of correct type if (settings is not INetworkProtocolSettings networkSettings) { this.settings = null; throw new ArgumentException($"Cannot connect due to wrong type of Protocol Settings. " + $"Check parameter {nameof(settings)}' of '{nameof(InternalConnect)}()' in {nameof(UdpProtocol)}."); } // store locally this.settings = networkSettings; // update identifier this.InstanceIdentifier = NetworkProtocolHelpers.GetLimitedLengthHostname(this.settings); // check if clients are null if(this.receivingUdpClient != null || this.sendingUdpClient != null) { throw new Exception($"A UDP client was not null when {nameof(InternalConnect)} was called: " + $"{nameof(receivingUdpClient)} isnull={this.receivingUdpClient == null}," + $"{nameof(sendingUdpClient)} isnull={this.sendingUdpClient == null}"); } /* create udp clients */ // try opening receiving udp socket try { this.receivingUdpClient = new UdpClient(this.settings.Port); } catch (Exception ex) { this.logger.LogException(ex, $"'{nameof(InternalConnect)}()'Opening Receiving UDP socket failed:", nameof(UdpProtocol)); // rollback this.InternalDisconnect(); return false; } // try opening sending udp socket try { this.sendingUdpClient = new UdpClient(); this.sendingUdpClient.Connect(this.settings.Hostname!, this.settings.Port); } catch (Exception ex) { this.logger.LogException(ex, $"'{nameof(InternalConnect)}()'Opening Sending UDP Socket failed:", nameof(UdpProtocol)); // rollback this.InternalDisconnect(); return false; } return true; } protected override void InternalDisconnect() { // close receiving udp client if(this.receivingUdpClient != null) { this.receivingUdpClient?.Close(); this.receivingUdpClient?.Dispose(); this.receivingUdpClient = null; } // close sending udp client if (this.sendingUdpClient != null) { this.sendingUdpClient?.Close(); this.sendingUdpClient?.Dispose(); this.sendingUdpClient = null; } // reset settings this.settings = null; } protected override void InternalRead(CancellationToken ct) { while(ct.IsCancellationRequested == false) { // if receiving Udp Client is null => break if (this.receivingUdpClient == null) { this.OnUnintentionallyDisconnected(); break; // break loop } // try receive message UdpReceiveResult receivedResult; try { receivedResult = this.receivingUdpClient.ReceiveAsync(ct).GetAwaiter().GetResult(); } catch (OperationCanceledException) // intentionally cancelled => just break { break; } catch (ObjectDisposedException objex) { this.logger.LogException(objex, $"ObjectDisposedException while sending data in {nameof(InternalRead)}", nameof(UdpProtocol)); this.OnUnintentionallyDisconnected(); break; // break loop } catch (Exception ex) { this.logger.LogException(ex, $"Exception while reading data in {nameof(InternalRead)}", nameof(UdpProtocol)); this.messenger.Send(new GenericUserInterfaceMessage($"UDP client ended reading on port " + $"{this.settings!.Port} because of exception.", MessageImportance.Medium)); break; // break loop } // any data received? byte[] receivedBytes = receivedResult.Buffer; if (receivedBytes.Length > 0) { foreach (byte b in receivedBytes) { // report received data this.OnReceivedData(new ExtendedByte(b)); } } } } protected override bool InternalSendBytes(byte[] bytes) { // if sending Udp Client is null => return error if (this.sendingUdpClient == null) { this.OnUnintentionallyDisconnected(); return false; } // check for empty bytes array if (bytes == null || bytes.Length == 0) { this.logger.LogWarn($"'{nameof(InternalSendBytes)}()' got null or empty bytes array.", nameof(UdpProtocol)); return false; } // try sending the data try { this.sendingUdpClient.Send(bytes, bytes.Length); } catch (ObjectDisposedException objex) { this.logger.LogException(objex, $"ObjectDisposedException while sending data in {nameof(InternalSendBytes)}", nameof(UdpProtocol)); this.OnUnintentionallyDisconnected(); return false; } catch (Exception ex) { this.logger.LogException(ex, $"Exception while sending data in {nameof(InternalSendBytes)}", nameof(UdpProtocol)); return false; } foreach (byte b in bytes) { // report sent data this.OnSentData(new ExtendedByte(b)); } return true; } }