C#如何检测互联网连接/断开(使用事件)?

5

我写了这段代码,它可以完美地工作,但我担心每2秒钟ping一次会消耗太多的资源或者可能会对互联网连接造成一些问题。

        new Thread(() =>
        {
            if (CheckInternetConnection() == false)
            {
                Dispatcher.Invoke(new Action(delegate
                {
                    //internet access lost
                }));
            }
            else
            {
                Dispatcher.Invoke(new Action(delegate
                {
                    //internet access
                }));
            }

            Thread.Sleep(2000);
        }).Start();

    [DllImport("wininet.dll")]
    private extern static bool InternetGetConnectedState(out int Description, int ReservedValue);
    public static bool CheckInternetConnection()
    {
        int output = 0;
        return InternetGetConnectedState(out output, 0);
    }

这是两个事件,并非所有情况下都有效(仅在IP或网络卡更改时才有效)

NetworkChange.NetworkAvailabilityChanged += NetworkChange_NetworkAvailabilityChanged
NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged;

有人能帮我吗?

可能是使用.NET检查互联网连接的最佳方法是什么?的重复问题。 - codeMonkey
正如所述,你的问题太过宽泛。你抱怨“每2秒ping一次会消耗太多资源”,但你没有解释哪些资源被消耗得“太多”,也没有解释为什么你必须每两秒检查一次(而不是更长的时间间隔),也没有解释为什么你期望其他方法能够消耗更少的资源。 - Peter Duniho
请注意,NetworkChange.NetworkAvailabilityChanged 可以正常工作,但是有几个注意事项:1) 它不会告诉你是否有互联网访问权限,它只会告诉你是否至少有一个非环回网络适配器在工作;2) 通常会因为各种原因安装额外的网络适配器,即使你的主要互联网连接适配器被禁用或不可用,系统仍处于“网络可用”状态。 - Peter Duniho
看看这个方法,非常快速 https://dev59.com/jHI-5IYBdhLWcg3wED9V#25779403 - Vikas Sharma
3个回答

4
注意:关于您的原始解决方案,NetworkChange.NetworkAvailabilityChanged可以正常工作,但是有几个注意事项:1)它不会告诉您是否具有互联网访问权限,它只会告诉您是否至少有一个非环回网络适配器正在工作。2)通常会出现安装了各种原因的其他网络适配器,即使您的主要互联网连接适配器已被禁用/不可用,系统仍处于“网络可用”状态,感谢Peter Duniho的帮助。

由于网络连接不仅仅涉及路由器或网络卡,而且实际上涉及到您随时尝试连接的任何地方的每一跳。最简单和最可靠的方法就是ping一个众所周知的源,比如Google,或者使用某种心跳来连接您的互联网服务之一。

这是唯一可靠的方法的原因是,在您和外部世界之间可能发生任何数量的连通性问题。即使是主要的服务提供商也可能会崩溃。

因此,像Google这样的已知服务器的IMCP ping或在WebClient上调用OpenRead是两种有效的方法。相对而言,这些调用不昂贵,并且可以放入轻量级定时器或连续任务中。

至于您的评论,您可以在一定数量的失败后发出自定义事件来表示网络丢失以确保安全。

回答您的问题

但我担心每2秒ping一次会消耗太多资源或可能会在Internet连接方面产生一些问题。

无论是ping还是连接都非常便宜,涉及到CPU和网络流量的资源使用应该非常小。

注意:只需确保您正在ping或连接到具有高可用性的服务器,这将允许这种手法而不仅仅是阻止您。

Ping示例

using System.Net.NetworkInformation;

// Implementation 
using (var ping = new Ping())
{
   var reply = ping.Send("www.google.com");
   if (reply != null && reply.Status != IPStatus.Success)
   {
      // Raise an event
      // you might want to check for consistent failures 
      // before signalling a the Internet is down
   }
}

