如何获取运行我的C#应用程序的服务器的IP地址?

373

我正在运行一个服务器,并且希望显示我的自身IP地址。

获取计算机的(如果可能,外部)IP地址的语法是什么?

有人编写了以下代码。

IPHostEntry host;
string localIP = "?";
host = Dns.GetHostEntry(Dns.GetHostName());
foreach (IPAddress ip in host.AddressList)
{
    if (ip.AddressFamily.ToString() == "InterNetwork")
    {
        localIP = ip.ToString();
    }
}
return localIP;

然而,我通常不信任这个作者,也不理解这段代码。是否有更好的方法?


1
关于外部IP地址,我不认为有一种本地方法可以检索它。localhost可能在NAT路由器后面,将本地网络地址转换为公共地址。有没有(本地)方法可以验证是否是这种情况?我不知道任何方法... - Thiago Arrais
该示例使用 DNS 获取 IP 地址,我曾经遇到过 DNS 信息错误的情况。对于这种情况,示例可能会回复 错误 信息。 - leiflundgren
@leiflundgren 我也曾经遇到 DNS 信息错误的情况。当我面对这种情况时,我的回答描述了我如何在不依赖 DNS 的情况下获得所需的 IP 地址。 - Dr. Wily's Apprentice
13
使用LINQ:Dns.GetHostEntry(Dns.GetHostName()).AddressList.Where(o => o.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork).First().ToString() - Luis Perez
2
这是一个典型的情况,不同需求的用户往往会问相同的问题。有些人想知道如何从公共网络访问他们的计算机。规范答案是 STUN,尽管许多人会提供依赖于随机第三方的黑客解决方案。有些人只想知道他们在本地网络上的 IP 地址。在这种情况下,好的答案会提到 NetworkInterface.GetAllNetworkInterfaces Method - Stéphane Gourichon
27个回答

240

不,那基本上是最好的方法了。由于机器可能有多个IP地址,您需要迭代它们的集合以找到适当的一个。

编辑:我会改变的唯一一件事是更改此处:

if (ip.AddressFamily.ToString() == "InterNetwork")

变成这个:

if (ip.AddressFamily == AddressFamily.InterNetwork)

不需要对枚举类型调用 ToString 方法来进行比较。


3
如果可能的话,我想要获得外部IP地址。但是如果我在NAT之后,这可能不太可能实现。 - Nefzen
3
不,你的机器只能知道它的NAT地址。 - Andrew Hare
1
我非常确定你需要访问外部服务器来获取外部地址。 - Thiago Arrais
29
我建议在找到IP后使用“break”语句,以避免在集合中进一步迭代(在这种情况下,我怀疑性能影响永远不会有影响,但我喜欢强调良好的编码习惯)。 - Eric J.
7
请注意,当一台机器有多个“InterNetwork”端口时(比如我这种情况:一个以太网卡和一个虚拟机端口),这个方法可能会失败。当前的代码将会给出列表中最后一个IP地址。 - Christian Studer
显示剩余3条评论

169

知道你的公共IP的唯一方式是询问他人告诉你;此代码可能会帮助你:

public string GetPublicIP()
{
    String direction = "";
    WebRequest request = WebRequest.Create("http://checkip.dyndns.org/");
    using (WebResponse response = request.GetResponse())
    using (StreamReader stream = new StreamReader(response.GetResponseStream()))
    {
        direction = stream.ReadToEnd();
    }

    //Search for the ip in the html
    int first = direction.IndexOf("Address: ") + 9;
    int last = direction.LastIndexOf("</body>");
    direction = direction.Substring(first, last - first);

    return direction;
}

23
你知道吗,你的代码示例被提到了微软学院《二十个C#问题详解》的第13题中。演讲者为使用你的代码向你道歉。从8分30秒开始。请查看此链接:[http://www.microsoftvirtualacademy.com/Content/ViewContent.aspx?et=8464&m=8456&ct=28536] :) - user2609980
4
抱歉链接失效了。 - Baz Guvenkaya
如果有人想查看的话,这是一个新链接:https://mva.microsoft.com/en-us/training-courses/twenty-c-questions-answered-8298?l=JpBF7UYy_4104984382 - Kimmax
2
请使用链接http://ipof.in/txt,这样您可以直接获取IP,而无需进行所有HTML解析代码。 - vivekv

83

更干净且一体化的解决方案 :D

//This returns the first IP4 address or null
return Dns.GetHostEntry(Dns.GetHostName()).AddressList.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork);

5
这段代码存在以下问题:
  • 它假定一台计算机只有一个IP地址。很多计算机有多个IP地址。
  • 它只考虑了IPV4地址。需要添加InterNetworkV6以包括IPV6。
- Robert Bratton
2
@RobertBratton,感谢您的回复。这个问题并没有涉及到多个IP地址或者IPv6,通过对这段代码进行轻微修改,它可以处理特定的不同问题。 - Mohammed A. Fadil

