【问题标题】:Detect if the current connection supports both IPv4 and IPv6检测当前连接是否同时支持 IPv4 和 IPv6
【发布时间】:2015-09-25 11:56:38
【问题描述】:

我需要在 WinRT 应用程序 中检测哪些协议上有 Internet 访问(IPv4/IPv6/两者)。我有以下代码来确定支持的协议:

enum IpVersion
{
    None = 0,
    IPv4 = 1,
    IPv6 = 2,
    IPv46 = 3
}

IpVersion GetIpVersion(ConnectionProfile profile)
{
    var result = IpVersion.None;

    if (profile != null && profile.NetworkAdapter != null)
    {
        var hostnames = NetworkInformation.GetHostNames().Where(h => h.IPInformation != null && 
                                                                h.IPInformation.NetworkAdapter != null &&
                                                                h.IPInformation.NetworkAdapter.NetworkAdapterId == profile.NetworkAdapter.NetworkAdapterId);
        foreach (var hostname in hostnames)
        {
            if (hostname.Type == HostNameType.Ipv4)
            {
                result |= IpVersion.IPv4;
            }
            else if (hostname.Type == HostNameType.Ipv6)
            {
                result |= IpVersion.IPv6;
            }
        }
    }

    return result;
}  

我是这样使用的:

GetIpVersion(NetworkInformation.GetInternetConnectionProfile()); 

现在我想知道每个可用协议是否都可以访问互联网。当然我可以 ping 一些东西,但我想知道是否有一些 SDK 方法。这些信息可在Wi-Fi status 窗口中找到:

有一个方法可以返回例如NetworkConnectivityLevel.InternetAccess,但它不包含关于哪个协议存在连接的信息。

bool internetAccess = connectionProfile.GetNetworkConnectivityLevel() == NetworkConnectivityLevel.InternetAccess

【问题讨论】:

  • @KevinKal 我基于此,但是我没有检测到 IP 地址,而是 IP 版本。仔细阅读问题。
  • 我正在仔细阅读。我也找不到任何关于从现有方法中获取类型的信息。为什么不尝试通过 rawName 来确定 IP 版本……我想应该是可以的!
  • @KevinKal 如果您已连接到网络,它可以支持通过 IPv4 或 IPv6 或两者进行连接。我需要知道我的网络是否支持 BOTH,但是我的代码中的主机名包含枚举 HostNameType,它没有值“both”。
  • @KevinKal 不,您可能无法通过 IPv4 连接互联网。也许我应该看看那些 ConnectionProfiles。不幸的是,我将在星期一访问 IPv6,所以我会检查一下。

标签: c# windows-runtime connection ipv6 ipv4


【解决方案1】:

在您的代码中,ConnectionProfile 类型是什么?如果没有a good, minimal, complete code example 以及对该代码的作用以及为什么与您想要的不同的确切解释,很难理解这个问题。

也就是说,如果我正确理解了这个问题,您正在尝试确定您与 Internet 的连接是否同时支持 IPv4 和 IPv6。如果是这样,那么我看不出有任何方法可以从 API 级别做到这一点。本地 PC 实际上可以安装 IPv4 协议,而无需连接到允许传输该协议上的流量的网络。即使 LAN 支持该协议,互联网连接本身也可能只支持 IPv6。

同样适用于其他方式(即支持本地 IPv6,但仅支持 Internet 上的 IPv4)。

在我看来,唯一可靠的方法与许多其他情况所需的方法相同:只需尝试一下,看看它是否有效。 IE。尝试通过所需的协议版本连接到 Internet 上的远程端点;如果失败,则不支持。如果成功,则支持。


编辑:

感谢您更新问题。它仍然不是最好的代码示例,但它稍微细化了问题。

我仍然不是 100% 你需要做的事情,也不是我是否有对你最有用的方法。但这里有一个简短的程序,我认为它可以满足您的需求:

XAML:

<Page x:Class="TestSO32781692NetworkProtocolConnectivity.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:TestSO32781692NetworkProtocolConnectivity"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}"
      mc:Ignorable="d">

  <StackPanel Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <StackPanel.Resources>
      <Style TargetType="TextBlock">
        <Setter Property="FontSize" Value="24"/>
      </Style>
    </StackPanel.Resources>
    <StackPanel Orientation="Horizontal" Margin="10, 50, 10, 0">
      <TextBlock Text="IpV4: "/>
      <TextBlock Text="{Binding IpV4}"/>
    </StackPanel>
    <StackPanel Orientation="Horizontal" Margin="10, 10, 10, 0">
      <TextBlock Text="IpV6: "/>
      <TextBlock Text="{Binding IpV6}"/>
    </StackPanel>
    <Button Content="Check Network" Click="Button_Click"/>
    <ListBox ItemsSource="{Binding Profiles}"/>
  </StackPanel>
