1646 lines · 67.4 KB · C#
RawDownload
//******************************************************************************************************//  TcpClient.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://  ----------------------------------------------------------------------------------------------------//  06/02/2006 - Pinal C. Patel//       Original version of source code generated.//  09/06/2006 - J. Ritchie Carroll//       Added bypass optimizations for high-speed socket access.//  12/01/2006 - Pinal C. Patel//       Modified code for handling "PayloadAware" transmissions.//  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/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.//  09/14/2009 - Stephen C. Wills//       Added new header and license agreement.//  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/11/2011 - Pinal C. Patel//       Added IntegratedSecurity property to enable integrated windows authentication.//  02/13/2011 - Pinal C. Patel//       Modified ConnectAsync() to handle loopback address resolution failure on IPv6 enabled OSes.//  09/21/2011 - J. Ritchie Carroll//       Added Mono implementation exception regions.//  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.////****************************************************************************************************** using System;using System.Collections.Concurrent;using System.Collections.Generic;using System.ComponentModel;using System.Linq;using System.Net;using System.Net.Sockets;using System.Text;using System.Text.RegularExpressions;using System.Threading;#if !MONOusing System.Net.Security;using System.Security.Authentication;#endifusing GSF.Configuration;using GSF.Diagnostics;using GSF.Threading; // ReSharper disable AccessToDisposedClosurenamespace GSF.Communication{    /// <summary>    /// Represents a TCP-based communication client.    /// </summary>    /// <remarks>    /// The socket can be bound to a specified interface on a machine with multiple interfaces by     /// specifying the IP of the interface in the <see cref="ClientBase.ConnectionString"/>    /// (Example: "Server=localhost:8888; Interface=192.168.1.15")    /// </remarks>    /// <example>    /// This example shows how to use the <see cref="TcpClient"/> component:    /// <code>    /// using System;    /// using GSF;    /// using GSF.Communication;    /// using GSF.Security.Cryptography;    /// using GSF.IO.Compression;    ///     /// class Program    /// {    ///     static TcpClient s_client;    ///     ///     static void Main(string[] args)    ///     {    ///         // Initialize the client.    ///         s_client = new TcpClient("Server=localhost:8888");    ///         s_client.Handshake = false;    ///         s_client.PayloadAware = false;    ///         s_client.ReceiveTimeout = -1;    ///         s_client.MaxConnectionAttempts = 5;    ///         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 TcpClient : ClientBase    {        #region [ Members ]         // Nested Types        private sealed class ConnectState : IDisposable        {            public Socket Socket;        #if !MONO            public NetworkStream NetworkStream;            public NegotiateStream NegotiateStream;        #endif             public readonly SocketAsyncEventArgs ConnectArgs = new SocketAsyncEventArgs();            public SocketAsyncEventArgs ReceiveArgs;            public SocketAsyncEventArgs SendArgs;            public int ConnectionAttempts;             public readonly CancellationToken Token = new CancellationToken();            public ICancellationToken TimeoutToken;             public void Dispose()            {                ConnectArgs.Dispose();                ReceiveArgs?.Dispose();                SendArgs?.Dispose();                Socket?.Dispose();            #if !MONO                NetworkStream?.Dispose();                NegotiateStream?.Dispose();            #endif            }        }         private sealed class ReceiveState : IDisposable        {            public Socket Socket;            public SocketAsyncEventArgs ReceiveArgs;            public SocketAsyncEventArgs SendArgs;             public byte[] Buffer;            public int Offset;            public int PayloadLength = -1;             public CancellationToken Token;             public void Dispose()            {                Dispose(Socket);                Dispose(ReceiveArgs);                Dispose(SendArgs);            }             private static void Dispose(IDisposable obj) => obj?.Dispose();        }         private sealed class SendState : IDisposable        {            public Socket Socket;            public SocketAsyncEventArgs ReceiveArgs;            public SocketAsyncEventArgs SendArgs;             public readonly ConcurrentQueue<TcpClientPayload> SendQueue = new ConcurrentQueue<TcpClientPayload>();            public TcpClientPayload Payload;            public int Sending;             public CancellationToken Token;             public void Dispose()            {                Dispose(Socket);                Dispose(ReceiveArgs);                Dispose(SendArgs);                 while (SendQueue.TryDequeue(out TcpClientPayload payload))                {                    payload.WaitHandle.Set();                    payload.WaitHandle.Dispose();                }            }             private static void Dispose(IDisposable obj) => obj?.Dispose();        }        private class TcpClientPayload        {            public byte[] Data;            public int Offset;            public int Length;            public ManualResetEvent WaitHandle;        }         private class CancellationToken        {            private int m_cancelled;             public bool Cancelled => Interlocked.CompareExchange(ref m_cancelled, 0, 0) != 0;             public bool Cancel() => Interlocked.Exchange(ref m_cancelled, 1) != 0;        }         // Constants         /// <summary>        /// Specifies the default value for the <see cref="PayloadAware"/> property.        /// </summary>        public const bool DefaultPayloadAware = false;         /// <summary>        /// Specifies the default value for the <see cref="IntegratedSecurity"/> property.        /// </summary>        public const bool DefaultIntegratedSecurity = false;         /// <summary>        /// Specifies the default value for the <see cref="IgnoreInvalidCredentials"/> property.        /// </summary>        public const bool DefaultIgnoreInvalidCredentials = false;         /// <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="NoDelay"/> property.        /// </summary>        public const bool DefaultNoDelay = false;         /// <summary>        /// Specifies the default value for the <see cref="ClientBase.ConnectionString"/> property.        /// </summary>        public const string DefaultConnectionString = "Server=localhost:8888";         // Fields        private byte[] m_payloadMarker;        private EndianOrder m_payloadEndianOrder;        private IPStack m_ipStack;        private readonly ShortSynchronizedOperation m_dumpPayloadsOperation;        private string[] m_serverList;        private Dictionary<string, string> m_connectData;        private ManualResetEvent m_connectWaitHandle;        private ConnectState m_connectState;        private ReceiveState m_receiveState;        private SendState m_sendState;        private bool m_disposed;         #endregion         #region [ Constructors ]         /// <summary>        /// Initializes a new instance of the <see cref="TcpClient"/> class.        /// </summary>        public TcpClient() : this(DefaultConnectionString)        {        }         /// <summary>        /// Initializes a new instance of the <see cref="TcpClient"/> class.        /// </summary>        /// <param name="connectString">Connect string of the <see cref="TcpClient"/>. See <see cref="DefaultConnectionString"/> for format.</param>        public TcpClient(string connectString) : base(TransportProtocol.Tcp, connectString)        {            PayloadAware = DefaultPayloadAware;            m_payloadMarker = Payload.DefaultMarker;            m_payloadEndianOrder = EndianOrder.LittleEndian;            IntegratedSecurity = DefaultIntegratedSecurity;            IgnoreInvalidCredentials = DefaultIgnoreInvalidCredentials;            AllowDualStackSocket = DefaultAllowDualStackSocket;            MaxSendQueueSize = DefaultMaxSendQueueSize;            NoDelay = DefaultNoDelay;            m_dumpPayloadsOperation = new ShortSynchronizedOperation(DumpPayloads, OnSendDataException);        }         /// <summary>        /// Initializes a new instance of the <see cref="TcpClient"/> class.        /// </summary>        /// <param name="container"><see cref="IContainer"/> object that contains the <see cref="TcpClient"/>.</param>        public TcpClient(IContainer container) : this() => container?.Add(this);         #endregion         #region [ Properties ]         /// <summary>        /// Gets or sets a boolean value that indicates whether the payload boundaries are to be preserved during transmission.        /// </summary>        [Category("Data")]        [DefaultValue(DefaultPayloadAware)]        [Description("Indicates whether the payload boundaries are to be preserved during transmission.")]        public bool PayloadAware { get; set; }         /// <summary>        /// Gets or sets the byte sequence used to mark the beginning of a payload in a <see cref="PayloadAware"/> transmission.        /// </summary>        /// <remarks>        /// Setting property to <c>null</c> will create a zero-length payload marker.        /// </remarks>        [Browsable(false)]        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]        public byte[] PayloadMarker        {            get => m_payloadMarker;            set => m_payloadMarker = value ?? Array.Empty<byte>();        }         /// <summary>        /// Gets or sets the endian order to apply for encoding and decoding payload size in a <see cref="PayloadAware"/> transmission.        /// </summary>        /// <remarks>        /// Setting property to <c>null</c> will force use of little-endian encoding.        /// </remarks>        [Browsable(false)]        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]        public EndianOrder PayloadEndianOrder        {            get => m_payloadEndianOrder;            set => m_payloadEndianOrder = value ?? EndianOrder.LittleEndian;        }         /// <summary>        /// Gets or sets a boolean value that indicates whether the current Windows account credentials are used for authentication.        /// </summary>        /// <remarks>           /// This option is ignored under Mono deployments.        /// </remarks>        [Category("Security")]        [DefaultValue(DefaultIntegratedSecurity)]        [Description("Indicates whether the current Windows account credentials are used for authentication.")]        public bool IntegratedSecurity { get; set; }         /// <summary>        /// Gets or sets a boolean value that indicates whether the server        /// should ignore errors when the client's credentials are invalid.        /// </summary>        /// <remarks>        /// This property should only be set to true if there is an alternative by which        /// to authenticate the client when integrated security fails.        /// </remarks>        [Category("Security")]        [DefaultValue(DefaultIgnoreInvalidCredentials)]        [Description("Indicates whether the client Windows account credentials are validated during authentication.")]        public bool IgnoreInvalidCredentials { get; set; }         /// <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 or sets a boolean value that determines if small packets are delivered to the remote host without delay.        /// </summary>        [Category("Settings")]        [DefaultValue(DefaultNoDelay)]        [Description("Determines if small packets are delivered to the remote host without delay.")]        public bool NoDelay { get; set; }         /// <summary>        /// Gets the <see cref="Socket"/> object for the <see cref="TcpClient"/>.        /// </summary>        [Browsable(false)]        public Socket Client => m_connectState?.Socket;         /// <summary>        /// Gets the server URI of the <see cref="TcpClient"/>.        /// </summary>        [Browsable(false)]        public override string ServerUri => $"{TransportProtocol}://{ServerList[ServerIndex]}".ToLower();         /// <summary>        /// Gets or sets network credential that is used when        /// <see cref="IntegratedSecurity"/> is set to <c>true</c>.        /// </summary>        public NetworkCredential NetworkCredential { get; set; }         /// <summary>        /// Determines whether the base class should track statistics.        /// </summary>        protected override bool TrackStatistics => false;         // Gets server connect data as an array - will always be at least one empty string, not null        private string[] ServerList        {            get            {                if (!(m_serverList is null))                    return m_serverList;                 if (m_connectData is null || !m_connectData.TryGetValue("server", out string serverList) || string.IsNullOrWhiteSpace(serverList))                    return Array.Empty<string>();                 return m_serverList = serverList.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(server => server.Trim()).ToArray();            }        }         /// <summary>        /// Gets the descriptive status of the client.        /// </summary>        public override string Status        {            get            {                SendState sendState = m_sendState;                StringBuilder statusBuilder = new StringBuilder(base.Status);                 if (sendState != null)                {                    statusBuilder.AppendFormat("           Queued payloads: {0}", sendState.SendQueue.Count);                    statusBuilder.AppendLine();                }                 return statusBuilder.ToString();            }        }         #endregion         #region [ Methods ]         /// <summary>        /// Saves <see cref="TcpClient"/> settings to the config file if the <see cref="ClientBase.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["PayloadAware", true].Update(PayloadAware);

Lines 1–500 of 1646.

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