65.9K
CodeProject 正在变化。 阅读更多。
Home

检测互联网网络可用性

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.88/5 (29投票s)

2010年3月12日

Ms-PL

4分钟阅读

viewsIcon

130859

downloadIcon

11132

检测网络可用性,特别是对互联网的可用性,并监控状态变化

目录

引言

本文介绍了一个用于确定网络可用性并检测连接性变化的小类。本文侧重于“互联网可用性”。换句话说,如果至少有一个与 IP 相关的接口可用且正常工作,我们就认为“网络”是可用的。

背景

iTuner 歌词引擎依赖在线提供商搜索歌曲歌词。每当播放一首尚无歌词的歌曲时,它就会连接到互联网。iTuner 的特定要求如下:

  • 确定当前的连接状态
  • 订阅以在网络连接发生变化时收到通知
  • 忽略所有可能不提供互联网连接的网络适配器,因为我的主要目标是联系互联网资源

这导致了 System.Net.NetworkInformation 命名空间和下面提供的轻量级包装器。有些场景是这些 System 类无法覆盖的,本文将解决这些问题。当然,您可以将这些要求泛化到任何应用程序,或者用其他功能扩展此类,例如在进行更繁重的通信之前ping目标站点以检查其是否存在。

Using the Code

NetworkStatus 类是一个 static 类,它公开了一个方法和一个事件,如下所示:

public static class NetworkStatus
{
    public static event NetworkStatusChangedHandler AvailabilityChanged;
    public static bool IsAvailable { get; }
}

如您所见,它并不复杂——这就是关键。虽然这个类背后的代码量不多,但它封装了所有繁琐的细节,这样我们就不用担心这些细节了。NetworkStatusDemo 应用程序附带本文,演示了此类最简单的用法。它首先使用 IsAvailable 属性报告当前网络可用性。

if (NetworkStatus.IsAvailable)
{
    Console.WriteLine("... Network is available");
}
else
{
    Console.WriteLine("... Network is not available");
}

然后,NetworkStatusDemoNetworkStatusChangedHandler 处理程序附加到 AvailabilityChanged 事件。

NetworkStatus.AvailabilityChanged +=
    new NetworkStatusChangedHandler(DoAvailabilityChanged);

static void DoAvailabilityChanged (
    object sender, NetworkStatusChangedArgs e)
{
    if (e.IsAvailable)
    {
        Console.WriteLine("... Network is available");
    }
    else
    {
        Console.WriteLine("... Network is not available");
    }
}

很简单,但足以确定我们在任何特定时间点是否应该尝试联系网站或 Web 服务。

代码内部

虽然原始的 .NET 类提供了此处所需的大部分功能,但我有两个主要目标:

  1. 缩小 NetworkInterface 类的范围,使其仅报告能够连接到互联网的适配器
  2. 封装 NetworkChange 类提供的多个事件

System.Net.NetworkInformation.NetworkInterface

NetworkInteface 类包含 static GetIsNetworkAvailable() 方法。虽然此方法已完成了我们大部分需要的工作,但我发现有些情况,尤其是在使用无线适配器时,网络似乎在线但尚未建立真正的开放连接。它还会看到虚拟机适配器并认为它们在线;显然,除非您有一些奇怪的开发配置,您正在使用虚拟机作为路由器,否则这些都不会引导您访问互联网。

我们首先使用 GetIsNetworkAvailable 进行快速测试,然后通过检查每个合适的网络接口的活动、BytesReceivedBytesSent 来扩展它。这被封装在我们的 NetworkStatus.IsNetworkAvailable() 方法中。

private static bool IsNetworkAvailable ()
{
    // only recognizes changes related to Internet adapters
    if (NetworkInterface.GetIsNetworkAvailable())
    {
        // however, this will include all adapters
        NetworkInterface[] interfaces =
            NetworkInterface.GetAllNetworkInterfaces();

        foreach (NetworkInterface face in interfaces)
        {
            // filter so we see only Internet adapters
            if (face.OperationalStatus == OperationalStatus.Up)
            {
                if ((face.NetworkInterfaceType != NetworkInterfaceType.Tunnel) &&
                    (face.NetworkInterfaceType != NetworkInterfaceType.Loopback))
                {
                    IPv4InterfaceStatistics statistics =
                        face.GetIPv4Statistics();

                    // all testing seems to prove that once an interface
                    // comes online it has already accrued statistics for
                    // both received and sent...

                    if ((statistics.BytesReceived > 0) &&
                        (statistics.BytesSent > 0))
                    {
                        return true;
                    }
                }
            }
        }
    }

    return false;
}

虽然这足以测试当前可用性,但我们可以通过维护一个 private Boolean 变量 isAvailable 并仅在连接状态更改时设置其状态来优化效率。这时 NetworkChange 类就派上用场了。

System.Net.NetworkInformation.NetworkChange

NetworkChange 类有两个事件:NetworkAvailabilityChangedNetworkAddressChanged。听起来不错。但问题是 NetworkChange 不会区分可能不向我们提供互联网访问的适配器,例如隧道适配器。所以我们的 NetworkStatus 类将此功能扩展为仅从相关适配器获取事件。我们通过严格控制 NetworkAvailabilityChangedNetworkAddressChanged 事件来管理这一点,将它们隐藏在我们自己的 AvailabilityChanged 事件后面。

public static event NetworkStatusChangedHandler AvailabilityChanged
{
    add
    {
        if (handler == null)
        {
            NetworkChange.NetworkAvailabilityChanged
                += new NetworkAvailabilityChangedEventHandler(
                   DoNetworkAvailabilityChanged);

            NetworkChange.NetworkAddressChanged
                += new NetworkAddressChangedEventHandler(
                   DoNetworkAddressChanged);
        }

        handler = (NetworkStatusChangedHandler)Delegate.Combine(handler, value);
    }

    remove
    {
        handler = (NetworkStatusChangedHandler)Delegate.Remove(handler, value);

        if (handler == null)
        {
            NetworkChange.NetworkAvailabilityChanged
                -= new NetworkAvailabilityChangedEventHandler(
                   DoNetworkAvailabilityChanged);

            NetworkChange.NetworkAddressChanged
                -= new NetworkAddressChangedEventHandler(
                   DoNetworkAddressChanged);
        }
    }
}

您可以看到我们提供了自己的 handler 变量。这被定义为一个事件,我们可以在需要向使用者发出状态更改信号时触发它。

不幸的是,这两个包装事件的处理程序签名不相似。所以我们需要提供两个不同的处理程序。但它们都汇集到一个点:SignalAvailabilityChange

private static void SignalAvailabilityChange (object sender)
{
    bool change = IsNetworkAvailable();
    // only signal consumers when the general state changes
    if (change != isAvailable)
    {
        // set the local isAvailable variable
        isAvailable = change;
        if (handler != null)
        {
            handler(sender, new NetworkStatusChangedArgs(isAvailable));
        }
    }
}

最终,代码量不大,也不复杂或具有革命性。但它已被证明是一个整洁的实现,有助于简化任何主应用程序。

结论

如果您发现本文有帮助,并且喜欢 iTuner 应用程序,请考虑 捐赠 以支持 iTuner 的持续改进,并希望获得更多有用的文章。谢谢!

© . All rights reserved.