获取本地IP地址

384

互联网上有很多地方向你展示如何获取IP地址。其中很多看起来像这个例子:

String strHostName = string.Empty;
// Getting Ip address of local machine...
// First get the host name of local machine.
strHostName = Dns.GetHostName();
Console.WriteLine("Local Machine's Host Name: " + strHostName);
// Then using host name, get the IP address list..
IPHostEntry ipEntry = Dns.GetHostEntry(strHostName);
IPAddress[] addr = ipEntry.AddressList;

for (int i = 0; i < addr.Length; i++)
{
    Console.WriteLine("IP Address {0}: {1} ", i, addr[i].ToString());
}
Console.ReadLine();

通过这个例子,我可以获得多个IP地址,但我只对获取路由器分配给运行程序的计算机的IP感兴趣:例如,如果某人想要访问我计算机上的共享文件夹,那么我会给他这个IP。

如果我没有连接到网络,并且直接通过调制解调器连接到互联网,则我希望得到一个错误。如何使用C#查看计算机是否连接到网络,如果连接,则获取局域网IP地址。


2
如果我没有连接到网络但连接到互联网,这个语句似乎有矛盾。你是想确定计算机连接到私人局域网还是互联网吗? - Andy
6
警告:一台计算机可以拥有多个IP接口,例如局域网和WiFi。如果您将服务绑定到特定的硬件设备(比如局域网),您需要该局域网的IP地址。以下大部分示例将返回找到的“第一个”或“最后一个”IP地址。如果您拥有超过2个IP地址,您的程序可能50%的时间正常工作,这取决于操作系统返回IP地址的随机顺序。 - Mark Lakata
@MarkLakata 我也考虑到了同样的问题。我在下面的答案中提供的函数可以处理它。你可以指定你想要从哪种类型的网络接口获取IP地址。 - compman2408
3
只是提一下,如果你在谷歌上搜索Unity3D,它们的API中有Network.player.ipAddress。 - Fattie
@MarkLakata 严格来说,“第一个”或“最后一个”IP 就是“正确”的IP,因为浏览器可能使用任何可用的IP。可能一个好的修正应该是返回与机器相关联的每个IP。 - UncaAlby
28个回答

559

获取本地 IP 地址:

public static string GetLocalIPAddress()
{
    var host = Dns.GetHostEntry(Dns.GetHostName());
    foreach (var ip in host.AddressList)
    {
        if (ip.AddressFamily == AddressFamily.InterNetwork)
        {
            return ip.ToString();
        }
    }
    throw new Exception("No network adapters with an IPv4 address in the system!");
}

要检查你是否已连接:

System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable();


44
如果使用vm虚拟盒、genymotion等软件,这将无法工作。 - PauLEffect
6
同意PaulEffect的观点。这种方法不仅对于多网卡并不好,而且不能区分IPv6和IPv4地址。 - Max
26
@John AddressFamily.InterNetwork代表IP v4,而AddressFamily.InterNetworkV6代表IP v6。 - Leonardo Xavier
3
我正在使用Geraldo H Answer,但如果有人也在使用它,你可能需要删除所有以数字1结尾的IP地址,这样就会删除环回和虚拟机/VMware默认IP地址。 - Leonardo Xavier
9
看起来你正在使用VMware或其他虚拟化软件。OP没有要求这个,所以我认为因此而进行贬值评分有点严厉。如果你有VMware或多个网卡,一些其他答案已经提供了相关提示。 - Mrchief
显示剩余11条评论

333

当本地机器有多个IP地址可用时,有一种更准确的方法。 连接 一个UDP套接字并读取其本地端点:

string localIP;
using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, 0))
{
    socket.Connect("8.8.8.8", 65530);
    IPEndPoint endPoint = socket.LocalEndPoint as IPEndPoint;
    localIP = endPoint.Address.ToString();
}

Connect在UDP套接字上的作用是:它设置了Send/Recv的目标地址,丢弃所有其他地址的数据包,并将套接字转换为“已连接”状态,设置其相应的字段。这包括根据系统路由表检查到目的地的路由是否存在并相应地设置本地端点。最后一部分似乎没有正式记录,但它看起来是Berkeley sockets API的一个基本特性(UDP“已连接”状态的副作用),可靠地适用于Windows和Linux的各种版本和分发版。

