何年かおきに忘れるのでメモしておく。
TCPコネクションを行うためには接続先のホスト名からアドレスを知る必要がある。
そのためには二つの方法がある。
- gethostbyname()を使う。
- getaddrinfo()を使う。
どちらの方法も最終的にはconnect()に接続先のアドレスが格納されたsockaddr構造体を渡す。
sockaddr構造体とは、接続するために必要なネットワーク層レベルの通信プロトコルとアドレスとポートの情報を格納した構造体のこと。
ただし、これはそのままでは使われない。
IPv4の場合はsockaddr_in構造体にキャストされて使用される。
IPv6の場合はsockaddr_in6構造体にキャストされる。
サーバーソケットなどで受け取る場合は何が来てもいいように十分な大きさを持つsockaddr_storage構造体にキャストされる。
また、ホスト名が複数のアドレスを持つ場合に複数のsockaddr構造体をリンクリストで保持するaddrinfo構造体やhostent構造体が用意されている。
has-aの関係や同一レベルの階層構造で見るとこうなる。
addrinfo構造体 (48バイト)
メンバ
|-- sockaddr構造体 (16バイト)
|-- sockaddr_in構造体 (16バイト)
メンバ
|-- in_addr構造体 [iPv4のアドレス値]
|-- sockaddr_in6構造体 (28バイト)
メンバ
|-- in6_addr構造体 [iPv6のアドレス値]
|-- sockaddr_storage構造体 (128バイト)
#gethostbyname()を使う
gethostbyname()に文字列でホスト名を渡すとhostent構造体が取得できる。ちなみにgethostbyname()はスレッドセーフではないのでスレッドセーフな関数はgethostbyname_r()になる。
// 名前解決.
struct hostent *hp = gethostbyname("HOST NAME");
// 構造体初期化.
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
// 複数のアドレスの中から最初のものを選択.
addr.sin_addr = *(struct in_addr*)hp->h_addr_list[0];
// インターネット・プロトコルを指定する.
addr.sin_family = AF_INET;
// ポートを指定する.
addr.sin_port = htons(PORT);
// ソケット作成.
if ((_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))<0){
return -1;
}
// sockaddr構造体(sockaddr_in構造体)を渡して接続.
if (connect(_socket, (struct sockaddr *)&addr, sizeof(addr))<0){
return -1;
}
#getaddrinfo()を使う
getaddrinfo()はホスト名とヒントとなる情報を格納したaddrinfo構造体を渡すことで完全なaddrinfo構造体を取得することができる関数。
struct addrinfo hints, *address;
int errcode = 0;
int socket = 0;
// ヒントとなるaddrinfo構造体を初期化.
memset(&hints, 0, sizeof(hints));
// ヒントとしてIPネットワークプロトコルとTCP通信のソケットタイプを指定
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
// addrinfo構造体を取得する.
if((errcode = getaddrinfo("ホスト名", ポート番号, &hints, & address)) != 0){
return -1;
}
// 完全なaddrinfo構造体としてaddressが手に入ったので以後はこれを使う.
// ソケットタイプやプロトコルを指定してソケットを作成する.
if((socket = socket(address->ai_family, address->ai_socktype, address->ai_protocol)) == -1){
freeaddrinfo(address);
return -1;
}
// addrinfo構造体のsockaddr構造体を渡して接続.
if(::connect(socket, address->ai_addr, address->ai_addrlen) == -1){
close(socket);
freeaddrinfo(address);
return -1;
}
freeaddrinfo(address);
各構造体はLinuxでは次のように定義されている。
// 32バイト
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 */
#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
#define h_addr h_addr_list[0] /* address, for backward compatibility */
#endif /* (!_POSIX_C_SOURCE || _DARWIN_C_SOURCE) */
};
// 48バイト
struct addrinfo {
int ai_flags; /* AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST */
int ai_family; /* PF_xxx */
int ai_socktype; /* SOCK_xxx */
int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
socklen_t ai_addrlen; /* length of ai_addr */
char *ai_canonname; /* canonical name for hostname */
struct sockaddr *ai_addr; /* binary address */
struct addrinfo *ai_next; /* next structure in linked list */
};
// 16バイト
struct sockaddr {
__uint8_t sa_len; /* total length */
sa_family_t sa_family; /* [XSI] address family */
char sa_data[14]; /* [XSI] addr value (actually larger) */
};
// 16バイト
struct sockaddr_in {
__uint8_t sin_len;
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
// 4バイト
struct in_addr {
in_addr_t s_addr;
};
// 28バイト
struct sockaddr_in6 {
__uint8_t sin6_len; /* length of this struct(sa_family_t) */
sa_family_t sin6_family; /* AF_INET6 (sa_family_t) */
in_port_t sin6_port; /* Transport layer port # (in_port_t) */
__uint32_t sin6_flowinfo; /* IP6 flow information */
struct in6_addr sin6_addr; /* IP6 address */
__uint32_t sin6_scope_id; /* scope zone index */
};
// 128バイト
struct sockaddr_storage {
__uint8_t ss_len; /* address length */
sa_family_t ss_family; /* [XSI] address family */
char __ss_pad1[_SS_PAD1SIZE];
__int64_t __ss_align; /* force structure storage alignment */
char __ss_pad2[_SS_PAD2SIZE];
};
ついでにアドレスファミリーのプロトコルに指定できるものの種類を書いておく。
アドレスファミリー | 意味 |
---|---|
AF_INET | インターネットプロトコル |
AF_UNIX | UNIXファイルシステムドメイン |
AF_ISO | ISO標準プロトコル |
AF_NS | XeroxNetworkSystemsプロトコル |
AF_IPX | NovellIPXプロトコル |
AF_APPLETALK | Appletalk |
PF_INET | AF_INETと同じ |
PF_INET6 | IPv6 |
PF_IPX | IPX Novell プロトコル |
PF_NETLINK | カーネルユーザデバイス |
PF_X25 | ITU-T X.25(ISO-8208プロトコル) |
PF_AX25 | 無線 AX.25プロトコル |
PF_ATMPVC | ATM PVC |
PF_APPLETALK | Appletalk |
PF_PACKET | 低レベルのパケット |