51

如果你无法依赖从DNS服务器获取IP地址(这种情况发生在我身上过),可以使用以下方法:

System.Net.NetworkInformation命名空间包含一个NetworkInterface类,其中有一个静态的GetAllNetworkInterfaces方法

此方法将返回计算机上的所有“网络接口”,通常会有很多,即使您的计算机只安装了一个无线适配器和/或以太网适配器硬件。 所有这些网络接口都具有本地计算机的有效IP地址,尽管您可能只需要一个。

如果您正在寻找一个IP地址,则需要过滤列表,直到您可以识别正确的地址。 您可能需要进行一些实验,但我使用以下方法取得了成功:

  • 通过检查OperationalStatus == OperationalStatus.Up来过滤任何处于非活动状态的NetworkInterfaces。 如果您没有插入网络电缆,这将排除您的物理以太网适配器。

对于每个网络接口,您可以使用GetIPProperties方法获取一个IPInterfaceProperties对象,并从IPInterfaceProperties对象中访问UnicastAddresses属性以获取UnicastIPAddressInformation对象的列表。

  • 通过检查DuplicateAddressDetectionState == DuplicateAddressDetectionState.Preferred来过滤非首选单播地址。
  • 通过检查AddressPreferredLifetime != UInt32.MaxValue来过滤“虚拟”地址。

此时,我会获取第一个(如果有)符合所有这些过滤器的单播地址。

编辑:

[2018年5月16日修订代码,包括上文提到的重复地址检测状态和优选生存时间的条件]

下面的示例演示了基于操作状态、地址族、排除回环地址(127.0.0.1)、重复地址检测状态和优选生存时间的过滤。

static IEnumerable<IPAddress> GetLocalIpAddresses()
{
    // Get the list of network interfaces for the local computer.
    var adapters = NetworkInterface.GetAllNetworkInterfaces();

    // Return the list of local IPv4 addresses excluding the local
    // host, disconnected, and virtual addresses.
    return (from adapter in adapters
            let properties = adapter.GetIPProperties()
            from address in properties.UnicastAddresses
            where adapter.OperationalStatus == OperationalStatus.Up &&
                  address.Address.AddressFamily == AddressFamily.InterNetwork &&
                  !address.Equals(IPAddress.Loopback) &&
                  address.DuplicateAddressDetectionState == DuplicateAddressDetectionState.Preferred &&
                  address.AddressPreferredLifetime != UInt32.MaxValue
            select address.Address);
}

2
在这种特定情况下,原帖作者想要查看他的外部IP地址,因此DNS解决方案可能是正确的选择。但是对于迭代本地IP地址,我建议采用以下方法。 - Matt Davis
3
同意DNS是获得IP地址的更容易的方式。我在我的回答中提到,当您的DNS不可靠时,这种方法才有效。我曾在一个DNS出了问题的环境中使用过它,以至于如果您将一台机器从一个以太网口移动到另一个口,DNS仍然会报告旧的IP地址,因此对于我的目的几乎没有用处。 - Dr. Wily's Apprentice
我很感激您提供的所有描述,但您应该也发布代码示例。 - Aidin
非常感谢。请注意,根据最近的Windows更新,UnicastAddresses的第一个假设不再成立。现在我需要检查每个适配器的所有UnicastAddress,并使用AddressPreferredLifetimeDuplicateAddressDetectionStation进行进一步过滤(如您上面提到的)。 - user3085342

37
WebClient webClient = new WebClient();
string IP = webClient.DownloadString("http://myip.ozymo.com/");

1
ifconfig.me/ip不再可用。请尝试https://api.ipify.org或Doug评论中的链接。 - Kenny83

16
using System.Net;

string host = Dns.GetHostName();
IPHostEntry ip = Dns.GetHostEntry(host);
Console.WriteLine(ip.AddressList[0].ToString());

我在我的电脑上测试了这个,它可以运行。


3
它将获取您的本地IP,问题是关于外部IP,即您浏览互联网时使用的IP。 - Sangram Nandkhile

15

如果您希望避免使用DNS:

List<IPAddress> ipList = new List<IPAddress>();
foreach (var netInterface in NetworkInterface.GetAllNetworkInterfaces())
{
    foreach (var address in netInterface.GetIPProperties().UnicastAddresses)
    {
        if (address.Address.AddressFamily == AddressFamily.InterNetwork)
        {
            Console.WriteLine("found IP " + address.Address.ToString());
            ipList.Add(address.Address);
        }
    }
}