</Page>

C#:

using System;
using System.Collections.ObjectModel;
using System.Linq;
using Windows.Networking;
using Windows.Networking.Connectivity;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238

namespace TestSO32781692NetworkProtocolConnectivity
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        public static readonly DependencyProperty IpV4Property = DependencyProperty.Register(
            "IpV4", typeof(bool), typeof(MainPage), new PropertyMetadata(false));
        public static readonly DependencyProperty IpV6Property = DependencyProperty.Register(
            "IpV6", typeof(bool), typeof(MainPage), new PropertyMetadata(false));
        public static readonly DependencyProperty ProfilesProperty = DependencyProperty.Register(
            "Profiles", typeof(ObservableCollection<string>), typeof(MainPage), new PropertyMetadata(new ObservableCollection<string>()));

        public bool IpV4
        {
            get { return (bool)GetValue(IpV4Property); }
            set { SetValue(IpV4Property, value); }
        }

        public bool IpV6
        {
            get { return (bool)GetValue(IpV6Property); }
            set { SetValue(IpV6Property, value); }
        }

        public ObservableCollection<string> Profiles
        {
            get { return (ObservableCollection<string>)GetValue(ProfilesProperty); }
            set { SetValue(ProfilesProperty, value); }
        }

        public MainPage()
        {
            this.InitializeComponent();
        }

        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            bool ipV4 = false, ipV6 = false;
            ConnectionProfile internetProfile = NetworkInformation.GetInternetConnectionProfile();

            Profiles.Clear();
            Profiles.Add("Internet profile: " + internetProfile.ProfileName);

            var hostNames = NetworkInformation.GetHostNames()
                .Where(h => h.IPInformation != null &&
                       h.IPInformation.NetworkAdapter != null);

            foreach (HostName hostName in hostNames)
            {
                ConnectionProfile hostConnectedProfile =
                    await hostName.IPInformation.NetworkAdapter.GetConnectedProfileAsync();

                if (hostConnectedProfile.NetworkAdapter.NetworkAdapterId == internetProfile.NetworkAdapter.NetworkAdapterId)
                {
                    Profiles.Add("Host adapter: " + hostName.DisplayName);
                    if (hostName.Type == HostNameType.Ipv4)
                    {
                        ipV4 = true;
                    }
                    else if (hostName.Type == HostNameType.Ipv6)
                    {
                        ipV6 = true;
                    }
                }
            }

            IpV4 = ipV4;
            IpV6 = ipV6;
        }
    }
}

请注意,此代码示例不使用NetworkInformation.GetConnectionProfiles() 方法。尽管这似乎是扩展您当前方法的一种很有前途的方法,尽管 the documentation 实际上承诺 “调用 GetConnectionProfiles 方法会检索设备上当前建立的所有连接的配置文件,包括 Internet 连接。" [强调我的],事实证明并非如此。特别是,至少在我安装并启用了 Hyper-V 的机器上(对于那些进行 WinRT/Windows Phone 开发的人来说这是一个常见的场景:)),NetworkInformation.GetInternetConnectionProfile() 返回的 ConnectionProfile 对象实际上是 not 包含在NetworkInformation.GetConnectionProfiles() 返回的配置文件集合中。

因此,上面的代码示例所做的只是简单地识别与GetInternetConnectionProfile() 返回的ConnectionProfile 对应的任何主机名对象。

不幸的是,我所在的地方实际上并没有 IPv6 支持,所以我无法对此进行全面测试。 我能够在 IPv6 到 Internet 所在的不同网络上测试上述内容支持,我现在可以确认,至少在我的测试中,它按预期工作。该代码正确检测 IPv4 和 IPv6 协议上的 Internet 连接。我希望以上内容能满足您的需求。


顺便说一句,除了使用GetNetworkConnectivityLevel() 的明显问题(如您所述)没有提供实际使用的协议外,我发现该方法甚至没有返回至少直观地被认为是正确的信息。

