筑波大学 システム情報工学研究科 コンピュータサイエンス専攻, 電子・情報工学系 新城 靖 <yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~syspro/2009/No7_files/echo-server-nofork-fdopen.html
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~syspro/2009/
http://www.coins.tsukuba.ac.jp/~yas/
84: void 85: print_my_host_port( int portno ) 86: { 87: char hostname[100] ; 88: gethostname( hostname,sizeof(hostname) ); 89: hostname[99] = 0 ; 90: printf("run telnet %s %d \n",hostname, portno ); 91: } 92:gethostname() システムコールで自分自身のホスト名を取り出している。(正 式には、gethostname() の結果とインターネット的なホスト名(IPアドレスと 対応している))が一致して異ないことがある。
93: void 94: tcp_peeraddr_print( int com ) 95: { 96: struct sockaddr_storage addr ; 97: socklen_t addr_len ; /* MacOSX: __uint32_t */ 98: addr_len = sizeof( addr ); 99: if( getpeername( com, (struct sockaddr *)&addr, &addr_len )<0 ) 100: { 101: perror("tcp_peeraddr_print"); 102: return; 103: } 104: printf("[%d] connection (fd==%d) from ",getpid(),com ); 105: sockaddr_print( (struct sockaddr *)&addr, addr_len ); 106: printf("\n"); 107: } 108:通信相手のアドレス(IPアドレスとポート番号)は、getpeername() システムコー ルで得られる。得たアドレスを、sockaddr_print() に渡して表示している。
109: void 110: sockaddr_print( struct sockaddr *addrp, socklen_t addr_len ) 111: { 112: char host[BUFFERSIZE] ; 113: char port[BUFFERSIZE] ; 114: if( getnameinfo(addrp, addr_len, host, sizeof(host), 115: port, sizeof(port), NI_NUMERICHOST|NI_NUMERICSERV)<0 ) 116: return; 117: printf("%s:%s", host, port ); 118: } 119:IP アドレスは、IPv4 では、32 ビット(int)であり、 ポート番号は、16 ビット(sort)である。 ここでは、getnameinfo() ライブラリ関数を用いてホスト名とポート番号の 文字列表現に変換している。この時、NUMERIC と指定しいるので、 IPv4 では、ドット「.」で区切られた10進数4つになる。
getnameinfo() が使われている部分では、以前は、gethostbyaddr() が使われ ていた。
120: tcp_acc_port( int portno ) 121: { 122: struct sockaddr_in addr ; 123: int addr_len ; 124: int s ; 125: 126: if( (s = socket(PF_INET, SOCK_STREAM, 0)) < 0 ) 127: { 128: perror("socket"); 129: return( -1 ); 130: } 131: 132: memset( &addr, 0, sizeof(addr) ); 133: addr.sin_family = AF_INET ; 134: addr.sin_addr.s_addr = INADDR_ANY ; 135: addr.sin_port = htons( portno ); 136: 137: if( bind(s,(struct sockaddr *)&addr,sizeof(addr)) < 0 ) 138: { 139: perror("bind"); 140: fprintf(stderr,"port number %d is already used. wait a moment or kill another program.\n", portno ); 141: return( -1 ); 142: } 143: if( listen( s, 5 ) < 0 ) 144: { 145: perror("listen"); 146: close( s ); 147: return( -1 ); 148: } 149: return( s ); 150: } 151:まず、クライアント側と同様に、ソケットを、socket() システムコールで作成している。 PF_INET と SOCK_STREAMの組み合わせ なので、TCP を使うことを意味する。
socket() の引数で、PF_INET の変りに、AF_INET と書いてもよい。ここでは、 Protocol を選んでいるので、PF_ が正しいが、実際には、PF_INET と AF_INET は同じであり、また、多くのテキストで混在されて使われいる。
ソケットが作成できたら、bind() システムコールで、サーバ側で利用するア ドレス(IPアドレスとポート番号)を設定する。IP アドレスは、IPv4 では普通、 INADDR_ANY を指定する。複数の IP アドレスがある時には、どれに要求が来 ても受け付ける。特定の IP アドレスを指定すると、そのアドレスに来た要求 だけを受け付けるようになる。
ポート番号は、引数で与えられたものを、htons() でネットワーク・バイトオー ダに変換して与える。
次に、listen() システムコールにより、要求受け付けを開始する。第2引数 は、最大何個のクライアントを接続要求待ちで待たせるか(待ち行列の長さ) を指定する。 重たいサーバを設計する時には、キューの長さを調節する。Apache (WWW サー バ) などでは、500 程度になっていることがある。
注意:このプログラムには 複数のクライアントに対してサービスを同時に提供できない という問題がある。
bind() する addr を、getaddrinfo() で調べる流儀(IPv6風)もある。この場 合、getaddrinfo()の第一引数には、NULL を入れ、hints.ai_flags には AI_PASSIVE を設定する。
152: int 153: fdopen_sock( int sock, FILE **inp, FILE **outp ) 154: { 155: int sock2 ; 156: if( (sock2=dup(sock)) < 0 ) 157: { 158: return( -1 ); 159: } 160: if( (*inp = fdopen( sock2, "r" )) == NULL ) 161: { 162: close( sock2 ); 163: return( -1 ); 164: } 165: if( (*outp = fdopen( sock, "w" )) == NULL ) 166: { 167: fclose( *inp ); 168: *inp = 0 ; 169: return( -1 ); 170: } 171: setvbuf(*outp, (char *)NULL, _IONBF, 0); 172: return( 0 ); 173: }この関数は、 クライアント側 とまったく同じである。