因此,该方法将提供将用于连接到指定远程主机的本地地址。并没有真正建立连接,因此指定的远程IP可能无法访问。


8
这是最适合我需求的解决方案。在我的情况下,我有许多网络卡和几个无线连接。我需要根据激活的连接获取首选的IP地址。 - BeanFlicker
19
如果没有网络连接,你就没有IP地址了。因此,这段代码仍然有效,但最好添加一个try..catch来处理这种情况,并在抛出SocketException时返回类似于“127.0.0.1”的东西。 - Russ
4
@PatrickSteele,是的,它返回特定目标的首选出站IP地址。如果您想获取本地网络(LAN) IP地址,请尝试将目标指定为来自您的LAN的某个IP地址。 - Mr.Wang from Next Door
3
这种方法适用于搭载 dotnet core 的 Mac,而目前被接受的答案则不支持。 - Pellet
12
这个解决方案的运行速度比被接受的回答(Mrchief)快大约4倍。这应该是真正的答案。 - Kamarey
显示剩余16条评论

150

我知道这可能是老生常谈,但也许这可以帮助到某些人。我已经在各个地方寻找查找本地IP地址的方法,但无论哪里都说要使用:

Dns.GetHostEntry(Dns.GetHostName());

我不喜欢这个程序,因为它只是获取分配给你的计算机的所有地址。如果你有多个网络接口(现在几乎所有计算机都有),你就不知道哪个地址对应哪个网络接口。经过大量研究后,我创建了一个函数来使用NetworkInterface类并从中提取信息。这样,我就可以知道它是什么类型的接口(以太网、无线、环回、隧道等),是否处于活动状态,以及更多信息。
public string GetLocalIPv4(NetworkInterfaceType _type)
{
    string output = "";
    foreach (NetworkInterface item in NetworkInterface.GetAllNetworkInterfaces())
    {
        if (item.NetworkInterfaceType == _type && item.OperationalStatus == OperationalStatus.Up)
        {
            foreach (UnicastIPAddressInformation ip in item.GetIPProperties().UnicastAddresses)
            {
                if (ip.Address.AddressFamily == AddressFamily.InterNetwork)
                {
                    output = ip.Address.ToString();
                }
            }
        }
    }
    return output;
}

现在,要获取您的以太网网络接口的IPv4地址,请调用以下命令:
GetLocalIPv4(NetworkInterfaceType.Ethernet);

或者您的无线接口:

GetLocalIPv4(NetworkInterfaceType.Wireless80211);

如果您尝试获取无线接口的IPv4地址,但是您的计算机没有安装无线网卡,则会返回空字符串。以太网接口也是同样的情况。
编辑:(感谢@NasBanov指出)尽管此函数提取IP地址的方式比使用“Dns.GetHostEntry(Dns.GetHostName())”要好得多,但它在支持相同类型的多个接口或单个接口上的多个IP地址方面表现不佳。当可能分配了多个地址时,它仅返回单个IP地址。要返回所有这些分配的地址,您可以简单地操作原始函数,始终返回数组而不是单个字符串。例如:
public static string[] GetAllLocalIPv4(NetworkInterfaceType _type)
{
    List<string> ipAddrList = new List<string>();
    foreach (NetworkInterface item in NetworkInterface.GetAllNetworkInterfaces())
    {
        if (item.NetworkInterfaceType == _type && item.OperationalStatus == OperationalStatus.Up)
        {
            foreach (UnicastIPAddressInformation ip in item.GetIPProperties().UnicastAddresses)
            {
                if (ip.Address.AddressFamily == AddressFamily.InterNetwork)
                {
                    ipAddrList.Add(ip.Address.ToString());
                }
            }
        }
    }
    return ipAddrList.ToArray();
}

现在这个函数将返回特定接口类型的所有分配地址。如果只需要一个字符串,可以使用 .FirstOrDefault() 扩展来返回数组中的第一个项目,如果为空,则返回空字符串。
GetLocalIPv4(NetworkInterfaceType.Ethernet).FirstOrDefault();

