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