// Or if you wanted to get fancy ping multiple sources
private async Task<List<PingReply>> PingAsync(List<string> listOfIPs)
{
    Ping pingSender = new Ping();
    var tasks = listOfIPs.Select(ip => pingSender.SendPingAsync(ip, 2000));
    var results = await Task.WhenAll(tasks);

    return results.ToList();
}

连接示例

using System.Net; 

// Implementation 
try
{
    using (WebClient client = new WebClient())
    {
       using (client.OpenRead("http://www.google.com/"))
       {
          // success
       }
    }
}
catch
{
    // Raise an event
    // you might want to check for consistent failures 
    // before signalling the Internet is down
}

注意:这两种方法都有一个异步变体,它将返回一个 Task,可以等待IO绑定任务的 异步编程 模式更适合。

资源

Ping.Send 方法

Ping.SendAsync 方法

WebClient.OpenRead 方法

WebClient.OpenReadAsync 方法


“它不贵。”-- 因为您不知道原帖作者认为什么是“昂贵的”(他们现在使用的解决方案不应该有太多开销),所以您怎么能说发送ping不昂贵呢?当然,当您写“ping”时,您真正意思是什么?IMCP?检查HTTP服务器上的URL?还是其他什么?您的答案含糊不清,似乎不会对原帖作者有任何帮助,考虑到他们问题的具体性。 - Peter Duniho
我需要一个用于检测网络连接或断开的事件。 - Marius DV
@PeterDuniho 所说的都是有道理的,我以为这个问题会被埋在 SO 的坟场里,所以我没有花太多精力去解决它。 - TheGeneral
在我看来,当你看到一个问题可能会“死在SO墓地”时,通常有很好的理由,你不应该花费任何精力去解决它。除非你知道你的答案会有所帮助,并且除非你愿意付出足够的努力使其成为一个好的答案,否则不要发表回答。如果你想付出任何努力,请把你的努力放在让问题达到一个可以提供好的答案的程度上。 - Peter Duniho

1

NetworkInterface.GetIsNetworkAvailable() 不可靠,因为即使所有网络都没有连接到互联网,它也会返回 true。在我看来,检查连接的最佳方法是ping一个知名且速度快的在线资源。例如:

public static Boolean InternetAvailable()
{
    try
    {
        using (WebClient client = new WebClient())
        {
            using (client.OpenRead("http://www.google.com/"))
            {
                return true;
            }
        }
    }
    catch
    {
        return false;
    }
}

无论如何,您订阅的这两个事件并不像您想象的那样工作...实际上它们检查网络适配器的硬件状态...而不是它们是否连接到互联网。 它们与NetworkInterface.GetIsNetworkAvailable()具有相同的缺点。请继续检查连接性,使用单独的线程对安全源进行ping,并根据情况采取行动。您的Interop解决方案也非常出色。


-1

对公共资源进行ping操作会给您的应用程序带来额外的调用,并在循环中添加对该网站或任何您使用的内容的依赖。

如果您使用以下方法:NetworkInterface.GetIsNetworkAvailable(),这样做是否足以满足您的应用程序需求?

我在这里找到了它 https://learn.microsoft.com/en-us/dotnet/api/system.net.networkinformation.networkinterface.getisnetworkavailable?view=netframework-4.7.1#System_Net_NetworkInformation_NetworkInterface_GetIsNetworkAvailable


这个答案已经被OP明确排除了。他们抱怨NetworkChange.NetworkAvailabilityChanged对他们不起作用,可能是因为他们有一些与互联网访问无关的辅助网络适配器,即使互联网断开连接时仍然可用。您上面建议的方法只返回相同的信息(尽管效率较低,因为您必须轮询它而不是订阅事件)。 - Peter Duniho
那么在一定时间间隔内进行ping会更好。此外,我认为最好设置自己的服务,可以通过特定的主机名进行ping。 - Andy
这个选项的问题在于,即使网络没有连接到互联网,NetworkInterface.GetIsNetworkAvailable()也会返回true。 - Tommaso Belluzzo
@TommasoBelluzzo 我觉得你没有看我的先前评论 :( - Andy

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