筑波大学 システム情報工学研究科
コンピュータサイエンス専攻, 電子・情報工学系
新城 靖
<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の置き方。
図? バイト・オーダ/byte-order.png)
バイトオーダを変換するには、次のような関数を使う。
#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 に保存それた段階で既に そうなっている。