システムプログラム(第6回): tcp_connect()

                                       筑波大学 システム情報系 情報工学域
                                       新城 靖
                                       <yas@cs.tsukuba.ac.jp>

このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~syspro/2021/2021-05-26/tcp_connect.html
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~syspro/2021/
http://www.coins.tsukuba.ac.jp/~yas/

tcp_connect()

tcp_connect() は、通信路の開 設の仕事のうち、クライアント側の仕事をする関数である。 この関数は、役割さえわかれば、細かい内容は飛ばしてもよい。

 102:	/* 新城、筑波大学/情報学類/システムプログラム講義用 */
 103:	#define PORTNO_BUFSIZE 30
 104:	
 105:	int
 106:	tcp_connect( char *server, int portno )
 107:	{
 108:	        struct addrinfo hints, *ai, *p;
 109:	        char portno_str[PORTNO_BUFSIZE];
 110:	        int s, err;
 111:	        snprintf( portno_str,sizeof(portno_str),"%d",portno );
 112:	        memset( &hints, 0, sizeof(hints) );
 113:	        hints.ai_socktype = SOCK_STREAM;
 114:	        if( (err = getaddrinfo( server, portno_str, &hints, &ai )) )
 115:	        {
 116:	                fprintf(stderr,"unknown server %s (%s)\n",server,
 117:	                        gai_strerror(err) );
 118:	                goto error0;
 119:	        }
 120:	        for( p=ai ; p ; p=p->ai_next )
 121:	        {
 122:	                if( (s = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0 )
 123:	                {
 124:	                        perror("socket");
 125:	                        goto error1;
 126:	                }
 127:	                if( connect(s, p->ai_addr, p->ai_addrlen) >= 0 )
 128:	                {
 129:	                        break;
 130:	                }
 131:	                else
 132:	                {
 133:	                        close( s );
 134:	                }
 135:	        }
 136:	        freeaddrinfo( ai );
 137:	        return( s );
 138:	error1:
 139:	        freeaddrinfo( ai );
 140:	error0:
 141:	        return( -1 );
 142:	}
 143:	

まず、 標準の ライブラリ関数 getaddrinfo() を呼び出して、文字列のホスト名を IPアドレスとポート番号の組みに変換している。 変換された結果は、第4引数で番地が指定された構造体に返される。この構造体 には、socket() システム・コールを呼び出す時に使えるパラメタ (ai->ai_family, ai->ai_socktype, ai->ai_protocol) も返され る。 ポート番号は、引数で与えられた整数をそのまま使う。ただし、 getaddrinfo() は文字列を必要とするので、snprintf() を使って得ている。 ポート番号として、整数ではなく、/etc/services ファイルに含まれた文字列 で指定したいこともある。その場合は、getaddrinfo() 関数の第2引数には、 /etc/services に含まれている文字列を渡す。

次に、クライアント側のソケットを、socket() システムコールで作成している。 第1引数のai->ai_family は、PF_INET かまたは PF_INET6 のいずれかで ある。第2引数は、hints で指定した SOCK_STREAM である。 PF_INET と SOCK_STREAMの組み合わせの場合、 IPv4 の TCP、 PF_INET6 と SOCK_STREAMの組み合わせの場合、 IPv6 の TCP を使うことを意味する。

socket() の引数には、PF_INET/PF_INET6 の変りに、AF_INET/AF_INET6 と書い てもよい。ここでは、Protocol を選んでいるので、PF_ が正しいが、実際には、 PF_INET と AF_INET、PF_INET6 と AF_INET6 は同じである。また、多くのテキ ストで混在されて使われいる。

/usr/include/sys/socket.h:
...
#define	PF_INET		AF_INET
...
#define	PF_INET6	AF_INET6
...

ソケットが作成できたら、connect() システムコールで接続する。その第2引 数と第3引数で、接続先のアドレスを指定しなければならない。 接続先には、 getaddrinfo() で得られた sockaddr を使う。 この構造体には、IP アドレスとポート番号が含まれている。

getaddrinfo() の中で malloc() されたメモリは、free() を直接呼び出して 解放するのではなく、freeaddrinfo() を呼び出して解放する。 (直接 free() を呼ぶと、ai_next の先が解放されない。)


Last updated: 2021/05/12 14:40:39
Yasushi Shinjo / <yas@cs.tsukuba.ac.jp>