//******************************************************************************************************// UdpClient.cs - Gbtc//// Copyright © 2012, Grid Protection Alliance. All Rights Reserved.//// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See// the NOTICE file distributed with this work for additional information regarding copyright ownership.// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may// not use this file except in compliance with the License. You may obtain a copy of the License at://// http://www.opensource.org/licenses/MIT//// Unless agreed to in writing, the subject software distributed under the License is distributed on an// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the// License for the specific language governing permissions and limitations.//// Code Modification History:// ----------------------------------------------------------------------------------------------------// 07/06/2006 - Pinal C. Patel// Original version of source code generated.// 09/06/2006 - J. Ritchie Carroll// Added bypass optimizations for high-speed socket access.// 09/27/2007 - J. Ritchie Carroll// Added disconnect timeout overload.// 09/29/2008 - J. Ritchie Carroll// Converted to C#.// 07/08/2009 - J. Ritchie Carroll// Added WaitHandle return value from asynchronous connection.// 07/09/2009 - Pinal C. Patel// Modified to attempt resuming reception on SocketException for non-Handshake enabled connection.// 07/15/2009 - Pinal C. Patel// Modified Disconnect() to add error checking.// 07/17/2009 - Pinal C. Patel// Added support to specify a specific interface address on a multiple interface machine.// 07/20/2009 - Pinal C. Patel// Allowed for UDP endpoint to not be bound to a local interface by specifying -1 for port number.// 09/14/2009 - Stephen C. Wills// Added new header and license agreement.// 10/30/2009 - Pinal C. Patel// Added true multicast support by allowing for socket level subscription to a multicast group.// 11/17/2009 - Pinal C. Patel// Fixed a issue in the creation of random server endpoint when server endpoint information is // omitted from the ConnectionString.// 03/24/2010 - Pinal C. Patel// Updated the interpretation of server property in ConnectionString to correctly interpret // IPv6 IP addresses according to IETF - A Recommendation for IPv6 Address Text Representation.// 11/29/2010 - Pinal C. Patel// Corrected the implementation of ConnectAsync() method.// 02/13/2011 - Pinal C. Patel// Modified ConnectAsync() to handle loop-back address resolution failure on IPv6 enabled OSes.// 07/23/2012 - Stephen C. Wills// Performed a full refactor to use the SocketAsyncEventArgs API calls.// 10/31/2012 - Stephen C. Wills// Replaced single-threaded BlockingCollection pattern with asynchronous loop pattern.// 12/13/2012 - Starlynn Danyelle Gilliam// Modified Header.// 09/24/2015 - Allan V. Scheid// Fixed Mono socket error with System.Net.Sockets.Socket.IOControl method and SIO_UDP_CONNRESET// inside OpenPort().////****************************************************************************************************** using System;using System.Collections.Concurrent;using System.Collections.Generic;using System.ComponentModel;using System.Net;using System.Net.Sockets;using System.Text;using System.Text.RegularExpressions;using System.Threading;using GSF.Configuration;using GSF.Diagnostics;using GSF.IO;using GSF.Threading; namespace GSF.Communication{ /// <summary> /// Represents a UDP-based communication server. /// </summary> /// <remarks> /// <para> /// Use <see cref="UdpClient"/> when the primary purpose is to receive data. /// </para> /// <para> /// The <see cref="UdpClient.Client"/> socket can be bound to a specified interface on a machine with multiple interfaces by /// specifying the interface in the <see cref="ClientBase.ConnectionString"/> (Example: "Server=localhost:8888; Port=8989; Interface=127.0.0.1") /// </para> /// <para> /// The <see cref="UdpClient.Client"/> socket can be used just for transmitting data without being bound to a local interface /// by specifying -1 for the port number in the <see cref="ClientBase.ConnectionString"/> (Example: "Server=localhost:8888; Port=-1") /// </para> /// </remarks> /// <example> /// This example shows how to use the <see cref="UdpClient"/> component: /// <code> /// using System; /// using GSF; /// using GSF.Communication; /// using GSF.Security.Cryptography; /// using GSF.IO.Compression; /// /// class Program /// { /// static UdpClient s_client; /// /// static void Main(string[] args) /// { /// // Initialize the client. /// s_client = new UdpClient("Server=localhost:8888; Port=8989"); /// s_client.Handshake = false; /// s_client.ReceiveTimeout = -1; /// s_client.Encryption = CipherStrength.None; /// s_client.Compression = CompressionStrength.NoCompression; /// s_client.SecureSession = false; /// s_client.Initialize(); /// // Register event handlers. /// s_client.ConnectionAttempt += s_client_ConnectionAttempt; /// s_client.ConnectionEstablished += s_client_ConnectionEstablished; /// s_client.ConnectionTerminated += s_client_ConnectionTerminated; /// s_client.ReceiveDataComplete += s_client_ReceiveDataComplete; /// // Connect the client. /// s_client.Connect(); /// /// // Transmit user input to the server. /// string input; /// while (string.Compare(input = Console.ReadLine(), "Exit", true) != 0) /// { /// s_client.Send(input); /// } /// /// // Disconnect the client on shutdown. /// s_client.Dispose(); /// } /// /// static void s_client_ConnectionAttempt(object sender, EventArgs e) /// { /// Console.WriteLine("Client is connecting to server."); /// } /// /// static void s_client_ConnectionEstablished(object sender, EventArgs e) /// { /// Console.WriteLine("Client connected to server."); /// } /// /// static void s_client_ConnectionTerminated(object sender, EventArgs e) /// { /// Console.WriteLine("Client disconnected from server."); /// } /// /// static void s_client_ReceiveDataComplete(object sender, EventArgs<byte[], int> e) /// { /// Console.WriteLine(string.Format("Received data - {0}.", s_client.TextEncoding.GetString(e.Argument1, 0, e.Argument2))); /// } /// } /// </code> /// </example> // ReSharper disable UnusedVariable public class UdpClient : ClientBase { #region [ Members ] // Nested Types private class UdpClientPayload { public EndPoint Destination; public byte[] Data; public int Offset; public int Length; public ManualResetEventSlim WaitHandle; } // Constants /// <summary> /// Specifies the default value for the <see cref="ClientBase.ReceiveBufferSize"/> property. /// </summary> public new const int DefaultReceiveBufferSize = 65536; /// <summary> /// Specifies the default value for the <see cref="MaxPacketSize"/> property. /// </summary> public const int DefaultMaxPacketSize = 65536; /// <summary> /// Specifies the default value for the <see cref="AllowDualStackSocket"/> property. /// </summary> public const bool DefaultAllowDualStackSocket = true; /// <summary> /// Specifies the default value for the <see cref="MaxSendQueueSize"/> property. /// </summary> public const int DefaultMaxSendQueueSize = 500000; /// <summary> /// Specifies the default value for the <see cref="ClientBase.ConnectionString"/> property. /// </summary> public const string DefaultConnectionString = "Server=localhost:8888; Port=8989"; /// <summary> /// Specifies the constant to be used for disabling <see cref="SocketError.ConnectionReset"/> when endpoint is not listening. /// </summary> private const int SIO_UDP_CONNRESET = -1744830452; // Events /// <summary> /// Occurs when unprocessed data has been received from the server. /// </summary> /// <remarks> /// <para> /// This event can be used to receive a notification that server data has arrived. The <see cref="Read"/> method can then be used /// to copy data to an existing buffer. In many cases it will be optimal to use an existing buffer instead of subscribing to the /// <see cref="ReceiveDataFromComplete"/> event. /// </para> /// <para> /// <see cref="EventArgs{T1,T2}.Argument1"/> is the end-point that data has been received from.<br/> /// <see cref="EventArgs{T1,T2}.Argument2"/> is the number of bytes received in the buffer from the server. /// </para> /// </remarks> [Category("Data")] [Description("Occurs when unprocessed data has been received from the server.")] public event EventHandler<EventArgs<EndPoint, IPPacketInformation, int>> ReceiveDataFrom; /// <summary> /// Occurs when data received from the server has been processed and is ready for consumption. /// </summary> /// <remarks> /// <see cref="EventArgs{T1,T2,T3}.Argument1"/> is the end-point that data has been received from.<br/> /// <see cref="EventArgs{T1,T2,T3}.Argument2"/> is a new buffer containing post-processed data received from the server starting at index zero.<br/> /// <see cref="EventArgs{T1,T2,T3}.Argument3"/> is the number of post-processed bytes received in the buffer from the server. /// </remarks> [Category("Data")] [Description("Occurs when data received from the server has been processed and is ready for consumption.")] public event EventHandler<EventArgs<EndPoint, IPPacketInformation, byte[], int>> ReceiveDataFromComplete; // Fields //private bool m_destinationReachableCheck; private IPEndPoint m_udpServer; private TransportProvider<Socket> m_udpClient; private IPStack m_ipStack; private int m_maxPacketSize; private Dictionary<string, string> m_connectData; private ManualResetEvent m_connectionHandle; private Thread m_connectionThread; private int m_sending; private int m_receiving; private readonly object m_sendLock; private readonly ConcurrentQueue<UdpClientPayload> m_sendQueue; private readonly ShortSynchronizedOperation m_dumpPayloadsOperation; private SocketAsyncEventArgs m_sendArgs; private SocketAsyncEventArgs m_receiveArgs; private readonly EventHandler<SocketAsyncEventArgs> m_sendHandler; private readonly EventHandler<SocketAsyncEventArgs> m_receiveHandler; private bool m_receivePacketInfo; private bool m_disposed; #endregion #region [ Constructors ] /// <summary> /// Initializes a new instance of the <see cref="UdpClient"/> class. /// </summary> public UdpClient() : this(DefaultConnectionString) { } /// <summary> /// Initializes a new instance of the <see cref="UdpClient"/> class. /// </summary> /// <param name="connectString">Connect string of the <see cref="UdpClient"/>. See <see cref="DefaultConnectionString"/> for format.</param> public UdpClient(string connectString) : base(TransportProtocol.Udp, connectString) { base.ReceiveBufferSize = DefaultReceiveBufferSize; m_maxPacketSize = DefaultMaxPacketSize; AllowDualStackSocket = DefaultAllowDualStackSocket; MaxSendQueueSize = DefaultMaxSendQueueSize; m_sendLock = new object(); m_sendQueue = new ConcurrentQueue<UdpClientPayload>(); m_dumpPayloadsOperation = new ShortSynchronizedOperation(DumpPayloads, OnSendDataException); m_sendHandler += (sender, args) => ProcessSend(); m_receiveHandler += (sender, args) => ProcessReceive(); } /// <summary> /// Initializes a new instance of the <see cref="UdpClient"/> class. /// </summary> /// <param name="container"><see cref="IContainer"/> object that contains the <see cref="UdpClient"/>.</param> public UdpClient(IContainer container) : this() => container?.Add(this); #endregion #region [ Properties ] /// <summary> /// Gets or sets a boolean value that determines if dual-mode socket is allowed when endpoint address is IPv6. /// </summary> [Category("Settings")] [DefaultValue(DefaultAllowDualStackSocket)] [Description("Determines if dual-mode socket is allowed when endpoint address is IPv6.")] public bool AllowDualStackSocket { get; set; } /// <summary> /// Gets or sets the maximum size for the send queue before payloads are dumped from the queue. /// </summary> [Category("Settings")] [DefaultValue(DefaultMaxSendQueueSize)] [Description("The maximum size for the send queue before payloads are dumped from the queue.")] public int MaxSendQueueSize { get; set; } /// <summary> /// Gets the <see cref="Socket"/> object for the <see cref="UdpClient"/>. /// </summary> [Browsable(false)] public Socket Client => m_udpClient.Provider; /// <summary> /// Gets the server URI of the <see cref="UdpClient"/>. /// </summary> [Browsable(false)] public override string ServerUri { get { if (m_connectData is null) return $"{TransportProtocol}://0.0.0.0:0"; if (m_connectData.ContainsKey("server")) return $"{TransportProtocol}://{m_connectData["server"]}".ToLower(); if (!m_connectData.TryGetValue("interface", out string targetInterface) || string.IsNullOrWhiteSpace(targetInterface)) { IPStack ipStack = m_ipStack == IPStack.Default ? Transport.GetDefaultIPStack() : m_ipStack; targetInterface = ipStack == IPStack.IPv6 ? "[::0]" : "0.0.0.0"; } if (!m_connectData.TryGetValue("port", out string targetPort) || !ushort.TryParse(targetPort, out ushort port)) port = 0; return $"{TransportProtocol}://{targetInterface}:{port}".ToLower(); } } /// <summary> /// Gets or sets the size of the buffer used by the client for receiving data from the server. /// </summary> /// <exception cref="ArgumentException">The value being assigned is either zero or negative.</exception> public override int ReceiveBufferSize { get => base.ReceiveBufferSize; set { base.ReceiveBufferSize = value; if (m_udpClient?.Provider != null) m_udpClient.Provider.ReceiveBufferSize = value; } } /// <summary> /// Gets or sets the maximum expected size for packets being received by this <see cref="UdpClient"/>. /// </summary> public int MaxPacketSize { get => m_maxPacketSize; set { m_maxPacketSize = value; m_udpClient?.SetReceiveBuffer(value); } } /// <summary> /// Gets or sets the flag that determines whether the UDP client /// should attempt to receive packet info when receiving data /// from the socket. /// </summary> public bool ReceivePacketInfo { get { return m_receivePacketInfo; } set { #if MONO if (value) throw new NotImplementedException("Not supported under Mono."); m_receivePacketInfo = false; #else m_receivePacketInfo = value; m_udpClient?.Provider?.SetSocketOption(m_udpClient.Provider.AddressFamily == AddressFamily.InterNetworkV6 ? SocketOptionLevel.IPv6 : SocketOptionLevel.IP, SocketOptionName.PacketInformation, value); #endif } } /// <summary> /// Determines whether the base class should track statistics. /// </summary> protected override bool TrackStatistics => false; /// <summary> /// Gets the descriptive status of the client. /// </summary> public override string Status { get { StringBuilder statusBuilder = new StringBuilder(base.Status); if (m_sendQueue != null) { statusBuilder.AppendFormat(" Queued payloads: {0}", m_sendQueue.Count); statusBuilder.AppendLine(); } return statusBuilder.ToString(); } } #endregion #region [ Methods ] /// <summary> /// Reads a number of bytes from the current received data buffer and writes those bytes into a byte array at the specified offset. /// </summary> /// <param name="buffer">Destination buffer used to hold copied bytes.</param> /// <param name="startIndex">0-based starting index into destination <paramref name="buffer"/> to begin writing data.</param> /// <param name="length">The number of bytes to read from current received data buffer and write into <paramref name="buffer"/>.</param> /// <returns>The number of bytes read.</returns> /// <remarks> /// This function should only be called from within the <see cref="ClientBase.ReceiveData"/> event handler. Calling this method outside /// this event will have unexpected results. /// </remarks> /// <exception cref="InvalidOperationException">No received data buffer has been defined to read.</exception> /// <exception cref="ArgumentNullException"><paramref name="buffer"/> is null.</exception> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="startIndex"/> or <paramref name="length"/> is less than 0 -or- /// <paramref name="startIndex"/> and <paramref name="length"/> will exceed <paramref name="buffer"/> length. /// </exception> public override int Read(byte[] buffer, int startIndex, int length) { buffer.ValidateParameters(startIndex, length); if (m_udpClient.ReceiveBuffer == null) throw new InvalidOperationException("No received data buffer has been defined to read."); int sourceLength = m_udpClient.BytesReceived - ReadIndex; int readBytes = length > sourceLength ? sourceLength : length; Buffer.BlockCopy(m_udpClient.ReceiveBuffer, ReadIndex, buffer, startIndex, readBytes); // Update read index for next call ReadIndex += readBytes; if (ReadIndex >= m_udpClient.BytesReceived) ReadIndex = 0; return readBytes; } /// <summary> /// Saves <see cref="TcpServer"/> settings to the config file if the <see cref="ServerBase.PersistSettings"/> property is set to true. /// </summary> public override void SaveSettings() { base.SaveSettings(); if (!PersistSettings) return; // Save settings under the specified category. ConfigurationFile config = ConfigurationFile.Current; CategorizedSettingsElementCollection settings = config.Settings[SettingsCategory]; settings["AllowDualStackSocket", true].Update(AllowDualStackSocket); settings["MaxSendQueueSize", true].Update(MaxSendQueueSize); config.Save(); } /// <summary> /// Loads saved <see cref="TcpServer"/> settings from the config file if the <see cref="ServerBase.PersistSettings"/> property is set to true. /// </summary> public override void LoadSettings() { base.LoadSettings(); if (!PersistSettings) return; // Load settings from the specified category.Lines 1–500 of 1256. Load more
//****************************************************************************************************** // UdpClient.cs - Gbtc // // Copyright © 2012, Grid Protection Alliance. All Rights Reserved. // // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See // the NOTICE file distributed with this work for additional information regarding copyright ownership. // The GPA licenses this file to you under the MIT License (MIT), the "License"; you may // not use this file except in compliance with the License. You may obtain a copy of the License at: // // http://www.opensource.org/licenses/MIT // // Unless agreed to in writing, the subject software distributed under the License is distributed on an // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the // License for the specific language governing permissions and limitations. // // Code Modification History: // ---------------------------------------------------------------------------------------------------- // 07/06/2006 - Pinal C. Patel // Original version of source code generated. // 09/06/2006 - J. Ritchie Carroll // Added bypass optimizations for high-speed socket access. // 09/27/2007 - J. Ritchie Carroll // Added disconnect timeout overload. // 09/29/2008 - J. Ritchie Carroll // Converted to C#. // 07/08/2009 - J. Ritchie Carroll // Added WaitHandle return value from asynchronous connection. // 07/09/2009 - Pinal C. Patel // Modified to attempt resuming reception on SocketException for non-Handshake enabled connection. // 07/15/2009 - Pinal C. Patel // Modified Disconnect() to add error checking. // 07/17/2009 - Pinal C. Patel // Added support to specify a specific interface address on a multiple interface machine. // 07/20/2009 - Pinal C. Patel // Allowed for UDP endpoint to not be bound to a local interface by specifying -1 for port number. // 09/14/2009 - Stephen C. Wills // Added new header and license agreement. // 10/30/2009 - Pinal C. Patel // Added true multicast support by allowing for socket level subscription to a multicast group. // 11/17/2009 - Pinal C. Patel // Fixed a issue in the creation of random server endpoint when server endpoint information is // omitted from the ConnectionString. // 03/24/2010 - Pinal C. Patel // Updated the interpretation of server property in ConnectionString to correctly interpret // IPv6 IP addresses according to IETF - A Recommendation for IPv6 Address Text Representation. // 11/29/2010 - Pinal C. Patel // Corrected the implementation of ConnectAsync() method. // 02/13/2011 - Pinal C. Patel // Modified ConnectAsync() to handle loop-back address resolution failure on IPv6 enabled OSes. // 07/23/2012 - Stephen C. Wills // Performed a full refactor to use the SocketAsyncEventArgs API calls. // 10/31/2012 - Stephen C. Wills // Replaced single-threaded BlockingCollection pattern with asynchronous loop pattern. // 12/13/2012 - Starlynn Danyelle Gilliam // Modified Header. // 09/24/2015 - Allan V. Scheid // Fixed Mono socket error with System.Net.Sockets.Socket.IOControl method and SIO_UDP_CONNRESET // inside OpenPort(). // //****************************************************************************************************** using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Net; using System.Net.Sockets; using System.Text; using System.Text.RegularExpressions; using System.Threading; using GSF.Configuration; using GSF.Diagnostics; using GSF.IO; using GSF.Threading; namespace GSF.Communication { /// <summary> /// Represents a UDP-based communication server. /// </summary> /// <remarks> /// <para> /// Use <see cref="UdpClient"/> when the primary purpose is to receive data. /// </para> /// <para> /// The <see cref="UdpClient.Client"/> socket can be bound to a specified interface on a machine with multiple interfaces by /// specifying the interface in the <see cref="ClientBase.ConnectionString"/> (Example: "Server=localhost:8888; Port=8989; Interface=127.0.0.1") /// </para> /// <para> /// The <see cref="UdpClient.Client"/> socket can be used just for transmitting data without being bound to a local interface /// by specifying -1 for the port number in the <see cref="ClientBase.ConnectionString"/> (Example: "Server=localhost:8888; Port=-1") /// </para> /// </remarks> /// <example> /// This example shows how to use the <see cref="UdpClient"/> component: /// <code> /// using System; /// using GSF; /// using GSF.Communication; /// using GSF.Security.Cryptography; /// using GSF.IO.Compression; /// /// class Program /// { /// static UdpClient s_client; /// /// static void Main(string[] args) /// { /// // Initialize the client. /// s_client = new UdpClient("Server=localhost:8888; Port=8989"); /// s_client.Handshake = false; /// s_client.ReceiveTimeout = -1; /// s_client.Encryption = CipherStrength.None; /// s_client.Compression = CompressionStrength.NoCompression; /// s_client.SecureSession = false; /// s_client.Initialize(); /// // Register event handlers. /// s_client.ConnectionAttempt += s_client_ConnectionAttempt; /// s_client.ConnectionEstablished += s_client_ConnectionEstablished; /// s_client.ConnectionTerminated += s_client_ConnectionTerminated; /// s_client.ReceiveDataComplete += s_client_ReceiveDataComplete; /// // Connect the client. /// s_client.Connect(); /// /// // Transmit user input to the server. /// string input; /// while (string.Compare(input = Console.ReadLine(), "Exit", true) != 0) /// { /// s_client.Send(input); /// } /// /// // Disconnect the client on shutdown. /// s_client.Dispose(); /// } /// /// static void s_client_ConnectionAttempt(object sender, EventArgs e) /// { /// Console.WriteLine("Client is connecting to server."); /// } /// /// static void s_client_ConnectionEstablished(object sender, EventArgs e) /// { /// Console.WriteLine("Client connected to server."); /// } /// /// static void s_client_ConnectionTerminated(object sender, EventArgs e) /// { /// Console.WriteLine("Client disconnected from server."); /// } /// /// static void s_client_ReceiveDataComplete(object sender, EventArgs<byte[], int> e) /// { /// Console.WriteLine(string.Format("Received data - {0}.", s_client.TextEncoding.GetString(e.Argument1, 0, e.Argument2))); /// } /// } /// </code> /// </example> // ReSharper disable UnusedVariable public class UdpClient : ClientBase { #region [ Members ] // Nested Types private class UdpClientPayload { public EndPoint Destination; public byte[] Data; public int Offset; public int Length; public ManualResetEventSlim WaitHandle; } // Constants /// <summary> /// Specifies the default value for the <see cref="ClientBase.ReceiveBufferSize"/> property. /// </summary> public new const int DefaultReceiveBufferSize = 65536; /// <summary> /// Specifies the default value for the <see cref="MaxPacketSize"/> property. /// </summary> public const int DefaultMaxPacketSize = 65536; /// <summary> /// Specifies the default value for the <see cref="AllowDualStackSocket"/> property. /// </summary> public const bool DefaultAllowDualStackSocket = true; /// <summary> /// Specifies the default value for the <see cref="MaxSendQueueSize"/> property. /// </summary> public const int DefaultMaxSendQueueSize = 500000; /// <summary> /// Specifies the default value for the <see cref="ClientBase.ConnectionString"/> property. /// </summary> public const string DefaultConnectionString = "Server=localhost:8888; Port=8989"; /// <summary> /// Specifies the constant to be used for disabling <see cref="SocketError.ConnectionReset"/> when endpoint is not listening. /// </summary> private const int SIO_UDP_CONNRESET = -1744830452; // Events /// <summary> /// Occurs when unprocessed data has been received from the server. /// </summary> /// <remarks> /// <para> /// This event can be used to receive a notification that server data has arrived. The <see cref="Read"/> method can then be used /// to copy data to an existing buffer. In many cases it will be optimal to use an existing buffer instead of subscribing to the /// <see cref="ReceiveDataFromComplete"/> event. /// </para> /// <para> /// <see cref="EventArgs{T1,T2}.Argument1"/> is the end-point that data has been received from.<br/> /// <see cref="EventArgs{T1,T2}.Argument2"/> is the number of bytes received in the buffer from the server. /// </para> /// </remarks> [Category("Data")] [Description("Occurs when unprocessed data has been received from the server.")] public event EventHandler<EventArgs<EndPoint, IPPacketInformation, int>> ReceiveDataFrom; /// <summary> /// Occurs when data received from the server has been processed and is ready for consumption. /// </summary> /// <remarks> /// <see cref="EventArgs{T1,T2,T3}.Argument1"/> is the end-point that data has been received from.<br/> /// <see cref="EventArgs{T1,T2,T3}.Argument2"/> is a new buffer containing post-processed data received from the server starting at index zero.<br/> /// <see cref="EventArgs{T1,T2,T3}.Argument3"/> is the number of post-processed bytes received in the buffer from the server. /// </remarks> [Category("Data")] [Description("Occurs when data received from the server has been processed and is ready for consumption.")] public event EventHandler<EventArgs<EndPoint, IPPacketInformation, byte[], int>> ReceiveDataFromComplete; // Fields //private bool m_destinationReachableCheck; private IPEndPoint m_udpServer; private TransportProvider<Socket> m_udpClient; private IPStack m_ipStack; private int m_maxPacketSize; private Dictionary<string, string> m_connectData; private ManualResetEvent m_connectionHandle; private Thread m_connectionThread; private int m_sending; private int m_receiving; private readonly object m_sendLock; private readonly ConcurrentQueue<UdpClientPayload> m_sendQueue; private readonly ShortSynchronizedOperation m_dumpPayloadsOperation; private SocketAsyncEventArgs m_sendArgs; private SocketAsyncEventArgs m_receiveArgs; private readonly EventHandler<SocketAsyncEventArgs> m_sendHandler; private readonly EventHandler<SocketAsyncEventArgs> m_receiveHandler; private bool m_receivePacketInfo; private bool m_disposed; #endregion #region [ Constructors ] /// <summary> /// Initializes a new instance of the <see cref="UdpClient"/> class. /// </summary> public UdpClient() : this(DefaultConnectionString) { } /// <summary> /// Initializes a new instance of the <see cref="UdpClient"/> class. /// </summary> /// <param name="connectString">Connect string of the <see cref="UdpClient"/>. See <see cref="DefaultConnectionString"/> for format.</param> public UdpClient(string connectString) : base(TransportProtocol.Udp, connectString) { base.ReceiveBufferSize = DefaultReceiveBufferSize; m_maxPacketSize = DefaultMaxPacketSize; AllowDualStackSocket = DefaultAllowDualStackSocket; MaxSendQueueSize = DefaultMaxSendQueueSize; m_sendLock = new object(); m_sendQueue = new ConcurrentQueue<UdpClientPayload>(); m_dumpPayloadsOperation = new ShortSynchronizedOperation(DumpPayloads, OnSendDataException); m_sendHandler += (sender, args) => ProcessSend(); m_receiveHandler += (sender, args) => ProcessReceive(); } /// <summary> /// Initializes a new instance of the <see cref="UdpClient"/> class. /// </summary> /// <param name="container"><see cref="IContainer"/> object that contains the <see cref="UdpClient"/>.</param> public UdpClient(IContainer container) : this() => container?.Add(this); #endregion #region [ Properties ] /// <summary> /// Gets or sets a boolean value that determines if dual-mode socket is allowed when endpoint address is IPv6. /// </summary> [Category("Settings")] [DefaultValue(DefaultAllowDualStackSocket)] [Description("Determines if dual-mode socket is allowed when endpoint address is IPv6.")] public bool AllowDualStackSocket { get; set; } /// <summary> /// Gets or sets the maximum size for the send queue before payloads are dumped from the queue. /// </summary> [Category("Settings")] [DefaultValue(DefaultMaxSendQueueSize)] [Description("The maximum size for the send queue before payloads are dumped from the queue.")] public int MaxSendQueueSize { get; set; } /// <summary> /// Gets the <see cref="Socket"/> object for the <see cref="UdpClient"/>. /// </summary> [Browsable(false)] public Socket Client => m_udpClient.Provider; /// <summary> /// Gets the server URI of the <see cref="UdpClient"/>. /// </summary> [Browsable(false)] public override string ServerUri { get { if (m_connectData is null) return $"{TransportProtocol}://0.0.0.0:0"; if (m_connectData.ContainsKey("server")) return $"{TransportProtocol}://{m_connectData["server"]}".ToLower(); if (!m_connectData.TryGetValue("interface", out string targetInterface) || string.IsNullOrWhiteSpace(targetInterface)) { IPStack ipStack = m_ipStack == IPStack.Default ? Transport.GetDefaultIPStack() : m_ipStack; targetInterface = ipStack == IPStack.IPv6 ? "[::0]" : "0.0.0.0"; } if (!m_connectData.TryGetValue("port", out string targetPort) || !ushort.TryParse(targetPort, out ushort port)) port = 0; return $"{TransportProtocol}://{targetInterface}:{port}".ToLower(); } } /// <summary> /// Gets or sets the size of the buffer used by the client for receiving data from the server. /// </summary> /// <exception cref="ArgumentException">The value being assigned is either zero or negative.</exception> public override int ReceiveBufferSize { get => base.ReceiveBufferSize; set { base.ReceiveBufferSize = value; if (m_udpClient?.Provider != null) m_udpClient.Provider.ReceiveBufferSize = value; } } /// <summary> /// Gets or sets the maximum expected size for packets being received by this <see cref="UdpClient"/>. /// </summary> public int MaxPacketSize { get => m_maxPacketSize; set { m_maxPacketSize = value; m_udpClient?.SetReceiveBuffer(value); } } /// <summary> /// Gets or sets the flag that determines whether the UDP client /// should attempt to receive packet info when receiving data /// from the socket. /// </summary> public bool ReceivePacketInfo { get { return m_receivePacketInfo; } set { #if MONO if (value) throw new NotImplementedException("Not supported under Mono."); m_receivePacketInfo = false; #else m_receivePacketInfo = value; m_udpClient?.Provider?.SetSocketOption(m_udpClient.Provider.AddressFamily == AddressFamily.InterNetworkV6 ? SocketOptionLevel.IPv6 : SocketOptionLevel.IP, SocketOptionName.PacketInformation, value); #endif } } /// <summary> /// Determines whether the base class should track statistics. /// </summary> protected override bool TrackStatistics => false; /// <summary> /// Gets the descriptive status of the client. /// </summary> public override string Status { get { StringBuilder statusBuilder = new StringBuilder(base.Status); if (m_sendQueue != null) { statusBuilder.AppendFormat(" Queued payloads: {0}", m_sendQueue.Count); statusBuilder.AppendLine(); } return statusBuilder.ToString(); } } #endregion #region [ Methods ] /// <summary> /// Reads a number of bytes from the current received data buffer and writes those bytes into a byte array at the specified offset. /// </summary> /// <param name="buffer">Destination buffer used to hold copied bytes.</param> /// <param name="startIndex">0-based starting index into destination <paramref name="buffer"/> to begin writing data.</param> /// <param name="length">The number of bytes to read from current received data buffer and write into <paramref name="buffer"/>.</param> /// <returns>The number of bytes read.</returns> /// <remarks> /// This function should only be called from within the <see cref="ClientBase.ReceiveData"/> event handler. Calling this method outside /// this event will have unexpected results. /// </remarks> /// <exception cref="InvalidOperationException">No received data buffer has been defined to read.</exception> /// <exception cref="ArgumentNullException"><paramref name="buffer"/> is null.</exception> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="startIndex"/> or <paramref name="length"/> is less than 0 -or- /// <paramref name="startIndex"/> and <paramref name="length"/> will exceed <paramref name="buffer"/> length. /// </exception> public override int Read(byte[] buffer, int startIndex, int length) { buffer.ValidateParameters(startIndex, length); if (m_udpClient.ReceiveBuffer == null) throw new InvalidOperationException("No received data buffer has been defined to read."); int sourceLength = m_udpClient.BytesReceived - ReadIndex; int readBytes = length > sourceLength ? sourceLength : length; Buffer.BlockCopy(m_udpClient.ReceiveBuffer, ReadIndex, buffer, startIndex, readBytes); // Update read index for next call ReadIndex += readBytes; if (ReadIndex >= m_udpClient.BytesReceived) ReadIndex = 0; return readBytes; } /// <summary> /// Saves <see cref="TcpServer"/> settings to the config file if the <see cref="ServerBase.PersistSettings"/> property is set to true. /// </summary> public override void SaveSettings() { base.SaveSettings(); if (!PersistSettings) return; // Save settings under the specified category. ConfigurationFile config = ConfigurationFile.Current; CategorizedSettingsElementCollection settings = config.Settings[SettingsCategory]; settings["AllowDualStackSocket", true].Update(AllowDualStackSocket); settings["MaxSendQueueSize", true].Update(MaxSendQueueSize); config.Save(); } /// <summary> /// Loads saved <see cref="TcpServer"/> settings from the config file if the <see cref="ServerBase.PersistSettings"/> property is set to true. /// </summary> public override void LoadSettings() { base.LoadSettings(); if (!PersistSettings) return; // Load settings from the specified category.