1256 lines · 54.8 KB · C#
RawDownload
//******************************************************************************************************//  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&lt;byte[], int&gt; 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.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t