[C言語] PCのIPアドレスを取得する方法を3つ解説【IPv4/IPv6】
C言語でPCのIPアドレスを取得する方法は複数あります。
一つ目は、getaddrinfo関数を使用する方法です。この関数は、ホスト名やサービス名を指定してアドレス情報を取得します。
二つ目は、getifaddrs関数を利用する方法です。この関数は、ネットワークインターフェースのアドレスを取得するために使用されます。
三つ目は、ioctlシステムコールを用いる方法です。SIOCGIFADDRを指定して、特定のインターフェースのIPアドレスを取得します。
これらの方法を使うことで、IPv4とIPv6の両方のアドレスを取得することが可能です。
IPアドレスとは
IPアドレスは、インターネットやローカルネットワーク上でデバイスを識別するための一意のアドレスです。
IPアドレスは、データが正しい宛先に届くようにするための重要な役割を果たします。
IPアドレスには、IPv4とIPv6の2つのバージョンがあります。
IPv4は32ビットのアドレスで、通常は 192.168.1.1 のようにドットで区切られた4つの10進数で表されます。
一方、IPv6は128ビットのアドレスで、より多くのデバイスをサポートするために設計されており、 2001:0db8:85a3:0000:0000:8a2e:0370:7334 のようにコロンで区切られた16進数で表されます。
| 種類 | 特徴 | 
|---|---|
| IPv4 | 32ビットアドレス、約43億のアドレスを提供、ドットで区切られた10進数表記 | 
| IPv6 | 128ビットアドレス、ほぼ無限のアドレスを提供、コロンで区切られた16進数表記 | 
IPアドレスはさらに、プライベートIPとパブリックIPに分類されます。
プライベートIPは、家庭や企業内のローカルネットワークで使用され、インターネット上では直接使用されません。
これに対して、パブリックIPはインターネット上でデバイスを識別するために使用されます。
プライベートIPは、ルーターやNAT(ネットワークアドレス変換)を通じてパブリックIPに変換され、インターネットに接続されます。
方法1: gethostnameとgethostbynameを使用する
gethostname関数の使い方
gethostname関数は、現在のホスト名を取得するために使用されます。
この関数は、ホスト名を格納するためのバッファとそのサイズを引数として受け取ります。
成功すると0を返し、失敗すると-1を返します。
#include <unistd.h>
#include <stdio.h>
int main() {
    char hostname[256];
    if (gethostname(hostname, sizeof(hostname)) == 0) {
        printf("ホスト名: %s\n", hostname);
    } else {
        perror("gethostname");
    }
    return 0;
}gethostbyname関数の使い方
gethostbyname関数は、ホスト名からIPアドレスを取得するために使用されます。
この関数は、ホスト名を引数として受け取り、hostent構造体へのポインタを返します。
この構造体には、IPアドレス情報が含まれています。
#include <netdb.h>
#include <stdio.h>
#include <arpa/inet.h>
int main() {
    struct hostent *host;
    char *hostname = "example.com";
    host = gethostbyname(hostname);
    if (host != NULL) {
        printf("IPアドレス: %s\n", inet_ntoa(*(struct in_addr *)host->h_addr));
    } else {
        perror("gethostbyname");
    }
    return 0;
}サンプルコードの解説
以下のサンプルコードは、gethostnameとgethostbynameを組み合わせて、現在のホストのIPアドレスを取得する方法を示しています。
#include <unistd.h>
#include <stdio.h>
#include <netdb.h>
#include <arpa/inet.h>
int main() {
    char hostname[256];
    struct hostent *host;
    // ホスト名を取得
    if (gethostname(hostname, sizeof(hostname)) == 0) {
        printf("ホスト名: %s\n", hostname);
        // ホスト名からIPアドレスを取得
        host = gethostbyname(hostname);
        if (host != NULL) {
            printf("IPアドレス: %s\n", inet_ntoa(*(struct in_addr *)host->h_addr));
        } else {
            perror("gethostbyname");
        }
    } else {
        perror("gethostname");
    }
    return 0;
}ホスト名: my-computer
IPアドレス: 192.168.1.5このコードは、まずgethostnameを使用してホスト名を取得し、その後gethostbynameを使用してホスト名からIPアドレスを取得します。
メリットとデメリット
| メリット | デメリット | 
|---|---|
| 簡単にホスト名とIPアドレスを取得できる | IPv6には対応していない | 
| 標準ライブラリのみで実装可能 | gethostbynameは非推奨であり、getaddrinfoを推奨 | 
| クロスプラットフォームで動作する可能性が高い | ネットワークの設定によっては正確なIPが取得できない | 
方法2: ioctlを使用してIPアドレスを取得する
ioctl関数の概要
ioctl関数は、デバイスの制御操作を行うためのシステムコールです。
ネットワークインターフェースの情報を取得するためにも使用されます。
ioctlを使用してIPアドレスを取得するには、ネットワークインターフェースのソケットを作成し、SIOCGIFADDRコマンドを使用してインターフェースのアドレスを取得します。
必要なヘッダーファイルとデータ構造
ioctlを使用するためには、以下のヘッダーファイルが必要です。
また、ifreq構造体を使用してネットワークインターフェースの情報を格納します。
#include <sys/ioctl.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>サンプルコードの解説
以下のサンプルコードは、ioctlを使用して指定したネットワークインターフェースのIPアドレスを取得する方法を示しています。
#include <sys/ioctl.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main() {
    int fd;
    struct ifreq ifr;
    char *iface = "eth0";  // 取得したいインターフェース名
    char ip[INET_ADDRSTRLEN];
    // ソケットの作成
    fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd == -1) {
        perror("socket");
        return 1;
    }
    // インターフェース名の設定
    strncpy(ifr.ifr_name, iface, IFNAMSIZ-1);
    // IPアドレスの取得
    if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) {
        struct sockaddr_in *ipaddr = (struct sockaddr_in *)&ifr.ifr_addr;
        inet_ntop(AF_INET, &ipaddr->sin_addr, ip, sizeof(ip));
        printf("インターフェース %s のIPアドレス: %s\n", iface, ip);
    } else {
        perror("ioctl");
    }
    // ソケットのクローズ
    close(fd);
    return 0;
}インターフェース eth0 のIPアドレス: 192.168.1.10このコードは、指定したネットワークインターフェース(例:eth0)のIPアドレスを取得します。
ioctlを使用して、SIOCGIFADDRコマンドでインターフェースのアドレスを取得し、inet_ntopで人間が読める形式に変換しています。
メリットとデメリット
| メリット | デメリット | 
|---|---|
| 特定のインターフェースのIPアドレスを取得可能 | プラットフォーム依存がある(Linux特有の方法) | 
| IPv4アドレスを直接取得できる | IPv6には対応していない | 
| ネットワークプログラミングの理解が深まる | コードがやや複雑で、エラーハンドリングが必要 | 
方法3: getifaddrsを使用してIPアドレスを取得する
getifaddrs関数の概要
getifaddrs関数は、システム上のすべてのネットワークインターフェースのアドレス情報を取得するために使用されます。
この関数は、インターフェースのリストを取得し、それぞれのインターフェースに関連付けられたアドレス情報を提供します。
getifaddrsは、IPv4およびIPv6の両方のアドレスを取得することができ、クロスプラットフォームでの利用が可能です。
必要なヘッダーファイルとデータ構造
getifaddrsを使用するためには、以下のヘッダーファイルが必要です。
また、ifaddrs構造体を使用してインターフェースの情報を格納します。
#include <ifaddrs.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>サンプルコードの解説
以下のサンプルコードは、getifaddrsを使用してシステム上のすべてのネットワークインターフェースのIPアドレスを取得する方法を示しています。
#include <ifaddrs.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
int main() {
    struct ifaddrs *ifaddr, *ifa;
    char ip[INET6_ADDRSTRLEN];
    // インターフェースアドレスの取得
    if (getifaddrs(&ifaddr) == -1) {
        perror("getifaddrs");
        return 1;
    }
    // インターフェースを順に処理
    for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
        if (ifa->ifa_addr == NULL) continue;
        int family = ifa->ifa_addr->sa_family;
        if (family == AF_INET || family == AF_INET6) {
            void *addr;
            if (family == AF_INET) {
                addr = &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
            } else {
                addr = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
            }
            inet_ntop(family, addr, ip, sizeof(ip));
            printf("インターフェース %s のIPアドレス: %s\n", ifa->ifa_name, ip);
        }
    }
    // メモリの解放
    freeifaddrs(ifaddr);
    return 0;
}インターフェース lo のIPアドレス: 127.0.0.1
インターフェース eth0 のIPアドレス: 192.168.1.10
インターフェース eth0 のIPアドレス: fe80::1a2b:3c4d:5e6f:7g8hこのコードは、getifaddrsを使用してすべてのネットワークインターフェースのアドレスを取得し、IPv4およびIPv6のアドレスを表示します。
inet_ntopを使用して、アドレスを人間が読める形式に変換しています。
メリットとデメリット
| メリット | デメリット | 
|---|---|
| IPv4とIPv6の両方に対応 | 実装がやや複雑で、メモリ管理が必要 | 
| すべてのインターフェースの情報を取得可能 | ネットワークインターフェースが多いと処理が重くなる | 
| クロスプラットフォームで利用可能 | 一部の古いシステムではサポートされていない可能性 | 
応用例
複数のネットワークインターフェースからのIPアドレス取得
複数のネットワークインターフェースが存在する環境では、各インターフェースのIPアドレスを取得することが重要です。
getifaddrsを使用することで、すべてのインターフェースのアドレスを一度に取得できます。
これにより、特定のインターフェースに依存しない柔軟なネットワークアプリケーションを開発することが可能です。
例えば、サーバーアプリケーションが複数のネットワークに接続されている場合、各ネットワークのIPアドレスを取得して、適切なネットワークインターフェースを選択することができます。
IPアドレスの変更検知
ネットワーク環境が動的に変化する場合、IPアドレスの変更を検知することが重要です。
getifaddrsを定期的に呼び出して、前回取得したIPアドレスと比較することで、IPアドレスの変更を検知することができます。
これにより、IPアドレスが変更された際に、アプリケーションが自動的に再設定を行うことが可能になります。
例えば、モバイルデバイスが異なるWi-Fiネットワークに接続された場合に、アプリケーションが新しいネットワーク設定を適用することができます。
IPアドレスを用いたネットワーク診断ツールの作成
IPアドレスを取得する技術を応用して、ネットワーク診断ツールを作成することができます。
例えば、getifaddrsを使用して取得したIPアドレスをもとに、ネットワークの接続状態を確認したり、特定のインターフェースのパフォーマンスを測定することが可能です。
また、pingやtracerouteのようなネットワーク診断コマンドを組み合わせることで、より詳細なネットワーク診断を行うことができます。
これにより、ネットワークの問題を迅速に特定し、解決するためのツールを開発することができます。
まとめ
この記事では、C言語を使用してPCのIPアドレスを取得する3つの方法を解説しました。
gethostnameとgethostbyname、ioctl、getifaddrsを用いることで、さまざまなネットワーク環境に対応したIPアドレスの取得が可能です。
これらの方法を理解し、適切に活用することで、ネットワークプログラミングのスキルを向上させることができます。
ぜひ、この記事を参考にして、実際のプログラムに応用してみてください。
 
![[C言語] ホスト名からIPアドレスを取得する方法を解説](https://af-e.net/wp-content/uploads/2024/08/thumbnail-4458.png)
![[C言語] pingを自作する方法を解説](https://af-e.net/wp-content/uploads/2024/08/thumbnail-4457.png)