获取本地计算机的IP地址

时间:2008-09-23 16:41:42

标签: c++ sockets networking

在C ++中,获取本地计算机的IP地址和子网掩码的最简单方法是什么?

我希望能够在本地网络中检测本地计算机的IP地址。在我的特定情况下,我有一个子网掩码为255.255.255.0的网络,我的计算机的IP地址是192.168.0.5。我需要以编程方式获取这两个值,以便向我的网络发送广播消息(对于我的特定情况,格式为192.168.0.255)

编辑:许多答案没有给出我预期的结果,因为我有两个不同的网络IP。 Torial的代码完成了这个伎俩(它给了我两个IP地址)。谢谢。

编辑2:感谢Brian R. Bondy获取有关子网掩码的信息。

13 个答案:

答案 0 :(得分:31)

问题比它看起来更棘手,因为在许多情况下,没有“本地计算机的IP地址”,就像许多不同的IP地址一样。例如,我现在正在键入的Mac(这是一个非常基本的标准Mac设置)具有以下与之关联的IP地址:

fe80::1%lo0  
127.0.0.1 
::1 
fe80::21f:5bff:fe3f:1b36%en1 
10.0.0.138 
172.16.175.1
192.168.27.1

......这不仅仅是要弄清楚以上哪个是“真正的IP地址”,要么......它们都是“真实的”且有用的;一些比其他更有用,取决于你将使用的地址。

根据我的经验,获取本地计算机“IP地址”的最佳方法是不要查询本地计算机,而是要求计算机程序正在与计算机的IP地址进行通信。 。例如如果您正在编写客户端程序,请向服务器发送一条消息,要求服务器将请求来自的IP地址作为数据发回。这样,根据您正在与之通信的计算机的上下文,您将知道相关的 IP地址是什么。

也就是说,这个技巧可能不适用于某些目的(例如,当您不与特定计算机通信时),因此有时您只需要收集与您的计算机关联的所有IP地址列表。在Unix / Mac(AFAIK)下执行此操作的最佳方法是调用getifaddrs()并迭代结果。在Windows下,尝试GetAdaptersAddresses()以获得类似的功能。例如,两者的用法,请参阅this file中的GetNetworkInterfaceInfos()函数。

答案 1 :(得分:23)

基于gethostbyname的所有方法的问题在于,您不会获得分配给特定计算机的所有IP地址。服务器通常有多个适配器。

以下是如何迭代主机上所有Ipv4和Ipv6地址的示例:

void ListIpAddresses(IpAddresses& ipAddrs)
{
  IP_ADAPTER_ADDRESSES* adapter_addresses(NULL);
  IP_ADAPTER_ADDRESSES* adapter(NULL);

  // Start with a 16 KB buffer and resize if needed -
  // multiple attempts in case interfaces change while
  // we are in the middle of querying them.
  DWORD adapter_addresses_buffer_size = 16 * KB;
  for (int attempts = 0; attempts != 3; ++attempts)
  {
    adapter_addresses = (IP_ADAPTER_ADDRESSES*)malloc(adapter_addresses_buffer_size);
    assert(adapter_addresses);

    DWORD error = ::GetAdaptersAddresses(
      AF_UNSPEC, 
      GAA_FLAG_SKIP_ANYCAST | 
        GAA_FLAG_SKIP_MULTICAST | 
        GAA_FLAG_SKIP_DNS_SERVER |
        GAA_FLAG_SKIP_FRIENDLY_NAME, 
      NULL, 
      adapter_addresses,
      &adapter_addresses_buffer_size);

    if (ERROR_SUCCESS == error)
    {
      // We're done here, people!
      break;
    }
    else if (ERROR_BUFFER_OVERFLOW == error)
    {
      // Try again with the new size
      free(adapter_addresses);
      adapter_addresses = NULL;

      continue;
    }
    else
    {
      // Unexpected error code - log and throw
      free(adapter_addresses);
      adapter_addresses = NULL;

      // @todo
      LOG_AND_THROW_HERE();
    }
  }

  // Iterate through all of the adapters
  for (adapter = adapter_addresses; NULL != adapter; adapter = adapter->Next)
  {
    // Skip loopback adapters
    if (IF_TYPE_SOFTWARE_LOOPBACK == adapter->IfType)
    {
      continue;
    }

    // Parse all IPv4 and IPv6 addresses
    for (
      IP_ADAPTER_UNICAST_ADDRESS* address = adapter->FirstUnicastAddress; 
      NULL != address;
      address = address->Next)
    {
      auto family = address->Address.lpSockaddr->sa_family;
      if (AF_INET == family)
      {
        // IPv4
        SOCKADDR_IN* ipv4 = reinterpret_cast<SOCKADDR_IN*>(address->Address.lpSockaddr);

        char str_buffer[INET_ADDRSTRLEN] = {0};
        inet_ntop(AF_INET, &(ipv4->sin_addr), str_buffer, INET_ADDRSTRLEN);
        ipAddrs.mIpv4.push_back(str_buffer);
      }
      else if (AF_INET6 == family)
      {
        // IPv6
        SOCKADDR_IN6* ipv6 = reinterpret_cast<SOCKADDR_IN6*>(address->Address.lpSockaddr);

        char str_buffer[INET6_ADDRSTRLEN] = {0};
        inet_ntop(AF_INET6, &(ipv6->sin6_addr), str_buffer, INET6_ADDRSTRLEN);

        std::string ipv6_str(str_buffer);

        // Detect and skip non-external addresses
        bool is_link_local(false);
        bool is_special_use(false);

        if (0 == ipv6_str.find("fe"))
        {
          char c = ipv6_str[2];
          if (c == '8' || c == '9' || c == 'a' || c == 'b')
          {
            is_link_local = true;
          }
        }
        else if (0 == ipv6_str.find("2001:0:"))
        {
          is_special_use = true;
        }

        if (! (is_link_local || is_special_use))
        {
          ipAddrs.mIpv6.push_back(ipv6_str);
        }
      }
      else
      {
        // Skip all other types of addresses
        continue;
      }
    }
  }

  // Cleanup
  free(adapter_addresses);
  adapter_addresses = NULL;

  // Cheers!
}