9
不要总是依赖InterNetwork,因为您可能有多个设备也使用IP4,这会影响获取IP的结果。
首先,我会获取路由器(网关)的地址。如果返回的结果显示我连接到了一个网关(这意味着没有直接连接到无线调制解调器或者不是无线的),那么我们就有了网关地址作为IPAddress,否则我们将得到一个空指针IPAddress引用。
然后,我们需要获取计算机的IP地址列表。这里并不难,因为所有路由器(所有的路由器)都使用4个字节(…)。前三个最重要,因为连接到它的任何计算机的IP4地址都将匹配前三个字节。例如:192.168.0.1是路由器默认IP的标准值,除非管理员对其进行了更改。“192.168.0”或其他值是我们需要匹配的内容。这就是我在IsAddressOfGateway函数中所做的全部工作。
之所以需要匹配长度是因为并不是所有地址(只针对计算机)都具有4个字节的长度。通过在cmd中输入netstat命令,您会发现这一点。所以这样做确实需要更多的工作来找到你真正需要的东西。消去法的过程。
而且,千万不要通过ping来查找地址,因为这需要时间,首先您会发送要被ping的地址,然后它必须返回结果。不要这样做,而是直接使用与您的系统环境相关的.Net类来处理,您将在需要仅与计算机有关的答案时得到您要找的答案。
现在如果您直接连接到您的调制解调器,过程几乎相同,因为调制解调器是您的网关,但子掩码不同,因为您是直接从DNS服务器通过调制解调器获取信息,而不是由路由器掩盖,提供给您上网。尽管如此,您仍然可以使用相同的代码,因为分配给调制解调器的IP的最后一个字节是1。因此,如果从调制解调器发送的IP(会发生变化)为111.111.111.1,则将获得111.111.111.(某个字节值)。请记住,我们需要查找网关信息,因为与互联网连接有关的设备比您的路由器和调制解调器更多。
现在您知道为什么不要更改路由器的前两个字节192和168了。这些只是为路由器专门保留的,不用于互联网使用,否则IP协议将出现严重问题,并且双ping将导致计算机崩溃。想象一下,如果您被分配的路由器IP是192.168.44.103,而您单击了一个具有相同IP的站点。天啊!您的计算机将不知道要ping什么。就会在那里崩溃。为避免这个问题,只分配给路由器,而不用于互联网使用。所以请让路由器的前两个字节保持原样。
static IPAddress FindLanAddress()
{
    IPAddress gateway = FindGetGatewayAddress();
    if (gateway == null)
        return null;

    IPAddress[] pIPAddress = Dns.GetHostAddresses(Dns.GetHostName());

    foreach (IPAddress address in pIPAddress)            {
        if (IsAddressOfGateway(address, gateway))
                return address;
    return null;
}
static bool IsAddressOfGateway(IPAddress address, IPAddress gateway)
{
    if (address != null && gateway != null)
        return IsAddressOfGateway(address.GetAddressBytes(),gateway.GetAddressBytes());
    return false;
}
static bool IsAddressOfGateway(byte[] address, byte[] gateway)
{
    if (address != null && gateway != null)
    {
        int gwLen = gateway.Length;
        if (gwLen > 0)
        {
            if (address.Length == gateway.Length)
            {
                --gwLen;
                int counter = 0;
                for (int i = 0; i < gwLen; i++)
                {
                    if (address[i] == gateway[i])
                        ++counter;
                }
                return (counter == gwLen);
            }
        }
    }
    return false;

}
static IPAddress FindGetGatewayAddress()
{
    IPGlobalProperties ipGlobProps = IPGlobalProperties.GetIPGlobalProperties();

    foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces())
    {
        IPInterfaceProperties ipInfProps = ni.GetIPProperties();
        foreach (GatewayIPAddressInformation gi in ipInfProps.GatewayAddresses)
            return gi.Address;
    }
    return null;
}

1
这个没有意义:foreach (GatewayIPAddressInformation gi in ipInfProps.GatewayAddresses) return gi.Address; - Edwin Evans
3
“任何连接到网关的计算机都没有保证与前三个字节匹配的IP4地址”。这取决于子网掩码,它可以包含各种位组合。另外,起始字节不必是“192.168”,如此处所述。如果子网掩码是255.255.255.0,则此代码仅适用,并且在我看来它会以相当复杂的方式工作。 - vgru

8

虽然已经有很多其他有用的答案了,但我想加上自己的一句话。


string ipAddress = new WebClient().DownloadString("http://icanhazip.com");

这行代码的作用是从"http://icanhazip.com"这个网站下载一个字符串类型的IP地址。


4
请注意,这段代码可能存在内存泄漏问题。WebClient没有被正确地释放。可以改为使用以下代码:using (var client = new WebClient()) { return client.DownloadString("http://icanhazip.com/").Trim(); }。 - FOO

4

要获取当前的公共IP地址,您只需要创建一个带有以下代码行的ASPX页面,并在页面加载事件中调用:

Response.Write(HttpContext.Current.Request.UserHostAddress.ToString());

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