public IPv4V6DualStackSpeculativeConnector(TcpConnectParam basicParam, TcpIpSystem system) : base(basicParam)
        {
            if (basicParam.DestHostname._IsEmpty())
            {
                // IP address is specified
                AddAttempt(new Attempt(system, basicParam, 0));
                return;
            }

            // Hostname is specified
            var hostInfo = system.GetHostInfo(true);

            if (hostInfo.IsIPv4Supported)
            {
                AddAttempt(new Attempt(system, new TcpConnectParam(basicParam.DestHostname, basicParam.DestPort, AddressFamily.InterNetwork,
                                                                   connectTimeout: basicParam.ConnectTimeout, dnsTimeout: basicParam.DnsTimeout), 0));
            }

            if (hostInfo.IsIPv6Supported)
            {
                AddAttempt(new Attempt(system, new TcpConnectParam(basicParam.DestHostname, basicParam.DestPort, AddressFamily.InterNetworkV6,
                                                                   connectTimeout: basicParam.ConnectTimeout, dnsTimeout: basicParam.DnsTimeout), 0));
            }
        }
        // メインループ
        async Task MainLoopAsync(CancellationToken cancel)
        {
            while (cancel.IsCancellationRequested == false)
            {
                // インターネットが接続されるまで待機する
                if (await WaitForInternetAsync(cancel) == false)
                {
                    // キャンセルされた
                    return;
                }

                // 接続された
                _IsInternetConnected = true;
                FirstConnectedEvent.Set(true);
                EventListener.FireSoftly(this, NonsenseEventType.Nonsense, true);

                int lastNetworkVersion = TcpIp.GetHostInfo(false).InfoVersion;

                AsyncAutoResetEvent networkChangedEvent = new AsyncAutoResetEvent();
                int eventRegisterId = TcpIp.RegisterHostInfoChangedEvent(networkChangedEvent);
                try
                {
                    // 定期的に、現在もインターネットに接続されているかどうか確認する
                    while (cancel.IsCancellationRequested == false)
                    {
                        // 一定時間待つ ただしネットワーク状態の変化が発生したときは待ちを解除する
                        await TaskUtil.WaitObjectsAsync(cancels : cancel._SingleArray(), events : networkChangedEvent._SingleArray(),
                                                        timeout : Options.CheckIntervalAfterEstablished);

                        if (cancel.IsCancellationRequested)
                        {
                            break;
                        }

                        // 接続検査を再実行する
                        // ただし、今度はタイムアウトを設定する
                        CancellationTokenSource timeoutCts = new CancellationTokenSource();
                        timeoutCts.CancelAfter(Options.TimeoutToDetectDisconnected);

                        bool ret = await StartEveryTestAsync(timeoutCts.Token, null);

                        if (cancel.IsCancellationRequested)
                        {
                            break;
                        }

                        if (ret == false)
                        {
                            // 接続試験に失敗 (タイムアウト発生)
                            // 切断された
                            _IsInternetConnected = false;
                            FirstDisconnectedEvent.Set(true);
                            EventListener.FireSoftly(this, NonsenseEventType.Nonsense, false);

                            break;
                        }
                    }
                }
                finally
                {
                    TcpIp.UnregisterHostInfoChangedEvent(eventRegisterId);
                }
            }
        }