筑波大学 システム情報工学研究科 コンピュータサイエンス専攻, 電子・情報工学系 新城 靖 <yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~syspro/2009/No6_files/tcp_connect.html
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~syspro/2009/
http://www.coins.tsukuba.ac.jp/~yas/
65: /* 新城、筑波大学/情報学類/システムプログラム講義用 */ 66: int 67: tcp_connect( char *server, int portno ) 68: { 69: struct addrinfo hints, *ai; 70: struct sockaddr_in addr ; /* IPv4 */ 71: int s ; 72: int err ; 73: 74: if( (s = socket(PF_INET, SOCK_STREAM, 0)) < 0 ) 75: { 76: perror("socket"); 77: return( -1 ); 78: } 79: if( sockaddr_in_init( &addr, sizeof(addr), server, portno )<0 ) 80: { 81: perror("sockaddr_in_init"); 82: return( -1 ); 83: } 84: if( connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0 ) 85: { 86: perror( server ); 87: close( s ); 88: return( -1 ); 89: } 90: return( s ); 91: } 92:まず、クライア ント側のソケットを、socket() システムコールで作成している。 PF_INET と SOCK_STREAMの組み合わせなので、 TCP を使うことを意味する。
socket() の引数で、PF_INET の変りに、AF_INET と書いてもよい。ここでは、 Protocol を選んでいるので、PF_ が正しいが、実際には、PF_INET と AF_INET は同じであり、また、多くのテキストで混在されて使われいる。
ソケットが作成できたら、connect() システムコールで接続する。その第2引 数と第3引数で、接続先のアドレスを指定しなければならない。 接続先には、sockaddr を使う。 この構造体には、IP アドレスとポート番号が含まれている。 この構造体を初期化するめたに、sockaddr_in_init() という 関数を呼び出している。
93: int 94: sockaddr_in_init( struct sockaddr_in *addr, int addrlen, 95: char *hostname, int portno ) 96: { 97: struct addrinfo hints, *ai; 98: int err ; 99: 100: if( addrlen < sizeof(struct sockaddr_in) ) 101: { 102: fprintf(stderr,"sockaddr_in, not enough space (%d) > (%d)\n", 103: addrlen, sizeof(struct sockaddr_in) ); 104: return( -1 ); 105: } 106: memset( &hints, 0, sizeof(hints) ); 107: hints.ai_family = AF_INET ; /* IPv4 */ 108: if( (err = getaddrinfo( hostname, NULL, &hints, &ai )) ) 109: { 110: fprintf(stderr,"unknown host %s (%s)\n",hostname, 111: gai_strerror(err) ); 112: return( -1 ); 113: } 114: if( ai->ai_addrlen > addrlen ) 115: { 116: fprintf(stderr,"sockaddr too large (%d) > (%d)\n", 117: ai->ai_addrlen,sizeof(addr) ); 118: freeaddrinfo( ai ); 119: return( -1 ); 120: } 121: memcpy( addr, ai->ai_addr, ai->ai_addrlen ); 122: addr->sin_port = htons( portno ); 123: freeaddrinfo( ai ); 124: 125: return( 0 ); 126: } 127:
IP アドレスは、文字列で与えられた接続先のサーバのホスト名から 標準の ライブラリ関数 getaddrinfo() を使って調べている。 getaddrinfo() は、ホスト名を IP アドレスに変換し、さらに、struct sockaddr の先頭に定数 AF_INET を設定している。ポート番号は、第2引数が NULL なので、0 を指定している。 (従来は、getaddrinfo() ではなく gethostbyname() が よく使われていた。getaddrinfo() は、IPv6 にも対応している。)
ポート番号は、引数で与えられたものをそのまま使う。ただし、ネットワーク・ バイトオーダ(ビッグエンディアン)に変換する必要がある。 ポート番号として、整数(short)ではなく、/etc/services ファイルに含まれ た文字列で指定したいこともある。その場合は、getaddrinfo() 関数の第2引 数に文字列を渡す。
getaddrinfo() の中で malloc() されたメモリは、free() を直接呼び出して 解放するのではなく、freeaddrinfo() を呼び出して解放する。 (直接 free() を呼ぶと、ai_next の先が解放されない。)
例:0x12345678の置き方。
図? バイト・オーダ
バイトオーダを変換するには、次のような関数を使う。
#include <netinet/in.h> unsigned long int htonl(unsigned long int hostlong ); unsigned short int htons(unsigned short int hostshort); unsigned long int ntohl(unsigned long int netlong ); unsigned short int ntohs(unsigned short int netshort );ここで、n は、network、h は、ホスト、s は short、l は、long を意味する。 ネットワークの変りに、ファイルでもよい。ファイルやネットワークに出力す る時には、htonl() や htons() で標準系(ビッグエンディアン)に変換する。 入力の時は、逆に ntohl() や ntohs() で元にもどす。
ポート番号は、16 ビットなので、ライブラリ関数 htons() (host to network, short))を使って変換している。
IP アドレスもネットワーク・バイトオーダになっている必要がある。これは getaddrinfo() 関数で、struct addrinfo の ai_addr に保存それた段階で既に そうなっている。