答案 2 :(得分:16)

您可以使用gethostname后跟gethostbyname来获取本地接口内部IP。

此返回的IP可能与您的外部IP不同。要获得外部IP,您必须与外部服务器通信,该外部服务器将告诉您外部IP是什么。因为外部IP不是你的,但它是你的路由器。

//Example: b1 == 192, b2 == 168, b3 == 0, b4 == 100
struct IPv4
{
    unsigned char b1, b2, b3, b4;
};

bool getMyIP(IPv4 & myIP)
{
    char szBuffer[1024];

    #ifdef WIN32
    WSADATA wsaData;
    WORD wVersionRequested = MAKEWORD(2, 0);
    if(::WSAStartup(wVersionRequested, &wsaData) != 0)
        return false;
    #endif


    if(gethostname(szBuffer, sizeof(szBuffer)) == SOCKET_ERROR)
    {
      #ifdef WIN32
      WSACleanup();
      #endif
      return false;
    }

    struct hostent *host = gethostbyname(szBuffer);
    if(host == NULL)
    {
      #ifdef WIN32
      WSACleanup();
      #endif
      return false;
    }

    //Obtain the computer's IP
    myIP.b1 = ((struct in_addr *)(host->h_addr))->S_un.S_un_b.s_b1;
    myIP.b2 = ((struct in_addr *)(host->h_addr))->S_un.S_un_b.s_b2;
    myIP.b3 = ((struct in_addr *)(host->h_addr))->S_un.S_un_b.s_b3;
    myIP.b4 = ((struct in_addr *)(host->h_addr))->S_un.S_un_b.s_b4;

    #ifdef WIN32
    WSACleanup();
    #endif
    return true;
}

您也可以始终使用始终代表本地计算机的127.0.0.1。

Windows中的子网掩码:

您可以通过查询此注册表项的子项来获取子网掩码(以及网关和其他信息):

HKEY_LOCAL_MACHINE \ SYSTEM \ CURRENTCONTROLSET \服务\ TCPIP \参数\接口

查找注册表值SubnetMask。

在Windows中获取界面信息的其他方法:

您还可以使用以下方法检索您要查找的信息: WSAIoctl使用此选项:SIO_GET_INTERFACE_LIST

答案 3 :(得分:6)

您无法在标准C ++中执行此操作。

我发布这个是因为这是唯一正确的答案。您的问题询问如何在C ++中执行此操作。好吧,你不能用C ++做。您可以在Windows,POSIX,Linux,Android中执行此操作,但所有这些都是特定于操作系统的解决方案,而不是语言标准的一部分。

标准C ++ 根本没有网络层

我假设您有一个错误的假设,即C ++ Standard定义了与其他语言标准Java相同的功能范围。虽然Java可能在语言自己的标准库中具有内置网络(甚至是GUI框架),但C ++却没有。

