使用.NET检查互联网连接的最佳方法是什么?

294

什么是在.NET中检查互联网连接的最快最有效的方法?


6
如果用户有互联网连接,那么用户可以连接到互联网。为了发送电子邮件日志。请注意:本翻译仅限于对原文进行逐字翻译,可能不是最佳表达方式。 - Mohit Deshpande
14
只需发送电子邮件。如果用户未连接,您可能会收到某种异常(无论如何,您都必须处理它)。 - Seth
19
请注意,无法检查用户是否连接到互联网;您只能知道他们曾经连接过。假设您有一个方法:"bool c = IsConnected(); if (c) { DoSomething(); }" - 在调用IsConnected和DoSomething之间,无线网络路由器可能已被拔掉。应将IsConnected更名为WasRecentlyConnected。 - Eric Lippert
Windows NLM API 应该是最适合的。https://dev59.com/02035IYBdhLWcg3wePzf#60585095 - Mahbubur Rahman
1
不知道你的用例是什么,但更明智的做法可能是更关注防火墙是否阻止访问你关心的服务器,而不是整个互联网。 - Chiramisu
显示剩余2条评论
27个回答

356

您可以使用以下代码,这段代码也适用于伊朗和中国 -

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

22
这可能比ping Google更好,因为我认为我们无法保证Google会继续响应ping。另一方面,我无法想象一个www.google.com不返回任何HTML的世界 :) - Daniel Vassallo
5
@Daniel:一方面是正确的,但另一方面,实际上下载整个网站有点繁琐,我认为。 - Leo
9
无论如何,没有理由撤回4KB - 只需使用client.OpenRead(url)即可。如果它没有抛出异常,则说明连接成功。 - Josh M.
15
实际上这样并不高效。如果没有网络连接,使用它会使我的程序重新启动超过1分钟。可能是因为尝试解析DNS。向谷歌DNS服务器(8.8.8.8)发送ping请求将时间缩短到3秒。 - MadBoy
23
例如在中国,无法想象谷歌网站(www.google.com)返回的不是一些HTML文件的世界。 - Jérôme MEVEL
显示剩余19条评论

105

你没有办法可靠地检查是否存在网络连接(我假设你的意思是访问互联网)。

然而,你可以请求那些几乎永远不会离线的资源,比如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;
}

36
「绝对没有可靠的方法可以检查是否有互联网连接。」 - woz
6
这只是检查你ping Google时是否能够连接成功。但如果你成功ping通后的瞬间,互联网就断了怎么办?在执行之前进行检查毫无意义。 - dbasnett
14
这如何与我回答的主要陈述相矛盾? - Leo
12
使用 "google.com" 需要解析,因此需要更多时间。相反,直接使用IP进行ping会更快。对于我来说,对Google公共DNS IP地址(8.8.8.88.8.4.4)进行ping是有效的。 - Mangesh
9
我希望重申一下:“注意 - 很多学校和办公室会阻止ping协议。”如果你正在使用这种方法来检查客户端的网络连接,我建议不要使用此方法。 - JKennedy
显示剩余7条评论

47

