UNIXネットワークプログラミングに登場する構造体の紹介と正しい使い方


今まで詳しく説明して来なかった sockaddr_un, sockaddr_in, hostent型の 構造体とその正しい設定の仕方について解説します。 正しい使い方は少々面倒なので今まで簡略化して使い方をしてきましたので、 正しい使い方をしたい人は以下を参考にしていままでのプログラムを 書き直してください。

プロセスが持つソケットにプロセス間通信ができるように OS上のアドレスを割り当てるための手続き書類を作成するための sockaddr型構造体があります。これは sys/socket.h 内で定義されています。

struct sockaddr { u_char sa_len; /* total length */ sa_family_t sa_family; /* address family */ char sa_data[14]; /* actually longer; address value */ };
しかし、この sockaddr型は汎用型であり、実際はこれを UNIX LOCAL用に特化した sockaddr_un型と Internet用に特化した sockaddr_in型 を使います。


次は UNIX LOCAL なソケットのアドレスの情報を表すsockaddr_un型構造体です。 これは sys/un.h 内で定義されています。

struct sockaddr_un { u_char sun_len; /* sockaddr len including null */ u_char sun_family; /* AF_UNIX */ char sun_path[104]; /* path name (gag) */ };
この変数を用いてUNIX LOCALなソケットのアドレスを指定したり、逆にソケットの アドレスを調べたりします。アドレスを指定する時には 第2メンバ sun_family には #define 定数 AF_LOCAL の値を代入して、 第3メンバ sun_path にアドレスの代わりとなる UNIX domain ソケット と呼ばれるファイルの名前を代入しておきます。 アドレスを調べる時には、調べられた結果がこの変数に入り、 第1メンバ sun_len にはこの変数のサイズが代入されて、sun_family には AF_LOCAL、sun_path には用いられているアドレスの UNIX domain ソケットの ファイル名が代入されています。
余談ですが sun_family には昔は AF_UNIX の#define定数を代入して いましたが、最近は Windowsでもこれが使えるので AF_LOCAL を 代入するようです。どちらも実際の数値は同じです。

sockaddr_un型変数の"正しい"設定の仕方は以下のとおりです。

sockaddr_un addr; bzero( &addr, sizeof(addr) ); addr.sun_family = AF_LOCAL; strcpy( addr.sun_path, "/tmp/localudp" );
私はこれがなんとなく見苦しいので例のマクロを使って初期化しています。
sockaddr_un addr = SOCKADDR_UN_INIT( AF_LOCAL, "/tmp/localudp" );

次は Internet用ソケットのアドレスの情報を表すsockaddr_in型構造体と IPアドレスを記述する in_addr型構造体です。 これらは netinet/in.h 内で定義されています。

struct sockaddr_in { u_char sin_len; u_char sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero[8]; }; struct in_addr { u_int32_t s_addr; };
この変数を用いてInternet用のソケットのアドレスを指定したり、逆にソケットの アドレスを調べたりします。アドレスを指定する時には 第2メンバ sin_family には #define 定数 AF_INET の値を代入して、 第3メンバ sin_port にマシン内でのアドレスの代わりのようであるポートの値を network byte orderで代入しておきます。 第4メンバ in_addr型構造体は1メンバだけを持つ構造体で、この変数 s_addr にはマシンのIPアドレスを network byte orderで代入しておきます。 アドレスを調べる時には、調べられた結果がこの変数に入り、 第1メンバ sin_len にはこの変数のサイズが代入されて、sin_family には AF_INET、sin_port, sin_addr にはポートとIPアドレスが代入されています。

sockaddr_in型変数の"正しい"設定の仕方は以下のとおりです。

sockaddr_in addr; bzero( &addr, sizeof(addr) ); addr.sin_family = AF_INET; addr.sin_port = htons(80); inet_aton( "127.0.0.1", &addr.sin_addr );
私はこれがなんとなく見苦しいので例のマクロを使って初期化しています。
sockaddr_in addr = SOCKADDR_IN_INIT( AF_INET, htons(80), InAddr("127.0.0.1")) );

次はマシンのIPアドレスなどの情報を調べる際に使う hostent型構造体です。これは netdb.h 内で定義されています。

struct hostent { char *h_name; /* official name of host */ char **h_aliases; /* alias list */ int h_addrtype; /* host address type */ int h_length; /* length of address */ char **h_addr_list; /* list of addresses from name server */ #define h_addr h_addr_list[0] /* address, for backward compatibility */ };
h_nameの指すアドレスにはマシンの正式名称が入り、 h_aliases[i]の指すアドレスにはマシンの別名が、もし存在すれば入ります。 このポインタ配列の最後の要素は NULL になっています。 h_addr_list[i]の指すアドレスにはマシンが持ついくつかのIPアドレスが入りますが、 普通0番目の要素しか使わないので、h_addr という #define マクロが用意されています。 この h_addr は char* 型ということになりますが、IPアドレスはこのアドレスに 文字列として入っているのではなく、h_addr[0], h_addr[1], h_addr[2], h_addr[3] の計 4つのchar型変数に整数値として入っています。これを in_addr型構造体の変数に 代入するには正しくは bcopy関数を使うのですが、 *(in_addr*) h_addr とする キャスト変換でもできます。

hostent型変数の"正しい"設定の仕方は以下のとおりです。

// program hostent.cc #include <stdio.h> #include <string.h> #include <netdb.h> #include "integnet.h" int main( int, char* argv[] ) { hostent* hp; if( (hp = gethostbyname( argv[1] )) == NULL ){ herror("gethostbyname"); return 0; } printf("h_name = %s\n", hp->h_name ); printf("h_addrtype = %d\n", hp->h_addrtype ); printf("h_length = %d\n", hp->h_length ); int i; for( i=0; hp->h_aliases[i]; i++ ){ printf("h_aliases[%d] = %s\n", i, hp->h_aliases[i] ); } for( i=0; hp->h_addr_list[i]; i++ ){ in_addr sin_addr; bcopy( hp->h_addr_list[i], &sin_addr, hp->h_length ); printf("h_addr_list[%d] = %s\n", i, inet_ntoa(sin_addr) ); } return 0; }

目次

Copyright(C) by Naoki Watanabe. Jan 1st, 2001.
渡辺尚貴 naoki@cms.phys.s.u-tokyo.ac.jp