虽然C ++程序可以使用第三方API和库,但这并不能说你可以在C ++中使用它。

这是一个澄清我的意思的例子。您可以使用C ++打开文件,因为它有一个fstream类作为其标准库的一部分。这与使用CreateFile()不同,{{1}}是Windows特有的功能,仅适用于WINAPI。

答案 4 :(得分:4)

另外,请注意“本地IP”可能不是特别独特的事情。如果您使用多个物理网络(例如,有线+无线+蓝牙,或具有大量以太网卡的服务器等),或者设置了TAP / TUN接口,则您的计算机可以轻松拥有多个接口。

答案 5 :(得分:3)

How to Obtain the IP Address of the Local Machine on the Network似乎很好地描述了解决方案......

答案 6 :(得分:2)

Winsock具体:

// Init WinSock
WSADATA wsa_Data;
int wsa_ReturnCode = WSAStartup(0x101,&wsa_Data);

// Get the local hostname
char szHostName[255];
gethostname(szHostName, 255);
struct hostent *host_entry;
host_entry=gethostbyname(szHostName);
char * szLocalIP;
szLocalIP = inet_ntoa (*(struct in_addr *)*host_entry->h_addr_list);
WSACleanup();

答案 7 :(得分:2)

  
    来自torial:     如果你使用winsock,这是一种方式:http://tangentsoft.net/wskfaq/examples/ipaddr.html

  

问题的子网部分;没有平台无关的方法来检索子网掩码,因为POSIX套接字API(所有现代操作系统都实现)没有指定这一点。因此,您必须使用您正在使用的平台上可用的任何方法。

答案 8 :(得分:1)

我能够使用VS2013下的DNS服务使用以下代码:

#include <Windns.h>

WSADATA wsa_Data;

int wsa_ReturnCode = WSAStartup(0x101, &wsa_Data);

gethostname(hostName, 256);
PDNS_RECORD pDnsRecord;

DNS_STATUS statsus = DnsQuery(hostName, DNS_TYPE_A, DNS_QUERY_STANDARD, NULL, &pDnsRecord, NULL);
IN_ADDR ipaddr;
ipaddr.S_un.S_addr = (pDnsRecord->Data.A.IpAddress);
printf("The IP address of the host %s is %s \n", hostName, inet_ntoa(ipaddr));

DnsRecordListFree(&pDnsRecord, DnsFreeRecordList);

我不得不在链接器选项中添加Dnsapi.lib作为成瘾依赖。

参考here

答案 9 :(得分:0)

你能不能发送给INADDR_BROADCAST?不可否认,这将发送所有接口 - 但这很少是一个问题。

否则,ioctl和SIOCGIFBRDADDR应该在* nix上获取地址,在win32上获取WSAioctl and SIO_GET_BROADCAST_ADDRESS

答案 10 :(得分:0)

在DEV C ++中,我使用纯C和WIN32,使用了这段代码:

case IDC_IP:

             gethostname(szHostName, 255);
             host_entry=gethostbyname(szHostName);
             szLocalIP = inet_ntoa (*(struct in_addr *)*host_entry->h_addr_list);
             //WSACleanup(); 
             writeInTextBox("\n");
             writeInTextBox("IP: "); 
             writeInTextBox(szLocalIP);
             break;

当我点击'show ip'按钮时,它可以正常工作。但是第二次,程序退出(没有警告或错误)。当我这样做时:

//WSACleanup(); 

程序不会退出,即使以最快的速度多次单击同一按钮也是如此。 所以WSACleanup()可能不适用于Dev-C ++ ..

答案 11 :(得分:0)

我建议我的代码。