特别是,调用FindConnectionProfilesAsync(new ConnectionProfileFilter { IsConnected = true }) 确实会返回与我用来连接互联网的连接(例如无线网络)对应的配置文件,但是当我在该配置文件上调用GetNetworkConnectivityLevel() 时,它只返回LocalAccess .我猜这与我上面提到的安装 Hyper-V 的问题有关。

这可以通过将GetConnectedProfileAsync() 方法返回的ConnectionProfileFindConnectionProfilesAsync() 返回的每个连接配置文件的NetworkAdapterGetInternetConnectionProfile() 返回的配置文件进行比较来解决。对顶级配置文件的网络适配器的配置文件进行间接处理似乎会产生预期的 Internet 连接配置文件。

当然,解决该问题并不能解决所使用协议的问题。我仅在其他人更多地针对配置文件管理方面而不是协议连接问题查看此答案时才提及它。

【讨论】:

  • 感谢您为这个答案所做的努力,我会在这周检查它并让您知道。您是否尝试关闭 IPv6 的 DNS(然后没有互联网连接)并验证它是否正常工作?因为看起来此代码仅验证是否为每个协议分配了 IP 地址。所以有可能的情况是,当我们仅在 IPv4 上获得 Internet 连接时 - GetInternetConnectionProfile() 将返回此配置文件,但另一个主机名 (IPv6) 未连接到 Internet,因此此方法将返回两者,因为它们属于相同个人资料(有互联网连接)
  • 我没有对这段代码进行广泛的测试。我可以告诉你,在 Internet 端只有 IPv4 服务,而 LAN 端只有 IPv6 的站点上,它正确地将 IPv4 主机名识别为已连接,而将 IPv6 主机名识别为未连接。
  • 不幸的是,这段代码不能正常工作。仅对于 IPv4,如果我弄乱了地址(因此我的访问权限有限或没有网络访问权限),它仍然显示 IPv4 上有连接。仅对于 IPv6,它总是向我表明这两种协议都具有 Internet 连接性(即使它们都不是真的)。如果仍然不清楚我想要实现什么,请查看我帖子中带有“Wi-Fi 状态”窗口的屏幕截图。我想获得关于 IPv4 和 IPv6 的完全相同的信息(我的意思是 Internet = True,其他任何内容 = False)。
【解决方案2】:

了解问题:

我们实际上需要了解哪些有关 Internet 连接的信息才能假设存在通过特定协议的连接?

我们需要检查两件事:

  1. 如果我们能够使用 IP 地址进行连接。
  2. 如果我们能够使用 DNS 服务器将域名解析为 IP 地址。

解决方案:

我们可以使用域名解析来检查支持哪些协议。

它有效,因为如果我们能够连接到 DNS 服务器,我们就能够使用 IP 地址通过该协议进行连接(我的意思是一般来说,假设没有防火墙规则、网络问题等)。然后,如果我们得到某个域的 IPv6 地址作为响应,这意味着 DNS 服务器已设置并适用于该协议。

代码示例:

// the order is important, if we want to support bitwise OR: IPv4 | IPv6 equals IPv4and6
public enum IpVersion
{
    None,
    IPv4,
    IPv6,
    IPv4and6
}

public async Task<IpVersion> GetCurrentIpVersion()
{
    try
    {
        // resolves domain name to IP addresses (may contain several)
        var endPointPairs = await DatagramSocket.GetEndpointPairsAsync(new HostName("google.com"), "0");
        if (endPointPairs == null)
        {
            return IpVersion.None;
        }

        // detect which IP version is supported
        var result = IpVersion.None;
        foreach (var endPoint in endPointPairs)
        {
            if (endPoint.RemoteHostName != null)
            {
                if (endPoint.RemoteHostName.Type == HostNameType.Ipv4)
                {
                    result |= IpVersion.IPv4;
                }
                else if (endPoint.RemoteHostName.Type == HostNameType.Ipv6)
                {
                    result |= IpVersion.IPv6;
                }
            }
        }
        return result;
    }
    catch
    {
        return IpVersion.None;
    }
}

测试:
我在纯 IPv4、纯 IPv6 和 IPv4 + IPv6 网络上对其进行了测试 - 按预期工作。

示例项目:
IpVersionDetection - GitHub

我也在此处描述了此解决方案:
WinRT - how to detect supported IP version

【讨论】:

    猜你喜欢
    • 2010-12-09
    • 2012-05-09
    • 1970-01-01
    • 2019-05-16
    • 2012-07-26
    • 2013-09-16
    • 2016-06-10
    • 1970-01-01
    • 2015-09-16
    相关资源
    最近更新 更多