不要进行检查,直接执行操作(网页请求、电子邮件、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

10
没问题!就去做吧 - 但要准备好承担所有结果。 - Colonel Panic
4
请考虑以下内容:如果网络宕机了x个小时(例如,跟踪日志、重置路由器),则您需要“做某事”。 - Abir
3
我不是原帖作者,但我想这样做的原因是为了避免默认的100秒超时,如果没有网络连接的话。HTTP请求在后台线程上完成,所以UI线程不会被阻塞,但我的应用程序直到后台线程从HTTP请求中返回并终止后才能退出。我不想试图找到某个“适当”的超时值,而是希望在知道没有网络连接的情况下完全避免请求。 - Scott Smith
2
我的观点是,我们无法预测远程服务何时可用/不可用。另外,那些不响应ping的站点怎么办? - dbasnett
2
另一个使用案例是在线检查程序,它会在我的互联网恢复后立即向我发出警报。 - Ben Philipp
1
当然了解“客户端无法访问您的特定 Web 服务”和“客户端根本无法访问互联网”之间的区别是有用的。我之所以在10年后关注这个问题,是因为我希望当我的 Web 服务宕机时,本地应用程序在启动时具有不同的行为,而与其无法访问网络时的行为不同;如果您不知道发生了什么,那么你该如何“做好准备”呢? - Lovethenakedgun

37

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

好的,Justin Oringer。虽然我已经检查过了,但还需要再次验证。 - Kamran Shahid
2
对我来说最好的方法是,在开始例程之前,仍然检查我的服务连接性......我只是想避免异常。 - Wagner Bertolini Junior
8
这段代码只是检测网络电缆是否已插好。 - HackerMan
2
这段代码无法在Linux和MacOS上移植。 - weePee
1
嗨@weePee,如果你正在尝试使用.net core,请记住这篇文章是2014年之前的.net core时代。 - Kamran Shahid
显示剩余2条评论

16

对google.com进行ping操作会涉及到DNS解析的依赖。对8.8.8.8进行ping操作没问题,但是Google离我很远。我所需要做的就是ping互联网上距离我最近的东西。

我可以使用Ping的TTL(生存时间)功能来ping第一跳,然后ping第二跳,直到我从一个可路由的地址收到回复;如果该节点在可路由的地址上,则它在互联网上。对于我们大多数人来说,第一跳将是我们的本地网关/路由器,而第二跳将是我们光纤连接或其他任何东西另一端的第一个点。

这段代码对我有效,并且响应速度比本帖中的一些其他建议更快,因为它会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;
        }
    }
}

1
这是一个有趣的方法,与其他答案非常不同!(+1) - AnorZaken

15

测试网络连接是否正常,通过ping Google进行测试:

new Ping().Send("www.google.com.mx").Status == IPStatus.Success

8
附上一段描述会对不仅仅是问题的原作者,而是更多人有益。 - Boeckm
14
注意 - 很多学校和办公室会阻止ping协议。很傻,我知道。 - Colonel Panic
我找不到ping类。你能帮我吗?我正在使用UWP,其他获取网络信息的方法都无法工作。 - iam.Carrot
什么是www.google.com.mx?为什么不直接ping google.com? - AH.

13

我不同意那些说:"在执行任务之前检查连接有什么意义,因为检查后连接可能会丢失"的人。 当然,在我们作为开发者承担许多编程任务时存在一定的不确定性,但将不确定性降至可接受水平是挑战的一部分。

最近我遇到了一个问题,我正在开发一个应用程序,其中包含一个链接到在线瓷砖服务器的映射功能。当检测到没有互联网连接时,该功能将被禁用。

这个页面上的一些回答非常好,但是在缺乏连接的情况下主要导致性能问题,例如卡顿。

这是我最终采用的解决方案,借鉴了一些答案和我的同事的帮助:

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

11

我已查看上面列出的所有选项,唯一可行的检查互联网是否可用的选项是“Ping”选项。 导入[DllImport("Wininet.dll")]System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces()或任何其他变量的NetworkInterface类都无法很好地检测网络的可用性。这些方法仅检查网络电缆是否插入。

“Ping选项”

if(连接可用) 返回true

if(连接不可用且网络电缆已插入) 返回false

if(网络电缆未插入)抛出异常

网络接口

if(互联网可用)返回True

if(互联网不可用且网络电缆已插入)返回True

if(网络电缆未插入)返回false

[DllImport("Wininet.dll")]

if(互联网可用)返回True

if(互联网不可用且网络电缆已插入)返回True

if(网络电缆未插入)返回false

因此,在[DllImport("Wininet.dll")]NetworkInterface的情况下,没有办法知道互联网连接是否可用。


这不是真的,我导入了Wininet.dll,插上了网络电缆,并得到了正确的结果,即使互联网不可用的情况下。 - Waldemar Gałęzinowski

8

这并不能解决在检查和运行代码之间网络中断的问题,但相当可靠。

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

很好的想法,但就像你所说的可能不完美。您还可以缓存已发送/接收的字节以供将来检查。尽管仍不完美。 - Kind Contributor
此代码无法运行,请进行更正。它仅检查网络电缆是否已插入。 - HackerMan
1
根据MSDN的定义,只要任何网络接口被标记为“up”且不是环回或隧道接口,则认为存在网络连接。除了检查BytesSentBytesReceived之外,您基本上只是复制了已经在GetIsNetworkAvailable中存在的功能。 - AnorZaken

5

这里 是如何在安卓中实现的。

作为概念证明,我将这段代码翻译成了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
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接