DllExport void get_local_ips(boost::container::vector<wstring>& ips)
{
   IP_ADAPTER_ADDRESSES*       adapters  = NULL;
   IP_ADAPTER_ADDRESSES*       adapter       = NULL;
   IP_ADAPTER_UNICAST_ADDRESS* adr           = NULL;
   ULONG                       adapter_size = 0;
   ULONG                       err           = 0;
   SOCKADDR_IN*                sockaddr  = NULL;

   err = ::GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME, NULL, NULL, &adapter_size);
   adapters = (IP_ADAPTER_ADDRESSES*)malloc(adapter_size);
   err = ::GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME, NULL, adapters, &adapter_size);

   for (adapter = adapters; NULL != adapter; adapter = adapter->Next)
   {
       if (adapter->IfType     == IF_TYPE_SOFTWARE_LOOPBACK) continue; // Skip Loopback
       if (adapter->OperStatus != IfOperStatusUp) continue;            // Live connection only  

       for (adr = adapter->FirstUnicastAddress;adr != NULL; adr = adr->Next)
       {
           sockaddr = (SOCKADDR_IN*)(adr->Address.lpSockaddr);
           char    ipstr [INET6_ADDRSTRLEN] = { 0 };
           wchar_t ipwstr[INET6_ADDRSTRLEN] = { 0 };
           inet_ntop(AF_INET, &(sockaddr->sin_addr), ipstr, INET_ADDRSTRLEN);
           mbstowcs(ipwstr, ipstr, INET6_ADDRSTRLEN);
           wstring wstr(ipwstr);
           if (wstr != "0.0.0.0") ips.push_back(wstr);                      
       }
   }

   free(adapters);
   adapters = NULL; }

答案 12 :(得分:0)

this answer 的修改版本。
添加了标头和库。

它也基于这些页面:
GetAdaptersAddresses
IP_ADAPTER_ADDRESSES_LH
IP_ADAPTER_UNICAST_ADDRESS_LH

简而言之,要获取 IPv4 地址,请调用 GetAdaptersAddresses() 以获取适配器,然后遍历以 IP_ADAPTER_UNICAST_ADDRESS 开头的 FirstUnicastAddress 结构并获取 Address 字段然后使用 inet_ntop() 将其转换为可读格式。

以下列格式打印信息:

[ADAPTER]: Realtek PCIe
[NAME]:    Ethernet 3
[IP]:      123.123.123.123

可以编译:

cl test.cpp

或者,如果您需要在命令行中添加 libs 依赖项:

cl test.cpp Iphlpapi.lib ws2_32.lib
#include <winsock2.h>
#include <iphlpapi.h>
#include <stdio.h>
#include <ws2tcpip.h>

// Link with Iphlpapi.lib and ws2_32.lib
#pragma comment(lib, "Iphlpapi.lib")
#pragma comment(lib, "ws2_32.lib")

void ListIpAddresses() {
  IP_ADAPTER_ADDRESSES* adapter_addresses(NULL);
  IP_ADAPTER_ADDRESSES* adapter(NULL);
  
  DWORD adapter_addresses_buffer_size = 16 * 1024;
  
  // Get adapter addresses
  for (int attempts = 0; attempts != 3; ++attempts) {
    adapter_addresses = (IP_ADAPTER_ADDRESSES*) malloc(adapter_addresses_buffer_size);

    DWORD error = ::GetAdaptersAddresses(AF_UNSPEC, 
      GAA_FLAG_SKIP_ANYCAST | 
        GAA_FLAG_SKIP_MULTICAST | 
        GAA_FLAG_SKIP_DNS_SERVER | 
        GAA_FLAG_SKIP_FRIENDLY_NAME,
      NULL, 
      adapter_addresses,
      &adapter_addresses_buffer_size);
    
    if (ERROR_SUCCESS == error) {
      break;
    }
    else if (ERROR_BUFFER_OVERFLOW == error) {
      // Try again with the new size
      free(adapter_addresses);
      adapter_addresses = NULL;
      continue;
    }
    else {
      // Unexpected error code - log and throw
      free(adapter_addresses);
      adapter_addresses = NULL;
      return;
    }
  }

  // Iterate through all of the adapters
  for (adapter = adapter_addresses; NULL != adapter; adapter = adapter->Next) {
    // Skip loopback adapters
    if (IF_TYPE_SOFTWARE_LOOPBACK == adapter->IfType) continue;
    
    printf("[ADAPTER]: %S\n", adapter->Description);
    printf("[NAME]:    %S\n", adapter->FriendlyName);

    // Parse all IPv4 addresses
    for (IP_ADAPTER_UNICAST_ADDRESS* address = adapter->FirstUnicastAddress; NULL != address; address = address->Next) {
      auto family = address->Address.lpSockaddr->sa_family;
      if (AF_INET == family) {
        SOCKADDR_IN* ipv4 = reinterpret_cast<SOCKADDR_IN*>(address->Address.lpSockaddr);
        char str_buffer[16] = {0};
        inet_ntop(AF_INET, &(ipv4->sin_addr), str_buffer, 16);

        printf("[IP]:      %s\n", str_buffer);
      }
    }
    printf("\n");
  }

  free(adapter_addresses);
  adapter_addresses = NULL;
}

int main() {
  ListIpAddresses();
  return 0;
}