/// <summary>
 /// Преобразует строку в экземпляр класса <see cref="Socks4aProxyClient"/>. Возвращает значение, указывающее, успешно ли выполнено преобразование.
 /// </summary>
 /// <param name="proxyAddress">Строка вида - хост:порт:имя_пользователя:пароль. Три последних параметра являются необязательными.</param>
 /// <param name="result">Если преобразование выполнено успешно, то содержит экземпляр класса <see cref="Socks4aProxyClient"/>, иначе <see langword="null"/>.</param>
 /// <returns>Значение <see langword="true"/>, если параметр <paramref name="proxyAddress"/> преобразован успешно, иначе <see langword="false"/>.</returns>
 public static bool TryParse(string proxyAddress, out Socks4aProxyClient result)
 {
     if (ProxyClient.TryParse(ProxyType.Socks4a, proxyAddress, out ProxyClient proxy))
     {
         result = proxy as Socks4aProxyClient;
         return(true);
     }
     else
     {
         result = null;
         return(false);
     }
 }
        /// <summary>
        /// Добавляет в цепочку новый прокси-клиент.
        /// </summary>
        /// <param name="proxy">Добавляемый прокси-клиент.</param>
        /// <exception cref="System.ArgumentNullException">Значение параметра <paramref name="proxy"/> равно <see langword="null"/>.</exception>
        public void AddProxy(ProxyClient proxy)
        {
            #region Проверка параметров

            if (proxy == null)
            {
                throw new ArgumentNullException("proxy");
            }

            #endregion

            _proxies.Add(proxy);
        }
        /// <summary>
        /// Создаёт соединение с сервером через цепочку прокси-серверов.
        /// </summary>
        /// <param name="destinationHost">Хост сервера, с которым нужно связаться через прокси-сервер.</param>
        /// <param name="destinationPort">Порт сервера, с которым нужно связаться через прокси-сервер.</param>
        /// <param name="tcpClient">Соединение, через которое нужно работать, или значение <see langword="null"/>.</param>
        /// <returns>Соединение с сервером через цепочку прокси-серверов.</returns>
        /// <exception cref="System.InvalidOperationException">
        /// Количество прокси-серверов равно 0.
        /// -или-
        /// Значение свойства <see cref="Host"/> равно <see langword="null"/> или имеет нулевую длину.
        /// -или-
        /// Значение свойства <see cref="Port"/> меньше 1 или больше 65535.
        /// -или-
        /// Значение свойства <see cref="Username"/> имеет длину более 255 символов.
        /// -или-
        /// Значение свойства <see cref="Password"/> имеет длину более 255 символов.
        /// </exception>
        /// <exception cref="System.ArgumentNullException">Значение параметра <paramref name="destinationHost"/> равно <see langword="null"/>.</exception>
        /// <exception cref="System.ArgumentException">Значение параметра <paramref name="destinationHost"/> является пустой строкой.</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">Значение параметра <paramref name="destinationPort"/> меньше 1 или больше 65535.</exception>
        /// <exception cref="xNetStandard.Net.ProxyException">Ошибка при работе с прокси-сервером.</exception>
        public override TcpClient CreateConnection(string destinationHost, int destinationPort, TcpClient tcpClient = null)
        {
            #region Проверка состояния

            if (_proxies.Count == 0)
            {
                throw new InvalidOperationException(
                          Resources.InvalidOperationException_ChainProxyClient_NotProxies);
            }

            #endregion

            List <ProxyClient> proxies;

            if (EnableShuffle)
            {
                proxies = _proxies.ToList();

                // Перемешиваем прокси.
                for (int i = 0; i < proxies.Count; i++)
                {
                    int randI = Rand.Next(proxies.Count);

                    ProxyClient proxy = proxies[i];
                    proxies[i]     = proxies[randI];
                    proxies[randI] = proxy;
                }
            }
            else
            {
                proxies = _proxies;
            }

            int       length       = proxies.Count - 1;
            TcpClient curTcpClient = tcpClient;

            for (int i = 0; i < length; i++)
            {
                curTcpClient = proxies[i].CreateConnection(
                    proxies[i + 1].Host, proxies[i + 1].Port, curTcpClient);
            }

            curTcpClient = proxies[length].CreateConnection(
                destinationHost, destinationPort, curTcpClient);

            return(curTcpClient);
        }
