プログラムから自ホストの IP アドレスを取得する必要があったので調べてみました.
3.3 - Winsock プログラム で自分の IP アドレスを取得する方法は?
3種類の方法があり、それぞれに利点、欠点があります:
http://www.kt.rim.or.jp/~ksk/wskfaq-ja/intermediate.html
- 最も簡単な方法は、connect 済みのソケットに対して getsockname() を呼び出すことです。connect 済みのソケットが無ければ、その呼出しは失敗するか、あるいは無意味な情報が返されます。
- ソケットを事前にオープンすることなしに自分のアドレスを取得するには、gethostname() の返却値に対して gethostbyname() を呼び出すことです。この例に示すように、この呼出しはそのホストが持つ全てのインターフェースのリストを返却します。
- 三番目の方法は Winsock 2 でのみ動作します。新しい WSAIoctl() API は SIO_GET_INTERFACE_LIST オプションをサポートしており、返却される情報の一部として、システムの各々のネットワークインターフェースのアドレスが返却されます。
この中で 1. の方法が最も簡単に取得することができるのですが,connect 済みのソケットが存在しない場合には使用できません.ただし,connect 済みのソケットとは“connect(2) システムコールに成功したソケット”であれば何でも良いようです.
UDP ソケットが、bind() 呼出し後の通常の状態である非コネクトソケットであれば、send() や write() は使えません。それは目的アドレスが利用できないためで、データを送るには sendto() しか使えません。
そのソケットに対して connect() を呼び出すと、単に指定されたアドレスとポート番号を、希望の通信相手として記録します。これはつまり、 send() や write() が使えるようになるという意味です。これらは、connect の呼び出しで与えられた目的アドレスとポートをパケットの宛先として使用します。
Programming UNIX Sockets in C - Frequently Asked Questions: UDP/SOCK_DGRAM アプリケーションの作成
UDP ソケットの場合,connect(2) システムコールは単に引数に指定されたアドレスとポート番号を記録するのみなので,指定した IP アドレスが存在しないものであっても成功します.そこで,UDP ソケットを利用してダミーの“connect 済みのソケット”を作成してそれを利用する事を考えてみます.サンプルコードは以下の通りです.尚,下記のコードは POSIX ソケット用です.Winsock の場合は若干の修正が必要となります(どちらでも使えるバージョンは,CLX C++ Libraries - localhost を参照して下さい).
#include <cstring> #include <iostream> #include <string> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <unistd.h> std::string localhost() { std::string dest; int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); if (s < 0) return dest; // コネクションを確立(しようと)するためのダミーの IP アドレス struct in_addr dummy; inet_aton("192.0.2.4", &dummy); struct sockaddr_in exaddr = { 0 }; std::memset(&exaddr, 0, sizeof(exaddr)); exaddr.sin_family = AF_INET; std::memcpy((char*)&exaddr.sin_addr, &dummy, sizeof(dummy)); if (connect(s, (struct sockaddr*)&exaddr, sizeof(exaddr)) < 0) { close(s); return dest; } struct sockaddr_in addr = {0}; socklen_t len = sizeof(addr); int status = getsockname(s, (struct sockaddr*)&addr, &len); if (status >= 0) dest = inet_ntoa(addr.sin_addr); close(s); return dest; } int main(int argc, char* argv[]) { std::cout << localhost() << std::endl; return 0; }
ダミー用の IP アドレスは何でも良かったのですが,例として推奨されているドメイン名とIPアドレス - あどけない話 に習って例に使用される IP アドレスにしておきました(実際に上記の IP アドレスを使用している人がいる可能性が低い).