8
这是一个更好的解决方案,因为许多地方都无法使用DNS,并且接口可以有多个IP地址。我也使用类似的方法。 - Mert Gülsoy
问题在于每个接口只返回一个IP地址...即最后一个IP,foreach。 - Nas Banov
@NasBanov 这个函数只设置为获取IPv4地址,所以我甚至不会谈论IPv6及其当前的无用性。我知道多宿主是连接到多个接口上的多个网络,但从未听说过在同一接口上连接到多个网络。尽管似乎你是正确的,实际上这是可能的,但使用NIC的<1%的人只需更改函数以返回数组即可。 - compman2408
1
谢谢您。FYI,在OSX上的.NET 3.5 mono中,item.OperationalStatus始终返回Unknown - gman
1
@PeterMoore 如果我没记错的话,item.OperationalStatus == OperationalStatus.Up 这部分代码是用来判断网卡是否处于连接状态并能够发送数据包。但这并不能测试与互联网的连接情况,只是表示网卡已经连接到了网络。此外,这个函数(按照它的写法)只会查看一种类型的网卡,而不是查看计算机上所有的网卡以返回“最相关”的IP地址。如果函数有传入参数,它将返回分配给该类型网卡的IP地址。 - compman2408
显示剩余5条评论

120

重构Mrcheif的代码,利用Linq(即.Net 3.0+)。

private IPAddress LocalIPAddress()
{
    if (!System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable())
    {
        return null;
    }

    IPHostEntry host = Dns.GetHostEntry(Dns.GetHostName());

    return host
        .AddressList
        .FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork);
}

:)


我有几个IP地址作为“Inter Network”,这个解决方案实际上有效并返回了正确的一个。Mrchief的另一个解决方案只给了我最后一个。所以实际上这个应该是正确的 ;) - Keenora Fluffball
32
@KeenoraFluffball - 这个会给你第一个,而这个会给你最后一个(或者反之,取决于列表的构建方式)。无论哪种情况,都不是正确的方法——如果给你多个IP地址,你需要知道你正在使用哪个网络。通过猜测第一个或最后一个来确定网络并不是正确的解决方案。 - gbjbaanb
可能还需要包括 AddressFamily.InterNetworkV6。 - joelsand
如果您还需要处理独立个人电脑,那么如果网络不可用时返回 null 是没有用的。我将其替换为"return IPAddress.Loopback;",对应于特殊的 IP 地址 127.0.0.1。 - Christian

47

这是一个经过修改的版本(来自compman2408的版本),对我有效:

    internal static string GetLocalIPv4(NetworkInterfaceType _type)
    {  // Checks your IP adress from the local network connected to a gateway. This to avoid issues with double network cards
        string output = "";  // default output
        foreach (NetworkInterface item in NetworkInterface.GetAllNetworkInterfaces()) // Iterate over each network interface
        {  // Find the network interface which has been provided in the arguments, break the loop if found
            if (item.NetworkInterfaceType == _type && item.OperationalStatus == OperationalStatus.Up)
            {   // Fetch the properties of this adapter
                IPInterfaceProperties adapterProperties = item.GetIPProperties();
                // Check if the gateway adress exist, if not its most likley a virtual network or smth
                if (adapterProperties.GatewayAddresses.FirstOrDefault() != null)
                {   // Iterate over each available unicast adresses
                    foreach (UnicastIPAddressInformation ip in adapterProperties.UnicastAddresses)
                    {   // If the IP is a local IPv4 adress
                        if (ip.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                        {   // we got a match!
                            output = ip.Address.ToString();
                            break;  // break the loop!!
                        }
                    }
                }
            }
            // Check if we got a result if so break this method
            if (output != "") { break; }
        }
        // Return results
        return output;
    }

你可以像这样调用该方法:

GetLocalIPv4(NetworkInterfaceType.Ethernet);

更改内容:我正在从分配了网关IP的适配器中检索IP。 第二个更改:我已经添加了文档字符串和break语句,以使此方法更加有效。


4
与它派生自的代码一样存在相同的问题:它只返回其中一个IP地址,随机选择一个IP地址 - 这不一定是你需要的那个。这赢得了“它在我的机器上工作奖”。 - Nas Banov
1
@NasBanov,当然,我确实赚到了,正如我在帖子中所说的:“对我有用”。 :-) - Gerardo H
这是正确的方法,感谢您提供了这个。当您安装了回环交换机模拟器时,所有其他变体都失败了,而这个成功了! - DiamondDrake
1
为什么你只返回找到的最后一个IP地址,而不是第一个?在最内层的if语句中使用简单的“break”或“return”即可解决问题。 - Martin Mulder
最佳答案!在2021年运行良好。非常感谢 :) - Shariful Islam Mubin