#pragma warning disable CS0108 // Member hides inherited member; missing new keyword
        /// <summary>
        /// Преобразует строку в экземпляр класса <see cref="Socks4aProxyClient"/>.
        /// </summary>
        /// <param name="proxyAddress">Строка вида - хост:порт:имя_пользователя:пароль. Три последних параметра являются необязательными.</param>
        /// <returns>Экземпляр класса <see cref="Socks4aProxyClient"/>.</returns>
        /// <exception cref="System.ArgumentNullException">Значение параметра <paramref name="proxyAddress"/> равно <see langword="null"/>.</exception>
        /// <exception cref="System.ArgumentException">Значение параметра <paramref name="proxyAddress"/> является пустой строкой.</exception>
        /// <exception cref="System.FormatException">Формат порта является неправильным.</exception>
        public static Socks4aProxyClient Parse(string proxyAddress)
#pragma warning restore CS0108 // Member hides inherited member; missing new keyword
        {
            return(ProxyClient.Parse(ProxyType.Socks4a, proxyAddress) as Socks4aProxyClient);
        }
 /// <summary>
 /// Инициализирует новый экземпляр класса <see cref="xNetStandard.Net.ProxyException"/> заданным сообщением об ошибке и прокси-клиентом.
 /// </summary>
 /// <param name="message">Сообщение об ошибке с объяснением причины исключения.</param>
 /// <param name="proxyClient">Прокси-клиент, в котором произошла ошибка.</param>
 /// <param name="innerException">Исключение, вызвавшее текущие исключение, или значение <see langword="null"/>.</param>
 public ProxyException(string message, ProxyClient proxyClient, Exception innerException = null)
     : base(message, innerException)
 {
     ProxyClient = proxyClient;
 }
 /// <summary>
 /// Преобразует строку в экземпляр класса <see cref="HttpProxyClient"/>.
 /// </summary>
 /// <param name="proxyAddress">Строка вида - хост:порт:имя_пользователя:пароль. Три последних параметра являются необязательными.</param>
 /// <returns>Экземпляр класса <see cref="HttpProxyClient"/>.</returns>
 /// <exception cref="System.ArgumentNullException">Значение параметра <paramref name="proxyAddress"/> равно <see langword="null"/>.</exception>
 /// <exception cref="System.ArgumentException">Значение параметра <paramref name="proxyAddress"/> является пустой строкой.</exception>
 /// <exception cref="System.FormatException">Формат порта является неправильным.</exception>
 public static HttpProxyClient Parse(string proxyAddress)
 {
     return(ProxyClient.Parse(ProxyType.Http, proxyAddress) as HttpProxyClient);
 }
 /// <summary>
 /// Преобразует строку в экземпляр класса <see cref="Socks5ProxyClient"/>.
 /// </summary>
 /// <param name="proxyAddress">Строка вида - хост:порт:имя_пользователя:пароль. Три последних параметра являются необязательными.</param>
 /// <returns>Экземпляр класса <see cref="Socks5ProxyClient"/>.</returns>
 /// <exception cref="System.ArgumentNullException">Значение параметра <paramref name="proxyAddress"/> равно <see langword="null"/>.</exception>
 /// <exception cref="System.ArgumentException">Значение параметра <paramref name="proxyAddress"/> является пустой строкой.</exception>
 /// <exception cref="System.FormatException">Формат порта является неправильным.</exception>
 public static Socks5ProxyClient Parse(string proxyAddress)
 {
     return(ProxyClient.Parse(ProxyType.Socks5, proxyAddress) as Socks5ProxyClient);
 }