检测互联网网络可用性






4.88/5 (29投票s)
检测网络可用性,特别是对互联网的可用性,并监控状态变化

目录
引言
本文介绍了一个用于确定网络可用性并检测连接性变化的小类。本文侧重于“互联网可用性”。换句话说,如果至少有一个与 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");
}
然后,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
NetworkInteface
类包含 static GetIsNetworkAvailable()
方法。虽然此方法已完成了我们大部分需要的工作,但我发现有些情况,尤其是在使用无线适配器时,网络似乎在线但尚未建立真正的开放连接。它还会看到虚拟机适配器并认为它们在线;显然,除非您有一些奇怪的开发配置,您正在使用虚拟机作为路由器,否则这些都不会引导您访问互联网。
我们首先使用 GetIsNetworkAvailable
进行快速测试,然后通过检查每个合适的网络接口的活动、BytesReceived
和 BytesSent
来扩展它。这被封装在我们的 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
类有两个事件: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();
// 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 的持续改进,并希望获得更多有用的文章。谢谢!