在 .NET 中检查 Internet 连接的最快和最有效的方法是什么?
26 回答
public static bool CheckForInternetConnection(int timeoutMs = 10000, string url = null)
{
try
{
url ??= CultureInfo.InstalledUICulture switch
{
{ Name: var n } when n.StartsWith("fa") => // Iran
"http://www.aparat.com",
{ Name: var n } when n.StartsWith("zh") => // China
"http://www.baidu.com",
_ =>
"http://www.gstatic.com/generate_204",
};
var request = (HttpWebRequest)WebRequest.Create(url);
request.KeepAlive = false;
request.Timeout = timeoutMs;
using (var response = (HttpWebResponse)request.GetResponse())
return true;
}
catch
{
return false;
}
}
您绝对无法可靠地检查是否有互联网连接(我假设您的意思是访问互联网)。
但是,您可以请求几乎从不离线的资源,例如 ping google.com 或类似的东西。我认为这将是有效的。
try {
Ping myPing = new Ping();
String host = "google.com";
byte[] buffer = new byte[32];
int timeout = 1000;
PingOptions pingOptions = new PingOptions();
PingReply reply = myPing.Send(host, timeout, buffer, pingOptions);
return (reply.Status == IPStatus.Success);
}
catch (Exception) {
return false;
}
无需检查,只需执行操作(Web 请求、邮件、ftp 等)并为请求失败做好准备,即使检查成功,您也必须这样做。
考虑以下:
1 - check, and it is OK
2 - start to perform action
3 - network goes down
4 - action fails
5 - lot of good your check did
如果网络出现故障,您的操作将像 ping 等一样迅速失败。
1 - start to perform action
2 - if the net is down(or goes down) the action will fail
NetworkInterface.GetIsNetworkAvailable
非常不可靠。只要有一些 VMware 或其他 LAN 连接,它就会返回错误的结果。另外关于Dns.GetHostEntry
方法,我只是担心测试 URL 是否可能在我的应用程序要部署的环境中被阻止。
所以我发现的另一种方法是使用InternetGetConnectedState
方法。我的代码是
[System.Runtime.InteropServices.DllImport("wininet.dll")]
private extern static bool InternetGetConnectedState(out int Description, int ReservedValue);
public static bool CheckNet()
{
int desc;
return InternetGetConnectedState(out desc, 0);
}
通过 ping Google 测试互联网连接:
new Ping().Send("www.google.com.mx").Status == IPStatus.Success
Ping google.com 引入了 DNS 解析依赖项。Pinging8.8.8.8
很好,但 Google 离我还有几跳。我需要做的就是 ping 互联网上离我最近的东西。
我可以使用Ping
的 TTL 功能来 ping #1,然后 hop #2,等等,直到我从可路由地址上的某处得到回复;如果该节点位于可路由地址上,则它位于 Internet 上。对于我们大多数人来说,第 1 跳将是我们的本地网关/路由器,而第 2 跳将是我们光纤连接另一端的第一个点或其他任何东西。
该代码对我有用,并且比该线程中的其他一些建议响应更快,因为它正在 ping 互联网上离我最近的任何内容。
using System.Diagnostics;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Threading.Tasks;
public static async Task<bool> IsConnectedToInternetAsync()
{
const int maxHops = 30;
const string someFarAwayIpAddress = "8.8.8.8";
// Keep pinging further along the line from here to google
// until we find a response that is from a routable address
for (int ttl = 1; ttl <= maxHops; ttl++)
{
var options = new PingOptions(ttl, true);
byte[] buffer = new byte[32];
PingReply reply;
try
{
using (var pinger = new Ping())
{
reply = await pinger.SendPingAsync(someFarAwayIpAddress, 10000, buffer, options);
}
}
catch (PingException pingex)
{
Debug.Print($"Ping exception (probably due to no network connection or recent change in network conditions), hence not connected to internet. Message: {pingex.Message}");
return false;
}
string address = reply.Address?.ToString() ?? null;
Debug.Print($"Hop #{ttl} is {address}, {reply.Status}");
if (reply.Status != IPStatus.TtlExpired && reply.Status != IPStatus.Success)
{
Debug.Print($"Hop #{ttl} is {reply.Status}, hence we are not connected.");
return false;
}
if (IsRoutableAddress(reply.Address))
{
Debug.Print("That's routable, so we must be connected to the internet.");
return true;
}
}
return false;
}
private static bool IsRoutableAddress(IPAddress addr)
{
if (addr == null)
{
return false;
}
else if (addr.AddressFamily == AddressFamily.InterNetworkV6)
{
return !addr.IsIPv6LinkLocal && !addr.IsIPv6SiteLocal;
}
else // IPv4
{
byte[] bytes = addr.GetAddressBytes();
if (bytes[0] == 10)
{ // Class A network
return false;
}
else if (bytes[0] == 172 && bytes[1] >= 16 && bytes[1] <= 31)
{ // Class B network
return false;
}
else if (bytes[0] == 192 && bytes[1] == 168)
{ // Class C network
return false;
}
else
{ // None of the above, so must be routable
return true;
}
}
}
我不同意有人说:“在执行任务之前检查连接有什么意义,因为在检查后连接可能会立即丢失”。当然,我们作为开发人员承担的许多编程任务都存在一定程度的不确定性,但将不确定性降低到可接受的水平是挑战的一部分。
我最近在制作一个应用程序时遇到了这个问题,该应用程序包括一个链接到在线磁贴服务器的映射功能。如果注意到缺少 Internet 连接,将禁用此功能。
此页面上的一些响应非常好,但是确实导致了很多性能问题,例如挂起,主要是在没有连接的情况下。
在其中一些答案和我的同事的帮助下,这是我最终使用的解决方案:
// Insert this where check is required, in my case program start
ThreadPool.QueueUserWorkItem(CheckInternetConnectivity);
}
void CheckInternetConnectivity(object state)
{
if (System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable())
{
using (WebClient webClient = new WebClient())
{
webClient.CachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.BypassCache);
webClient.Proxy = null;
webClient.OpenReadCompleted += webClient_OpenReadCompleted;
webClient.OpenReadAsync(new Uri("<url of choice here>"));
}
}
}
volatile bool internetAvailable = false; // boolean used elsewhere in code
void webClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
if (e.Error == null)
{
internetAvailable = true;
Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
{
// UI changes made here
}));
}
}
我已经看到了上面列出的所有选项,并且检查互联网是否可用的唯一可行选项是“Ping”选项。导入 [DllImport("Wininet.dll")]
和 System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces()
或任何其他 NetworkInterface
类的变体在检测网络的可用性方面效果不佳。这些方法仅检查网络电缆是否插入。
“Ping 选项”
if
(连接可用)返回 true
if
(连接不通,网线插上)返回 false
if
(网线未插好)Throws an exception
网络接口
if
(可上网)退货True
if
(互联网不可用且网络电缆已插入)返回True
if
(网线未插入)返回false
[DllImport("Wininet.dll")]
if
(可上网)退货True
if
(互联网不可用且网络电缆已插入)返回True
if
(网线未插入)返回false
因此,如果出现[DllImport("Wininet.dll")]
并且 NetworkInterface
无法知道互联网连接是否可用。
不能解决检查和运行代码之间网络中断的问题,但相当可靠
public static bool IsAvailableNetworkActive()
{
// only recognizes changes related to Internet adapters
if (System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable())
{
// however, this will include all adapters -- filter by opstatus and activity
NetworkInterface[] interfaces = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces();
return (from face in interfaces
where face.OperationalStatus == OperationalStatus.Up
where (face.NetworkInterfaceType != NetworkInterfaceType.Tunnel) && (face.NetworkInterfaceType != NetworkInterfaceType.Loopback)
select face.GetIPv4Statistics()).Any(statistics => (statistics.BytesReceived > 0) && (statistics.BytesSent > 0));
}
return false;
}
以下是它在 Android 中的实现方式。
作为概念证明,我将此代码翻译为 C#:
var request = (HttpWebRequest)WebRequest.Create("http://g.cn/generate_204");
request.UserAgent = "Android";
request.KeepAlive = false;
request.Timeout = 1500;
using (var response = (HttpWebResponse)request.GetResponse())
{
if (response.ContentLength == 0 && response.StatusCode == HttpStatusCode.NoContent)
{
//Connection to internet available
}
else
{
//Connection to internet not available
}
}
private bool ping()
{
System.Net.NetworkInformation.Ping pingSender = new System.Net.NetworkInformation.Ping();
System.Net.NetworkInformation.PingReply reply = pingSender.Send(address);
if (reply.Status == System.Net.NetworkInformation.IPStatus.Success)
{
return true;
}
else
{
return false;
}
}
尝试通过捕获异常来避免测试连接。因为我们真的期望有时我们可能会失去网络连接。
if (NetworkInterface.GetIsNetworkAvailable() &&
new Ping().Send(new IPAddress(new byte[] { 8, 8, 8, 8 }),2000).Status == IPStatus.Success)
//is online
else
//is offline
我不认为这是不可能的,只是不简单。
我已经构建了这样的东西,是的,它并不完美,但第一步是必不可少的:检查是否有任何网络连接。Windows Api 做得不好,为什么不做得更好呢?
bool NetworkIsAvailable()
{
var all = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces();
foreach (var item in all)
{
if (item.NetworkInterfaceType == NetworkInterfaceType.Loopback)
continue;
if (item.Name.ToLower().Contains("virtual") || item.Description.ToLower().Contains("virtual"))
continue; //Exclude virtual networks set up by VMWare and others
if (item.OperationalStatus == OperationalStatus.Up)
{
return true;
}
}
return false;
}
这很简单,但确实有助于提高检查质量,尤其是当您要检查各种代理配置时。
所以:
- 检查是否有网络连接(让它变得非常好,甚至可能在有误报时将日志发送回开发人员以改进 NetworkIsAvailable 功能)
- HTTP 平
- (循环使用每个 HTTP Ping 的代理配置)
另一种选择是可用于 Vista 和 Windows 7 的网络列表管理器 API。MSDN 文章在这里。在文章中是一个下载代码示例的链接,它允许您执行此操作:
AppNetworkListUser nlmUser = new AppNetworkListUser();
Console.WriteLine("Is the machine connected to internet? " + nlmUser.NLM.IsConnectedToInternet.ToString());
请务必从 COM 选项卡中添加对 Network List 1.0 Type Library 的引用...这将显示为 NETWORKLIST。
介绍
在某些情况下,您需要在 Windows 应用程序中使用 C# 代码检查 Internet 是否可用。可能是使用互联网以 Windows 形式下载或上传文件,或者从远程位置的数据库中获取一些数据,在这些情况下,互联网检查是强制性的。
有一些方法可以使用 C# 从后面的代码中检查 Internet 可用性。这里解释了所有这些方式,包括它们的局限性。
- InternetGetConnectedState(wininet)
'wininet' API 可用于检查本地系统是否有活动的互联网连接。用于此的命名空间是“System.Runtime.InteropServices”并使用 DllImport 导入 dll“wininet.dll”。在此之后创建一个带有 extern static 的布尔变量,其函数名称为 InternetGetConnectedState,带有两个参数 description 和 reservedValue,如示例所示。
注意:extern 修饰符用于声明在外部实现的方法。当您使用互操作服务调用非托管代码时,extern 修饰符的常见用途是与 DllImport 属性一起使用。在这种情况下,该方法也必须声明为静态。
接下来创建一个名为“IsInternetAvailable”作为布尔值的方法。此方法将使用上述函数,该方法返回本地系统的 Internet 状态
[DllImport("wininet.dll")]
private extern static bool InternetGetConnectedState(out int description, int reservedValue);
public static bool IsInternetAvailable()
{
try
{
int description;
return InternetGetConnectedState(out description, 0);
}
catch (Exception ex)
{
return false;
}
}
- 获取网络可用
下面的示例使用 GetIsNetworkAvailable 方法来确定网络连接是否可用。
if (System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable())
{
System.Windows.MessageBox.Show("This computer is connected to the internet");
}
else
{
System.Windows.MessageBox.Show("This computer is not connected to the internet");
}
备注(根据 MSDN):如果任何网络接口被标记为“up”并且不是环回或隧道接口,则认为网络连接可用。
在很多情况下,设备或计算机未连接到有用的网络,但仍被认为可用,GetIsNetworkAvailable 将返回 true。例如,如果运行应用程序的设备连接到需要代理的无线网络,但未设置代理,则 GetIsNetworkAvailable 将返回 true。GetIsNetworkAvailable 何时返回 true 的另一个示例是,如果应用程序在连接到集线器或路由器的计算机上运行,而该集线器或路由器已失去上游连接。
- ping 网络上的主机名
Ping 和 PingReply 类允许应用程序通过从主机获取回复来确定远程计算机是否可以通过网络访问。这些类在 System.Net.NetworkInformation 命名空间中可用。以下示例显示如何 ping 主机。
protected bool CheckConnectivity(string ipAddress)
{
bool connectionExists = false;
try
{
System.Net.NetworkInformation.Ping pingSender = new System.Net.NetworkInformation.Ping();
System.Net.NetworkInformation.PingOptions options = new System.Net.NetworkInformation.PingOptions();
options.DontFragment = true;
if (!string.IsNullOrEmpty(ipAddress))
{
System.Net.NetworkInformation.PingReply reply = pingSender.Send(ipAddress);
connectionExists = reply.Status ==
System.Net.NetworkInformation.IPStatus.Success ? true : false;
}
}
catch (PingException ex)
{
Logger.LogException(ex.Message, ex);
}
return connectionExists;
}
备注(根据 MSDN):应用程序使用 Ping 类来检测远程计算机是否可访问。网络拓扑可以确定 Ping 是否可以成功联系到远程主机。代理、网络地址转换 (NAT) 设备或防火墙的存在和配置会阻止 Ping 成功。一个成功的 Ping 仅表明网络上可以访问远程主机;不能保证远程主机上存在更高级别的服务(例如 Web 服务器)。
欢迎评论/建议。快乐编码......!
接受的答案很快成功,但在没有连接时失败的速度很慢。所以我想建立一个健壮的连接检查,它会更快地失败。
据说并非所有环境都支持 Ping,所以我从接受的答案开始,并从这里添加了一个带有自定义超时的 WebClient。您可以选择任何超时,但通过 wifi 连接时,3 秒对我有用。我尝试添加一个快速迭代(1 秒),如果第一个迭代失败,则添加一个慢速迭代(3 秒)。但这没有任何意义,因为两次迭代总是会失败(未连接时)或总是成功(连接时)。
我正在连接到 AWS,因为我想在连接测试通过时上传文件。
public static class AwsHelpers
{
public static bool GetCanConnectToAws()
{
try
{
using (var client = new WebClientWithShortTimeout())
using (client.OpenRead("https://aws.amazon.com"))
return true;
}
catch
{
return false;
}
}
}
public class WebClientWithShortTimeout: WebClient
{
protected override WebRequest GetWebRequest(Uri uri)
{
var webRequest = base.GetWebRequest(uri);
webRequest.Timeout = 5000;
return webRequest;
}
}
bool bb = System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable();
if (bb == true)
MessageBox.Show("Internet connections are available");
else
MessageBox.Show("Internet connections are not available");
如果您想在发生网络/连接更改时通知用户/采取措施。
使用 NLM API:
public static bool Isconnected = false;
public static bool CheckForInternetConnection()
{
try
{
Ping myPing = new Ping();
String host = "google.com";
byte[] buffer = new byte[32];
int timeout = 1000;
PingOptions pingOptions = new PingOptions();
PingReply reply = myPing.Send(host, timeout, buffer, pingOptions);
if (reply.Status == IPStatus.Success)
{
return true;
}
else if (reply.Status == IPStatus.TimedOut)
{
return Isconnected;
}
else
{
return false;
}
}
catch (Exception)
{
return false;
}
}
public static void CheckConnection()
{
if (CheckForInternetConnection())
{
Isconnected = true;
}
else
{
Isconnected = false;
}
}
我个人认为Anton 和 moffeltje的答案最好,但我添加了一个检查以排除 VMWare 和其他人设置的虚拟网络。
public static bool IsAvailableNetworkActive()
{
// only recognizes changes related to Internet adapters
if (!System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable()) return false;
// however, this will include all adapters -- filter by opstatus and activity
NetworkInterface[] interfaces = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces();
return (from face in interfaces
where face.OperationalStatus == OperationalStatus.Up
where (face.NetworkInterfaceType != NetworkInterfaceType.Tunnel) && (face.NetworkInterfaceType != NetworkInterfaceType.Loopback)
where (!(face.Name.ToLower().Contains("virtual") || face.Description.ToLower().Contains("virtual")))
select face.GetIPv4Statistics()).Any(statistics => (statistics.BytesReceived > 0) && (statistics.BytesSent > 0));
}
ping 的多线程版本:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.NetworkInformation;
using System.Threading;
namespace OnlineCheck
{
class Program
{
static bool isOnline = false;
static void Main(string[] args)
{
List<string> ipList = new List<string> {
"1.1.1.1", // Bad ip
"2.2.2.2",
"4.2.2.2",
"8.8.8.8",
"9.9.9.9",
"208.67.222.222",
"139.130.4.5"
};
int timeOut = 1000 * 5; // Seconds
List<Thread> threadList = new List<Thread>();
foreach (string ip in ipList)
{
Thread threadTest = new Thread(() => IsOnline(ip));
threadList.Add(threadTest);
threadTest.Start();
}
Stopwatch stopwatch = Stopwatch.StartNew();
while (!isOnline && stopwatch.ElapsedMilliseconds <= timeOut)
{
Thread.Sleep(10); // Cooldown the CPU
}
foreach (Thread thread in threadList)
{
thread.Abort(); // We love threads, don't we?
}
Console.WriteLine("Am I online: " + isOnline.ToYesNo());
Console.ReadKey();
}
static bool Ping(string host, int timeout = 3000, int buffer = 32)
{
bool result = false;
try
{
Ping ping = new Ping();
byte[] byteBuffer = new byte[buffer];
PingOptions options = new PingOptions();
PingReply reply = ping.Send(host, timeout, byteBuffer, options);
result = (reply.Status == IPStatus.Success);
}
catch (Exception ex)
{
}
return result;
}
static void IsOnline(string host)
{
isOnline = Ping(host) || isOnline;
}
}
public static class BooleanExtensions
{
public static string ToYesNo(this bool value)
{
return value ? "Yes" : "No";
}
}
}
使用NetworkMonitor监控网络状态和互联网连接。
样本:
namespace AmRoNetworkMonitor.Demo
{
using System;
internal class Program
{
private static void Main()
{
NetworkMonitor.StateChanged += NetworkMonitor_StateChanged;
NetworkMonitor.StartMonitor();
Console.WriteLine("Press any key to stop monitoring.");
Console.ReadKey();
NetworkMonitor.StopMonitor();
Console.WriteLine("Press any key to close program.");
Console.ReadKey();
}
private static void NetworkMonitor_StateChanged(object sender, StateChangeEventArgs e)
{
Console.WriteLine(e.IsAvailable ? "Is Available" : "Is Not Available");
}
}
}
对于我的应用程序,我们还通过下载小文件进行测试。
string remoteUri = "https://www.microsoft.com/favicon.ico"
WebClient myWebClient = new WebClient();
try
{
byte[] myDataBuffer = myWebClient.DownloadData (remoteUri);
if(myDataBuffer.length > 0) // Or add more validate. eg. checksum
{
return true;
}
}
catch
{
return false;
}
还。一些 ISP 可能会使用中间服务器来缓存文件。添加随机未使用的参数,例如。https://www.microsoft.com/favicon.ico?req=random_number 可以防止缓存。
我在我的 3g 路由器/调制解调器上遇到了这些方法的问题,因为如果互联网断开连接,路由器会将页面重定向到其响应页面,所以你仍然会得到一个蒸汽并且你的代码认为有互联网。苹果(或其他)有一个热点检测页面,它总是返回一个特定的响应。以下示例返回“成功”响应。因此,您将完全确定您可以连接互联网并获得真正的响应!
public static bool CheckForInternetConnection()
{
try
{
using (var webClient = new WebClient())
using (var stream = webClient.OpenRead("http://captive.apple.com/hotspot-detect.html"))
{
if (stream != null)
{
//return true;
stream.ReadTimeout = 1000;
using (var reader = new StreamReader(stream, Encoding.UTF8, false))
{
string line;
while ((line = reader.ReadLine()) != null)
{
if (line == "<HTML><HEAD><TITLE>Success</TITLE></HEAD><BODY>Success</BODY></HTML>")
{
return true;
}
Console.WriteLine(line);
}
}
}
return false;
}
}
catch
{
}
return false;
}
我对 Internet 连接进行了三个测试。
- 参考
System.Net
和System.Net.Sockets
- 添加以下测试函数:
测试 1
public bool IsOnlineTest1()
{
try
{
IPHostEntry dummy = Dns.GetHostEntry("https://www.google.com");
return true;
}
catch (SocketException ex)
{
return false;
}
}
测试 2
public bool IsOnlineTest2()
{
try
{
IPHostEntry dummy = Dns.GetHostEntry("https://www.google.com");
return true;
}
catch (SocketException ex)
{
return false;
}
}
测试 3
public bool IsOnlineTest3()
{
System.Net.WebRequest req = System.Net.WebRequest.Create("https://www.google.com");
System.Net.WebResponse resp = default(System.Net.WebResponse);
try
{
resp = req.GetResponse();
resp.Close();
req = null;
return true;
}
catch (Exception ex)
{
req = null;
return false;
}
}
执行测试
如果你做一个Dictionary
并String
调用Boolean
,CheckList
你可以将每个测试的结果添加到CheckList
。
KeyValuePair
现在,使用for...each
循环遍历每个。
如果CheckList
包含 a Value
,true
那么您就知道有 Internet 连接。
public static bool HasConnection()
{
try
{
System.Net.IPHostEntry i = System.Net.Dns.GetHostEntry("www.google.com");
return true;
}
catch
{
return false;
}
}
这样可行