58

C++ で、ローカル コンピューターの IP アドレスとサブネット マスクを取得する最も簡単な方法は何ですか?

ローカル ネットワークでローカル マシンの IP アドレスを検出できるようにしたいと考えています。私の特定のケースでは、サブネット マスクが 255.255.255.0 のネットワークがあり、コンピューターの IP アドレスは 192.168.0.5 です。ブロードキャスト メッセージをネットワークに送信するには、プログラムでこれらの値を 2 つ取得する必要があります (特定のケースでは 192.168.0.255 の形式で)。

編集: 2 つの異なるネットワーク IP を使用していたため、多くの回答で期待した結果が得られませんでした。Torialのコードはうまくいきました (両方の IP アドレスが得られました)。

編集 2:サブネット マスクに関する情報を提供してくれたBrian R. Bondyに感謝します。

4

13 に答える 13

36

多くの場合、"ローカル コンピュータの 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() 関数を参照してください。

于 2009-08-22T23:17:02.030 に答える
22

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!
}
于 2012-05-31T18:04:25.173 に答える
20

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\Services\Tcpip\Parameters\Interfaces

レジストリ値 SubnetMask を探します。

Windows でインターフェイス情報を取得するその他の方法:

次のオプション使用して、探している情報を取得することもできます 。

于 2008-09-23T16:44:57.837 に答える
10

標準 C++ ではそれを行うことはできません。

唯一の正解だったので投稿します。あなたの質問は、C++ でそれを行う方法を尋ねます。そうですね、C++ ではできません。Windows、POSIX、Linux、Android で実行できますが、これらはすべてOS 固有のソリューションであり、言語標準の一部ではありません。

標準 C++にはネットワーク層がまったくありません

C++ 標準が他の言語標準である Java と同じ範囲の機能を定義しているという誤った仮定をお持ちだと思います。Java には、言語独自の標準ライブラリにネットワーク (さらには GUI フレームワーク) が組み込まれている可能性がありますが、C++ にはありません。

C++ プログラムで使用できるサードパーティの API とライブラリがありますが、これは C++ で使用できるということと同じではありません。

これが私の言いたいことを明確にするための例です。fstream標準ライブラリの一部としてクラスがあるため、C++ でファイルを開くことができます。CreateFile()これは、Windows 固有の関数であり、WINAPI でのみ使用できる を使用することと同じではありません。

于 2015-03-04T08:16:29.890 に答える
3

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();
于 2008-09-23T16:47:55.100 に答える
3

また、「ローカル IP」は特に固有のものではない可能性があることに注意してください。複数の物理ネットワーク (有線 + 無線 + Bluetooth など、または多数のイーサネット カードを備えたサーバーなど) に接続している場合、または TAP/TUN インターフェイスをセットアップしている場合、マシンにインターフェイスのホスト全体を簡単に配置できます。

于 2008-10-21T11:40:18.737 に答える
1

公式より: winsock を使用する場合、次の方法があります: http://tangentsoft.net/wskfaq/examples/ipaddr.html

質問のサブネット部分については; POSIX ソケット API (最近のすべてのオペレーティング システムが実装している) がこれを指定していないため、サブネット マスクを取得するプラットフォームに依存しない方法はありません。したがって、使用しているプラ​​ットフォームで使用可能な方法を使用する必要があります。

于 2008-09-23T16:49:59.120 に答える
1

ネットワーク上のローカルマシンのIPアドレスを取得する方法は、ソリューションを非常によく説明しているようです...

于 2008-09-23T16:45:24.077 に答える
0

次のコードを使用して、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 を追加する必要がありました。

ここを参照してください。

于 2015-10-09T16:11:47.383 に答える
-1

INADDR_BROADCASTに送信できませんか? 確かに、これはすべてのインターフェースで送信されますが、それが問題になることはめったにありません。

それ以外の場合、ioctl と SIOCGIFBRDADDR は *nix ではアドレスを取得し、win32では WSAioctl と SIO_GET_BROADCAST_ADDRESSを取得する必要があります。

于 2008-09-23T17:44:51.517 に答える
-1

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;

「show ip」ボタンをクリックすると、機能します。しかし、2 回目には、プログラムは終了します (警告もエラーもなしに)。私がする時:

//WSACleanup(); 

同じボタンを最速で複数回クリックしても、プログラムは終了しません。そのため、WSACleanup() は Dev-C++ ではうまく機能しない可能性があります。

于 2009-08-22T22:51:07.107 に答える