Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/262.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 获取默认网关_C#_Network Programming_Network Protocols - Fatal编程技术网

C# 获取默认网关

C# 获取默认网关,c#,network-programming,network-protocols,C#,Network Programming,Network Protocols,我正在编写一个程序,向用户显示他们的IP地址、子网掩码和默认网关。我可以得到前两个,但对于最后一个,这是我发现的: GatewayIPAddressInformationCollection gwc = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces()[0].GetIPProperties().GatewayAddresses; 当然,它会返回一组网关IP地址信息。因此,如果一台计算机有多

我正在编写一个程序,向用户显示他们的IP地址、子网掩码和默认网关。我可以得到前两个,但对于最后一个,这是我发现的:

GatewayIPAddressInformationCollection gwc = 
    System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces()[0].GetIPProperties().GatewayAddresses;
当然,它会返回一组
网关IP地址信息
。因此,如果一台计算机有多个网关,如何确定哪个是默认网关

实际上,我只见过这个集合包含一个条目,但由于它是作为集合实现的,因此一些计算机包含多个网关是理所当然的,没有一个网关被标记为“默认”。那么,有没有办法确定默认值,还是只是猜测?

它可能是第一个启用的网络接口的第一个有效和启用的网关地址:

public static IPAddress GetDefaultGateway()
{
    return NetworkInterface
        .GetAllNetworkInterfaces()
        .Where(n => n.OperationalStatus == OperationalStatus.Up)
        .Where(n => n.NetworkInterfaceType != NetworkInterfaceType.Loopback)
        .SelectMany(n => n.GetIPProperties()?.GatewayAddresses)
        .Select(g => g?.Address)
        .Where(a => a != null)
         // .Where(a => a.AddressFamily == AddressFamily.InterNetwork)
         // .Where(a => Array.FindIndex(a.GetAddressBytes(), b => b != 0) >= 0)
        .FirstOrDefault();
}
我还添加了一些进一步的注释检查,其他人在这里指出这些检查很有用。您可以检查
AddressFamily
one以区分IPv4和IPv6。后者可用于过滤掉0.0.0.0地址

上述解决方案将为您提供一个有效/连接的接口,对于99%的情况来说已经足够好了。这就是说,如果您有多个有效接口,流量可以通过这些接口进行路由,并且您需要100%准确,那么实现这一点的方法是找到一个接口,用于路由到特定的IP地址。这还可以处理您可能通过不同适配器路由特定地址范围的情况(例如,
10.*.*.
通过VPN,其他所有内容都进入您的路由器)


这两个示例可以封装到一个helper类中,并在适当的情况下使用:您有,或者您没有目标地址。

我认为您应该迭代此集合并显示所有网关,因为如果一个适配器有许多网关,那么它们都被认为是该适配器的默认网关,我知道这是一个稍微老一点的问题,但我刚刚发现需要检索本地网关的IPV4地址。当涉及到我自己的系统时,公认的答案并不完全符合要求,因此,我将其修改为suite,我相信其他人也可以使用此解决方案

由于我还没有足够的代表发表评论,我不得不将此作为“问题”添加:


traceroute
命令返回的第一个IP地址将是网关。您可以使用此事实来获取网关。
tracerout
的一个很好的实现可以在这里找到:

根据@midspace对@caesay答案的评论,这是一个更好的答案:

public static IPAddress GetDefaultGateway()
{
    var gateway_address = NetworkInterface.GetAllNetworkInterfaces()
        .Where(e => e.OperationalStatus == OperationalStatus.Up)
        .SelectMany(e => e.GetIPProperties().GatewayAddresses)
        .FirstOrDefault();
    if (gateway_address == null) return null;
    return gateway_address.Address;
}
我警告说,这不是一个完整的解决方案,如果您正在寻找负责internet访问的主接口,您应该结合使用win32 API等其他方法,找到连接到目标地址的最佳接口


您可以在这里找到示例用法:

我刚刚遇到了这个问题,并将使用以下代码-基本上,它寻找的接口不是环回的,并且是启动的,并且具有网关和单播地址。然后,我从接口获得一个非瞬时IPV4地址

public static class NetworkInterfaces
{
    public static NetworkInterface GetDefaultInterface()
    {
        var interfaces = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces();
        foreach (var intf in interfaces)
        {
            if (intf.OperationalStatus != OperationalStatus.Up)
            {
                continue;
            }
            if (intf.NetworkInterfaceType == NetworkInterfaceType.Loopback)
            {
                continue;
            }

            var properties = intf.GetIPProperties();
            if (properties == null)
            {
                continue;
            }
            var gateways = properties.GatewayAddresses;
            if ((gateways == null) || (gateways.Count == 0))
            {
                continue;
            }
            var addresses = properties.UnicastAddresses;
            if ((addresses == null) || (addresses.Count == 0))
            {
                continue;
            }
            return intf;
        }
        return null;
    }

