//******************************************************************************************************// 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<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 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. Load more
//****************************************************************************************************** // 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 !MONO using System.Net.Security; using System.Security.Authentication; #endif using GSF.Configuration; using GSF.Diagnostics; using GSF.Threading; // ReSharper disable AccessToDisposedClosure namespace 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<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 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);