代码之家  ›  专栏  ›  技术社区  ›  djeidot

获取本地计算机的IP地址

  •  46
  • djeidot  · 技术社区  · 16 年前

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

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

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

    编辑二:多亏了 Brian R. Bondy 有关子网掩码的信息。

    12 回复  |  直到 7 年前
        1
  •  31
  •   Jeremy Friesner    15 年前

    这个问题比看起来更棘手,因为在许多情况下,没有“本地计算机的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()以获得类似的功能。例如,两者的用法,请参见中的getNetworkInterfaceInfos()函数 this file .

        2
  •  23
  •   kgriffs    12 年前

    基于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!
    }
    
        3
  •  16
  •   Brian R. Bondy    16 年前

    您可以使用GethOSTNEX然后使用GeTHOSTBYNAMY来获得本地接口内部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\services\tcpip\parameters\interfaces公司

    查找注册表值subnetmask。

    在Windows中获取接口信息的其他方法:

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

        4
  •  7
  •   sashoalm Yaser Kalali    10 年前

    在标准C++中不能这样做。

    我发这个是因为这是唯一正确的答案。你的问题问如何在C++中完成。嗯,你不能用C++来做。你可以在Windows,POSIX,Linux,Android,但所有这些都是 操作系统特定解决方案 不是语言标准的一部分。

    标准C++ 没有网络层 完全。

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

    虽然有第三方API和库可以由C++程序使用,但这与在C++中可以做到这一点完全不同。

    这里有一个例子来说明我的意思。你可以在C++中打开一个文件,因为它有一个 fstream 类作为其标准库的一部分。这和使用 CreateFile() ,它是windows特有的函数,仅适用于winapi。

        5
  •  4
  •   jakobengblom2    16 年前

    另外,请注意,“本地IP”可能不是一个特别独特的东西。如果你在几个物理网络上(例如,有线+无线+蓝牙,或者有大量以太网卡等的服务器),或者有TAP / DSP接口,你的机器可以很容易地拥有一大堆的接口。

        6
  •  3
  •   PhiLho    16 年前
        7
  •  2
  •   GEOCHET S.Lott    16 年前

    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();
    
        8
  •  2
  •   nymacro    16 年前

    来自美国: 如果使用winsock,有一种方法: http://tangentsoft.net/wskfaq/examples/ipaddr.html

    至于问题的子网部分,没有平台无关的方法来检索子网掩码,因为posix socket api(所有现代操作系统都实现了这个api)没有指定这一点。因此,您必须使用平台上可用的任何方法。

        9
  •  1
  •   Zac    9 年前

    我可以使用下面的代码在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 .

        10
  •  0
  •   Mark Brackett    16 年前

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

    否则,ioctl和siocgiffrdaddr会在*nix上给你地址,并且 WSAioctl and SIO_GET_BROADCAST_ADDRESS 在Win32上。

        11
  •  0
  •   Ivan    15 年前

    在DEV C++中,我用Win32使用纯C,用这个给定的代码:

    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;
    

    当我点击“显示IP”按钮时,它就工作了。但第二次,程序退出(没有警告或错误)。当我这样做的时候:

    //WSACleanup(); 
    

    即使以最快的速度多次单击同一个按钮,程序也不会退出。 因此wsacleanup()在dev-c++中可能不能很好地工作。

        12
  •  0
  •   Mark Yang    7 年前

    我建议使用我的代码。

    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; }