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 に感謝します。
多くの場合、「ローカルコンピュータの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()関数を参照してください。
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!
}
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でインターフェイス情報を取得する他の方法:
次のオプションを使用して、探している情報を取得することもできます。 WSAIoctl このオプション:SIO_GET_INTERFACE_LIST
それが唯一の正解であるため、私はこれを投稿しています。あなたの質問は、C++でそれを行う方法を尋ねます。まあ、C++ではできません。 Windows、POSIX、Linux、Androidで実行できますが、これらはすべてOS固有のソリューションであり、言語標準の一部ではありません。
標準C++ ネットワーク層はありません。
私は、C++標準が他の言語標準であるJavaと同じ機能の範囲を定義しているという誤った仮定を持っていると思います。 Javaは言語の標準ライブラリに組み込みのネットワーク(およびGUIフレームワークさえ)を持っているかもしれませんが、C++は持っていません。
C++プログラムで使用できるサードパーティのAPIとライブラリがありますが、これはC++で実行できるということと決して同じではありません。
ここに私が意味することを明確にする例があります。標準ライブラリの一部としてfstream
クラスがあるため、C++でファイルを開くことができます。これはCreateFile()
を使用することと同じではありません。これはWindows固有の関数であり、WINAPIでのみ使用可能です。
また、「ローカルIP」は特にユニークなものではないかもしれないことに注意してください。複数の物理ネットワーク(たとえば、有線+ワイヤレス+ Bluetooth、または多数のイーサネットカードを備えたサーバーなど)を使用している場合、またはTAP/TUNインターフェイスをセットアップしている場合、マシンにはインターフェイスのホスト全体を簡単に設定できます。
ネットワーク上のローカルマシンのIPアドレスを取得する方法 ソリューションを非常によく説明しているようです...
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();
チュートリアルから:winsockを使用する場合の方法は次のとおりです。 http://tangentsoft.net/wskfaq/examples/ipaddr.html
質問のサブネット部分に関しては; POSIXソケットAPI(最新のすべてのオペレーティングシステムが実装している)はこれを指定していないため、サブネットマスクを取得するプラットフォームに依存しない方法はありません。そのため、使用しているプラットフォームで利用可能な方法を使用する必要があります。
コードを提案します。
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; }
次のコードで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を追加する必要がありました。
参照 ここ 。
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++ではうまく機能しない可能性があります。
INADDR_BROADCAST に送信することはできませんか?確かに、それはすべてのインターフェイスで送信されます-しかし、それはめったに問題ではありません。
それ以外の場合、ioctlおよびSIOCGIFBRDADDRは* nixでアドレスを取得し、win32では WSAioctlおよびSIO_GET_BROADCAST_ADDRESS を取得します。