Exemple #1
0
        protected async Task EnsureConnected()
        {
            try
            {
                if (IsConnected())
                {
                    return;
                }

                // Recreate the TCP client
                this.stream?.Dispose();
                this.client?.Close();

                if (this.disableDualMode)
                {
                    this.client = new TcpClient(AddressFamily.InterNetwork);
                }
                else
                {
                    // Allow connections to be made via IPv4 or IPv6. With just the default constructor,
                    // only IPv4 can be used. To support both, we must specify the AddressFamily.InterNetworkV6
                    // and set the DualMode property on the underlying socket to true. The DualMode property
                    // can only be set to true when the AddressFamily is set to InterNetworkV6.
                    //
                    // But there is another caveat. If you call the .Connect() method overload that takes in a
                    // DNS host name and port number, that method's code will first make a call to resolve the
                    // DNS host name to an IP address (or addresses). It then validates that one of the resolved
                    // IP addresses' AddressFamily type matches the AddressFamily type that was specified when
                    // the TcpClient was constructed. If it doesn't find a match, for example, because we specify
                    // AddressFamily.InterNetworkV6 and the DNS host name only resolves to an IPv4 address, it
                    // will throw an error. Essentially, the .Connect() method that takes in a DNS host name and
                    // port does not take into consideration the DualMode property that we set to true.
                    //
                    // All other .Connect() methods take in some form of IPAddress/IPEndPoint, which gets passed
                    // directly down to the underlying socket, which is where the DualMode property exists.
                    // Therefore, it is properly utilized and will work with either IPv4 or IPv6.
                    //
                    // So, we will make a call to resolve the DNS host name ourselves and call the .Connect()
                    // method that takes in the array of IP addresses. That way, it will try each of them and will
                    // work regardless of if the DNS host name resolved to an IPv4 or IPv6.
                    this.client = new TcpClient(AddressFamily.InterNetworkV6);
                    this.client.Client.DualMode = true;
                }

                IPAddress[] hostAddresses = new IPAddress[] { };
                if (this.checkHostIPAddress)
                {
                    // If the Host name specified is already an IP address, then that is what will be returned.
                    hostAddresses = await Dns.GetHostAddressesAsync(this.Host).ConfigureAwait(false);
                }

                // If we're running on Linux, only try to set keep-alives if they are wanted (in
                // that case we resolved the hostname to an IP in the ctor)
                // See https://github.com/dotnet/corefx/issues/26840
                if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || this.enableKeepAlive)
                {
                    this.client.Client.SetSocketOption(SocketOptionLevel.Socket,
                                                       SocketOptionName.KeepAlive, this.enableKeepAlive);
                }

                // Reduce latency to a minimum
                this.client.NoDelay = true;

                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    // Windows can support multiple connection attempts, thereby allowing us to pass in an
                    // array of addresses. See:
                    // https://github.com/dotnet/runtime/blob/release/5.0/src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs#L5071
                    if (this.checkHostIPAddress)
                    {
                        await this.client.ConnectAsync(hostAddresses, this.Port).ConfigureAwait(false);
                    }
                    else
                    {
                        await this.client.ConnectAsync(this.Host, this.Port).ConfigureAwait(false);
                    }
                }
                else
                {
                    // However, multiple connection attempts is not guaranteed on other platforms. So we'll
                    // be cautious and only use the first IP address. If for whatever reason the caller was
                    // hoping that the second or some other IP address would be used, then they will just
                    // have to change their DNS so that the IP address they want will be resolved with the
                    // highest priority.
                    if (this.checkHostIPAddress)
                    {
                        await this.client.ConnectAsync(hostAddresses.First(), this.Port).ConfigureAwait(false);
                    }
                    else
                    {
                        await this.client.ConnectAsync(this.Host, this.Port).ConfigureAwait(false);
                    }
                }

                this.stream = await GetStream(this.client.GetStream()).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                // Attempt to provide meaningful diagnostic messages for common connection problems
                HandleConnectError(ex);
                throw;
            }
        }