    public static IPAddress GetDefaultIPV4Address(NetworkInterface intf)
    {
        if (intf == null)
        {
            return null;
        }
        foreach (var address in intf.GetIPProperties().UnicastAddresses)
        {
            if (address.Address.AddressFamily != AddressFamily.InterNetwork)
            {
                continue;
            }
            if (address.IsTransient)
            {
                continue;
            }
            return address.Address;
        }
        return null;
    }
}

一直在寻找一个奇怪的技巧来确定本地IP地址? 然而,与早期的方法相比,它是否非常可靠

[对@caesay的回复见结尾]

这可能就是你要找的

using (var s = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Dgram,ProtocolType.Udp))
{
    s.Bind( new System.Net.IPEndPoint(System.Net.IPAddress.Any,0));
    s.Connect("google.com",0);

    var ipaddr = s.LocalEndPoint as System.Net.IPEndPoint;
    var addr = ipaddr.Address.ToString();
    Console.WriteLine(addr);
}
这将创建UDP套接字,但不会在其上发送数据。 然后可以查看套接字绑定到的本地接口和地址。 您必须提供我使用google.com的IP地址或主机名,这将 被操作系统用来确定套接字将绑定到哪个接口, 以及使用此方法可以找到的IP地址。 对于您使用的99%以上的IP地址或主机名,您将获得用于通过默认路由进行通信的接口+地址

虽然这有点迟钝,但我怀疑它比上面的方法更可靠,人们使用PInvoke调用并扫描结构以查看是否可以找到接口

我当然发现在我的3x双界面系统上,它比扫描NetworkInterface.GetAllNetworkInterfaces()的结果更健壮,使用启发式方法试图找出默认的接口地址

[更新/回复@caesay 2/6/2019] @caesay在这里提出了一个很好的观点,因此我将示例从使用UdpClient切换到了Socket,并在Bind()之后显式地调用了Socket.Connect()。我还回顾了Connect()和LocalEndPoint的框架源代码,它们都立即调用相应的Win32 OS系统调用,而不延迟或懒于调用操作系统。Win32 bind页面显示“在调用bind后,应用程序可以使用getsockname来了解已分配给套接字的地址和端口。如果Internet地址等于INADDR_ANY或in6addr_ANY,则getsockname在连接套接字之前不必提供地址”。 这个问题现在已经明确解决了,在查看了框架源代码之后,在Connect()调用之后,IP地址应该总是可用的。

(发布之后,我注意到前面的一个回答提到了使用traceroute的想法。如果我看到这个,我可能不会响应。但我已经包含了完整/紧凑的代码。)

这里有一种通过使用traceroute类型方法查找默认网关的不同方法。使用TTL为1的internet(或您想要访问的其他网络)上的Ping-an地址,并让Ping告诉您第一跳IP地址是什么。那应该是你要找的入口

using (var s = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Dgram,ProtocolType.Udp))
{
    s.Bind( new System.Net.IPEndPoint(System.Net.IPAddress.Any,0));
    s.Connect("google.com",0);

    var ipaddr = s.LocalEndPoint as System.Net.IPEndPoint;
    var addr = ipaddr.Address.ToString();
    Console.WriteLine(addr);
}
我想如果网关配置为不回复ping,这可能会出错,但我想我从来没有听说过这样设置的网络

注意:如果您向其传递IPV6地址,这种例程甚至可以工作。潜在的问题是,它似乎返回具有全局作用域的IPV6地址,而不是链接本地作用域。我没有足够的使用IPV6的经验来判断这是好是坏。但我
public static class NetworkInterfaces
{
    public static NetworkInterface GetDefaultInterface()
    {
        var interfaces = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces();
        foreach (var intf in interfaces)
        {
            if (intf.OperationalStatus != OperationalStatus.Up)
            {
                continue;
            }
            if (intf.NetworkInterfaceType == NetworkInterfaceType.Loopback)
            {
                continue;
            }

            var properties = intf.GetIPProperties();
            if (properties == null)
            {
                continue;
            }
            var gateways = properties.GatewayAddresses;
            if ((gateways == null) || (gateways.Count == 0))
            {
                continue;
            }
            var addresses = properties.UnicastAddresses;
            if ((addresses == null) || (addresses.Count == 0))
            {
                continue;
            }
            return intf;
        }
        return null;
    }

    public static IPAddress GetDefaultIPV4Address(NetworkInterface intf)
    {
        if (intf == null)
        {
            return null;
        }
        foreach (var address in intf.GetIPProperties().UnicastAddresses)
        {
            if (address.Address.AddressFamily != AddressFamily.InterNetwork)
            {
                continue;
            }
            if (address.IsTransient)
            {
                continue;
            }
            return address.Address;
        }
        return null;
    }
}
using (var s = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Dgram,ProtocolType.Udp))
{
    s.Bind( new System.Net.IPEndPoint(System.Net.IPAddress.Any,0));
    s.Connect("google.com",0);

    var ipaddr = s.LocalEndPoint as System.Net.IPEndPoint;
    var addr = ipaddr.Address.ToString();
    Console.WriteLine(addr);
}