.NET网络状态检测与变化监听

在本文中,将探讨一个.NET类库,它能够检测网络的可用性并监听网络连接的变化。关注的重点是“互联网可用性”,即如果至少有一个与IP相关的接口可用并且正常工作,那么认为“网络”是可用的。

iTuner歌词引擎依赖于在线提供商来搜索歌词。每当播放一首尚未有歌词的歌曲时,它就会连接到互联网。iTuner的具体需求如下:

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

这导致了对System.Net.NetworkInformation命名空间的使用,以及下面展示的薄包装器。这些System类没有覆盖的一些场景,本文将进行讨论。当然,可以将这些需求泛化到任何应用程序,或者通过添加其他功能(如在进行更重的通信之前ping目标站点以检查其存在)来扩展这个类。

使用代码

NetworkStatus类是一个静态类,暴露了一个方法和一个事件,如下所示:

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"); }

然后,NetworkStatusDemo将一个NetworkStatusChangedHandler处理程序附加到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类提供了这里所需的大部分功能,但有两个主要目标:

  • 将NetworkInterface类的范围缩小到只报告能够连接到互联网的适配器。
  • 封装NetworkChange类提供的多个事件。

System.Net.NetworkInformation.NetworkInterface类包括一个静态GetIsNetworkAvailable()方法。虽然这个方法已经做了需要的大部分工作,但发现在某些情况下,尤其是无线适配器,网络看起来是在线的,但还没有真正建立到网络的开放连接。它还会看到虚拟机适配器并认为它们是在线的;显然,除非有一些奇怪的开发配置,其中使用虚拟机作为路由器,否则这些不会导致连接到互联网。

首先使用GetIsNetworkAvailable()进行快速测试,然后通过检查每个适当的网络接口的活动、BytesReceived和BytesSent来扩展这一点。这在NetworkStatus.IsNetworkAvailable()方法中进行了封装。

private static bool IsNetworkAvailable() { if (NetworkInterface.GetIsNetworkAvailable()) { NetworkInterface[] interfaces = NetworkInterface.GetAllNetworkInterfaces(); foreach (NetworkInterface face in interfaces) { if (face.OperationalStatus == OperationalStatus.Up) { if ((face.NetworkInterfaceType != NetworkInterfaceType.Tunnel) && (face.NetworkInterfaceType != NetworkInterfaceType.Loopback)) { IPv4InterfaceStatistics statistics = face.GetIPv4Statistics(); if ((statistics.BytesReceived > 0) && (statistics.BytesSent > 0)) { return true; } } } } } return false; }

虽然这足以测试当前的可用性,但可以通过维护一个私有布尔变量isAvailable,并仅在连接状态变化时设置其状态来优化效率。这就是NetworkChange类发挥作用的地方。

System.Net.NetworkInformation.NetworkChange类有两个事件,NetworkAvailabilityChanged和NetworkAddressChanged。听起来不错。但问题是NetworkChange不区分可能不让访问互联网的适配器,如隧道适配器。因此,NetworkStatus类扩展了这个功能,只包括相关适配器的事件。通过紧密控制NetworkAvailabilityChanged和NetworkAddressChanged事件自己来管理这一点,将它们隐藏在自己的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(); if (change != isAvailable) { isAvailable = change; if (handler != null) { handler(sender, new NetworkStatusChangedArgs(isAvailable)); } } }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485