バイナリ形式の IP アドレスを文字列に変換する : Objective-C プログラミング
PROGRAM
バイナリ形式の IP アドレスを文字列に変換する
struct sockaddr 構造体は、各種アドレスファミリーのアドレス情報をバイナリー形式で保持しています。
これらのうち、IPv4 と IPv6 のアドレスを文字列に変換する関数が用意されているので、それを使ってアドレス情報を文字列に変換してみます。
IPv4 または IPv6 のアドレスを文字列に変換する
IPv4 と IPv6 の両方に対応した、バイナリ形式のアドレスデータを文字列に変換する関数として "inet_ntop" というものがあります。
ただ、IPv4 と IPv6 の両方に対応しているとは言っても、struct sockaddr 構造体の値をこの "inet_ntop" 関数に渡せば自動的に文字列を生成してくれるという訳ではないので、アドレスの種類(アドレスファミリー)に応じて関数を使い分けなくてはいけないところが少し厄介に思えます。
// 必要なヘッダーは次のような感じです。
#import <arpa/inet.h>
// struct sockaddr 構造体が IPv4 または IPv6 のアドレスを持っていた場合に、それを文字列に変換します。
- (NSString*)getStringFromSockaddr:(const struct sockaddr*)aSockaddr
{
NSString* result;
// 引数が NULL でないことを確認します。
if (aSockaddr != NULL)
{
// アドレスの種類に応じて処理を変えます。
if (aSockaddr->sa_family == AF_INET)
{
// IPv4 の場合の文字列取得を行います。
struct sockaddr_in* addr;
char str_buffer[INET_ADDRSTRLEN];
// 汎用アドレス情報を IPv4 アドレス情報に変換(キャスト)します。
addr = (struct sockaddr_in*)aSockaddr;
// IPv4 アドレスを文字列変換してバッファーに格納します。
if (inet_ntop(AF_INET, &addr->sin_addr, str_buffer, sizeof(str_buffer)) != NULL)
{
// 取得したアドレスバッファーを NSString に変換します。
result = [NSString stringWithCString:str_buffer encoding:NSUTF8StringEncoding];
}
else
{
// IPv4 アドレスを取得できなかった場合の処理です。今回は nil を返すことにします。
result = nil;
}
}
else if (aSockaddr->sa_family == AF_INET6)
{
// IPv6 の場合の文字列取得を行います。
struct sockaddr_in6* addr;
char str_buffer[INET6_ADDRSTRLEN];
// 汎用アドレス情報を IPv6 アドレス情報に変換(キャスト)します。
addr = (struct sockaddr_in6*)aSockaddr;
// IPv6 アドレスを文字列変換してバッファーに格納します。
if (inet_ntop(AF_INET6, &addr->sin6_addr, str_buffer, sizeof(str_buffer)) != NULL)
{
// 取得したアドレスバッファーを NSString に変換します。
result = [NSString stringWithCString:str_buffer encoding:NSUTF8StringEncoding];
}
else
{
// IPv6 アドレスを取得できなかった場合の処理です。今回は nil を返すことにします。
result = nil;
}
}
else
{
// アドレスファミリーが IPv4 や IPv6 でない場合の処理です。今回は nil を返すことにします。
result = nil;
}
}
else
{
// 引数が NULL だった場合は、ここでは nil を返すことにします。
result = nil;
}
return result;
}
以上のように、IPv4 と IPv6 とでアドレスを文字列に変換する流れはほとんど同じです。
ただ、どちらともアドレス情報は struct sockaddr 構造体に格納されているものの、これは IPv4 と IPv6 とでは、具体的なアドレスが格納されている位置が異なるため、それを取り出すために IPv4 か IPv6 かで処理を分けて、適切なアドレスを取り出さなくてはいけません。
そのほかの点としては、IPv4 アドレス用のバッファーであれば INET_ADDRSTRLEN 定数で、IPv6 アドレス用のバッファーであれば INET6_ADDRSTRLEN で、必要最低限のバッファーサイズを取得することができるようになっています。
なお、"inet_ntop" 関数は、文字列への変換に失敗した場合には NULL を返し、エラーコードが errno 大域変数に格納されます。
エラーコードは EAFNOSUPPORT であれば、第一引数で指定されたアドレスファミリーがサポートされていないことを示します。また、ENOSPC だった場合には、変換後の文字列がバッファーに収まらないことを示すとのことでした。
IPv4 専用の文字列変換
アドレスが IPv4 だけである場合には、次のように簡単にバイナリ形式のアドレスを文字列に変換することも可能です。
char* addr_str;
struct sockaddr_in* addr;
// アドレスファミリーで汎用な struct sockaddr を IPv4 用の struct sockaddr_in に変換(キャスト)します。
addr = (struct sockaddr_in*)aSockaddr;
// inet_ntoa に IPv4 アドレス (struct in_addr) を渡して、文字列を取得します。
// 文字列バッファーは共用のものが自動的に使用されます。
addr_str = inet_ntoa(addr->sin_addr);
// 取得した文字列を NSString 型に変換します。
result = [NSString stringWithCString:addr_str encoding:NSUTF8StringEncoding];
このようにだいぶ簡単に、バイナリ形式の IPv4 アドレスを文字列に変換することができました。
ただ、アドレスが保存されるバッファーは、予め inet_ntoa 関数用に用意されている共有バッファーを使用するので、マルチスレッド処理などで、同時に inet_ntoa が実行される可能性がある場合には、予期しない値が取得される可能性があるので、可能であればこの "inet_ntoa" よりも、最初にお話した "inet_ntop" を利用した方が良いかもしれません。
また、マルチスレッド上の問題がなくても、共用バッファーを使用する都合、アドレス文字列を後でも使用する場合には、strncpy 関数などを使用して、再び inet_ntoa を呼び出す前に、取得したアドレス文字列を別のメモリーにコピーしておくのも、忘れないように注意します。
[ もどる ]