29

这是我找到的最好的代码,可以获取当前IP地址,避免获取VMWare主机或其他无效的IP地址。

public string GetLocalIpAddress()
{
    UnicastIPAddressInformation mostSuitableIp = null;

    var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();

    foreach (var network in networkInterfaces)
    {
        if (network.OperationalStatus != OperationalStatus.Up)
            continue;

        var properties = network.GetIPProperties();

        if (properties.GatewayAddresses.Count == 0)
            continue;

        foreach (var address in properties.UnicastAddresses)
        {
            if (address.Address.AddressFamily != AddressFamily.InterNetwork)
                continue;

            if (IPAddress.IsLoopback(address.Address))
                continue;

            if (!address.IsDnsEligible)
            {
                if (mostSuitableIp == null)
                    mostSuitableIp = address;
                continue;
            }

            // The best IP is the IP got from DHCP server
            if (address.PrefixOrigin != PrefixOrigin.Dhcp)
            {
                if (mostSuitableIp == null || !mostSuitableIp.IsDnsEligible)
                    mostSuitableIp = address;
                continue;
            }

            return address.Address.ToString();
        }
    }

    return mostSuitableIp != null 
        ? mostSuitableIp.Address.ToString()
        : "";
}

你应该解释一下,为什么这段代码是答案的解决方案。如果你连接到互联网,你是否能够得到问题的答案,这会导致错误吗? - Philipp M
其他方法不使用IsDnsEligible和PrefixOrigin验证。 - rodcesar.santos
如果地址不是DNS合格的,则为保留的内部IP地址。它不是互联网服务提供商主机。如果PrefixOrigin由DHCP服务器提供,则可能这是最佳地址选择。这是对我有效的唯一功能! - rodcesar.santos
1
@user1551843 - 太好了,谢谢你。我之前一直在使用已废弃的 GetHostByName 方法,但它返回的是 VMWare 适配器的 IP :) - Jason Newland
这是我在VPN + 无线网络上唯一有效的解决方案。谢谢。 - jay-danger
谢谢。它实际上获取了由路由器分配的本地IP,而不像其他解决方案。 - mihaiconst

25

我认为使用LINQ更容易:

