检测当前连接是否同时支持IPv4和IPv6

时间:2015-09-25 11:56:38

标签: c# windows-runtime connection ipv6 ipv4

我需要在 WinRT应用程序中检测哪些协议有互联网访问权限(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窗口中找到:

WiFi-status

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

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

2 个答案:

答案 0 :(得分:1)

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

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

同样的事情适用于另一种方式(即具有本地IPv6支持,但只有Internet上的IPv4)。

在我看来,唯一可靠的方法与许多其他情况下所需的方法相同:只需尝试一下,看看它是否有效。即尝试通过所需的协议版本连接到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开发的人来说是常见的场景:)),ConnectionProfile返回的NetworkInformation.GetInternetConnectionProfile()对象实际上是< em> not 包含在NetworkInformation.GetConnectionProfiles()返回的个人资料集合中。

相反,上面的代码示例只是简单地标识看似与ConnectionProfile返回的GetInternetConnectionProfile()对应的任何主机名对象。

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


顺便说一下,除了明显的问题(如你所说)使用GetNetworkConnectivityLevel()并没有提供正在使用的实际协议,我发现该方法甚至不会返回至少在直觉上被认为是正确的信息。

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

这可以通过将ConnectionProfile方法返回的GetConnectedProfileAsync() NetworkAdapterFindConnectionProfilesAsync()返回的每个连接配置文件的GetInternetConnectionProfile()进行比较来处理@*。 1}}。指向顶级配置文件的网络适配器的配置文件似乎产生了预期的Internet连接配置文件。

当然,解决该问题并不能解决使用中的协议问题。我之所以提到它只是因为其他人正在寻找更多关于配置文件管理方面而不是协议连接问题的答案。

答案 1 :(得分:1)

了解问题:

我们需要了解互联网连接,以假设通过特定协议存在连接?

我们需要检查两件事:

  1. 如果我们能够使用IP地址进行连接。
  2. 如果我们能够使用DNS服务器将域名解析为IP地址。
  3. <强>解决方案:

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

    它有效,因为如果我们能够连接到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