获取默认网关

问题描述 投票:26回答:8

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

GatewayIPAddressInformationCollection gwc = 
    System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces()[0].GetIPProperties().GatewayAddresses;

当然会返回GatewayIPAddressInformation的集合。因此,如果一台计算机具有多个网关,我如何确定哪个是[[default网关?

实际上,我只见过这个集合包含一个条目,但是由于它是作为一个集合实现的,因此有理由认为某些计算机包含多个网关,而这些网关均未标记为“默认”。因此,有没有一种方法可以确定

default

还是仅仅是猜测?
c# network-programming network-protocols
8个回答
39
投票
它应该是第一个启用的网络接口的第一个有效且启用的网关地址:

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来区分IPv4和IPv6。如果您在返回0.0.0.0地址时遇到问题,可以使用后一种方法。

也就是说,推荐的方法是使用GetBestInterface查找用于路由到特定IP地址的接口。如果您已经有了目标IP地址,那么最好使用它-因此,我还在下面提供了一个示例:

GetBestInterface


4
投票
[DllImport("iphlpapi.dll", CharSet = CharSet.Auto)] private static extern int GetBestInterface(UInt32 destAddr, out UInt32 bestIfIndex); public static IPAddress GetGatewayForDestination(IPAddress destinationAddress) { UInt32 destaddr = BitConverter.ToUInt32(destinationAddress.GetAddressBytes(), 0); uint interfaceIndex; int result = GetBestInterface(destaddr, out interfaceIndex); if (result != 0) throw new Win32Exception(result); foreach (var ni in NetworkInterface.GetAllNetworkInterfaces()) { var niprops = ni.GetIPProperties(); if (niprops == null) continue; var gateway = niprops.GatewayAddresses?.FirstOrDefault()?.Address; if (gateway == null) continue; if (ni.Supports(NetworkInterfaceComponent.IPv4)) { var v4props = niprops.GetIPv4Properties(); if (v4props == null) continue; if (v4props.Index == interfaceIndex) return gateway; } if (ni.Supports(NetworkInterfaceComponent.IPv6)) { var v6props = niprops.GetIPv6Properties(); if (v6props == null) continue; if (v6props.Index == interfaceIndex) return gateway; } } return null; } 命令返回的第一个IP地址将是网关。您可以使用此事实来获取网关。traceroute的一个很好的实现可以在这里找到:tracerout

2
投票
我知道这是一个稍旧的问题,但是,我刚发现需要检索本地网关的IPV4地址。对于我自己的系统,可接受的答案不太合适,因此,我将其修改为套件,并且我确定其他人也可以使用此解决方案。

由于我的评论不足,我被迫将其添加为“问题”:

TraceRoute and Ping in C#


2
投票
曾经寻找一种奇怪的技巧来确定本地IP地址?相对于以前的方法,它是否非常健壮?

[[请参阅结尾以回复@caesay]

这可能是您要寻找的。

public static IPAddress GetDefaultGateway() { IPAddress result = null; var cards = NetworkInterface.GetAllNetworkInterfaces().ToList(); if (cards.Any()) { foreach (var card in cards) { var props = card.GetIPProperties(); if (props == null) continue; var gateways = props.GatewayAddresses; if (!gateways.Any()) continue; var gateway = gateways.FirstOrDefault(g => g.Address.AddressFamily.ToString() == "InterNetwork"); if (gateway == null) continue; result = gateway.Address; break; }; } return result; }

这将创建一个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系统调用,而不会延迟或懒于调用OS。 Win32绑定页面说:“应用程序可以在调用bind之后使用getsockname来学习地址和已分配给套接字的端口。如果Internet地址等于INADDR_ANY或in6addr_any,则在套接字成功之前,getsockname不一定能提供地址。连接的”。现在已经明确解决了这种情况,并且在检查了框架源之后,在Connect()调用之后,IP地址应该始终可用。


1
投票
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); }

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

NetworkInterface[] allNICs = NetworkInterface.GetAllNetworkInterfaces(); foreach (var nic in allNICs) { var ipProp = nic.GetIPProperties(); var gwAddresses = ipProp.GatewayAddresses; if (gwAddresses.Count > 0 && gwAddresses.FirstOrDefault(g => g.Address.AddressFamily == AddressFamily.InterNetwork) != null) { localIP = ipProp.UnicastAddresses.First(d => d.Address.AddressFamily == AddressFamily.InterNetwork).Address; } } Debug.Print(localIP.ToString());

我警告这不是一个完整的解决方案,如果您正在寻找负责互联网访问的主界面,则应结合使用其他方法,例如使用win32 API 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;
}
来找到最佳的接口以连接到目的地地址。

您可以在此处找到示例用法:GetBestInterface


1
投票
我刚刚碰到了这一点,将使用以下代码-基本上,它查找没有环回且已启动且具有网关和单播地址的接口。然后从接口获得一个非瞬态IPV4地址。

http://www.pinvoke.net/default.aspx/iphlpapi.getbestinterface


0
投票
我认为您应该遍历此集合并显示所有网关,因为如果有许多适配器网关,它们都被视为该适配器的默认网关
© www.soinside.com 2019 - 2024. All rights reserved.