Dns.GetHostEntry(Dns.GetHostName())
   .AddressList
   .First(x => x.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
   .ToString()

1
除了那个讨论,对我来说它运行得很好。我想建议用?.替换.,用?.FirstOrDefault替换.First,并在末尾添加??"" - 这样你就可以安全地使用它而不会出现空异常 - 如果无法检索IP,它将返回一个空字符串。 - Matt
@Matt 返回一个空字符串可能会在代码的其他地方产生意想不到的结果,但我认为你不会得到 NRE,因为 .First 已经会抛出异常了。但确实根据您的用例,您可以使用 .FirstOrDefault 并检查 null - Kapé
实际上,不必追加 ??"",这取决于使用情况。但是,这将分配 null,如果无法获取 IP,则可以轻松检查:var ip = Dns?.GetHostEntry(Dns.GetHostName())?.AddressList?.FirstOrDefault(x => x.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)?.ToString(); 因此,您不会收到 NRE - 您只需继续执行 if (ip != null) { ... // IP 已获取 ...} - Matt
我认为相关的问题是:你想要抛出异常吗?还是与代码保持一致(异常通常会“跳转”到代码逻辑丢失的地方),并沿着流程进行检查?关于.First() - 我经历过这也可能会引发不必要的异常,所以在可能的情况下,我使用.FirstOrDefault()。另一个问题是:在代码的哪个深度级别上,您想要捕获异常?您会重新抛出它们还是在原地处理它们?此外:通常,if语句更易于他人阅读。 - Matt

11

使用 LINQ 表达式获取 IP 的另一种方法:

public static List<string> GetAllLocalIPv4(NetworkInterfaceType type)
{
    return NetworkInterface.GetAllNetworkInterfaces()
                   .Where(x => x.NetworkInterfaceType == type && x.OperationalStatus == OperationalStatus.Up)
                   .SelectMany(x => x.GetIPProperties().UnicastAddresses)
                   .Where(x => x.Address.AddressFamily == AddressFamily.InterNetwork)
                   .Select(x => x.Address.ToString())
                   .ToList();
}

10

为了好玩,我尝试使用新的C# 6空引用条件运算符编写单个LINQ语句。看起来相当疯狂且可能效率很低,但它能够工作。

private string GetLocalIPv4(NetworkInterfaceType type = NetworkInterfaceType.Ethernet)
{
    // Bastardized from: https://dev59.com/nmw15IYBdhLWcg3wIoVM#28621250.

    return NetworkInterface
        .GetAllNetworkInterfaces()
        .FirstOrDefault(ni =>
            ni.NetworkInterfaceType == type
            && ni.OperationalStatus == OperationalStatus.Up
            && ni.GetIPProperties().GatewayAddresses.FirstOrDefault() != null
            && ni.GetIPProperties().UnicastAddresses.FirstOrDefault(ip => ip.Address.AddressFamily == AddressFamily.InterNetwork) != null
        )
        ?.GetIPProperties()
        .UnicastAddresses
        .FirstOrDefault(ip => ip.Address.AddressFamily == AddressFamily.InterNetwork)
        ?.Address
        ?.ToString()
        ?? string.Empty;
}

逻辑由Gerardo H(参考compman2408)提供。


所以...不是我会写的。这种奇怪的"?."链式LINQ-to-Objects可以通过使用Select/SelectMany(SelectMany可以增加或减少长度)进行转换来重写(或“修复”),使得最终形式为IEnumeable<string>,以便"FirstOrDefault() ?? string.Empty"就足够了。 - user2864740
我不确定你是否可以认为空条件运算符是“奇怪的”,但我认为这段代码(正如我所说)确实“相当疯狂”。 - Stajs
使用第一个空值条件语句后,它们之后的所有语句都应该具有该语句。如果没有任何网络接口与其匹配,则在“.UnicastAddresses”上会抛出空指针异常。 - Garr Godfrey

10

测试过单个或多个局域网卡和虚拟机

public static string DisplayIPAddresses()
    {
        string returnAddress = String.Empty;

        // Get a list of all network interfaces (usually one per network card, dialup, and VPN connection)
        NetworkInterface[] networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();

        foreach (NetworkInterface network in networkInterfaces)
        {
            // Read the IP configuration for each network
            IPInterfaceProperties properties = network.GetIPProperties();

            if (network.NetworkInterfaceType == NetworkInterfaceType.Ethernet &&
                   network.OperationalStatus == OperationalStatus.Up &&
                   !network.Description.ToLower().Contains("virtual") &&
                   !network.Description.ToLower().Contains("pseudo"))
            {
                // Each network interface may have multiple IP addresses
                foreach (IPAddressInformation address in properties.UnicastAddresses)
                {
                    // We're only interested in IPv4 addresses for now
                    if (address.Address.AddressFamily != AddressFamily.InterNetwork)
                        continue;

                    // Ignore loopback addresses (e.g., 127.0.0.1)
                    if (IPAddress.IsLoopback(address.Address))
                        continue;

                    returnAddress = address.Address.ToString();
                    Console.WriteLine(address.Address.ToString() + " (" + network.Name + " - " + network.Description + ")");
                }
            }
        }

       return returnAddress;
    }

请注意,此代码仅适用于以太网。移除NetworkInterfaceType限制以支持Wi-Fi。 - scor4er

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