我正在编写一个程序,向用户显示其IP地址,子网掩码和默认网关。我可以得到前两个,但是对于最后一个,这就是我出现的内容:
GatewayIPAddressInformationCollection gwc =
System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces()[0].GetIPProperties().GatewayAddresses;
当然会返回GatewayIPAddressInformation
的集合。因此,如果一台计算机具有多个网关,我如何确定哪个是[[default网关?
default
还是仅仅是猜测?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
[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
由于我的评论不足,我被迫将其添加为“问题”:
[[请参阅结尾以回复@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地址应该始终可用。
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);
}
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 APIpublic 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
http://www.pinvoke.net/default.aspx/iphlpapi.getbestinterface