#!/usr/local/bin/perl -w # $Id: echo-server-select.pl,v 1.2 2002/02/05 17:53:08 68user Exp $ use Socket; # Socket モジュールを使う $port = 5000; # ソケット生成 socket(CLIENT_WAITING, PF_INET, SOCK_STREAM, 0) || die "ソケットを生成できません。$!"; # ソケットオプション設定 setsockopt(CLIENT_WAITING, SOL_SOCKET, SO_REUSEADDR, 1) || die "setsockopt に失敗しました。$!"; # ソケットにアドレス(=名前)を割り付ける bind(CLIENT_WAITING, pack_sockaddr_in($port, INADDR_ANY)) || die "bind に失敗しました。$!"; # ポートを見張る listen(CLIENT_WAITING, SOMAXCONN) || die "listen: $!"; print "ポート $port を見張ります。\n"; %data_sockets = (); # 現在有効なデータコネクション用のハッシュテーブル $client_num = 0; # クライアントの通し番号 $rin = &set_bits(CLIENT_WAITING); # ビット列を生成 while (1){ $ret = select($rout=$rin, undef, undef, undef); printf("\$ret=$ret \$rout=%s,\$rin=%s\n", &to_bin($rout), &to_bin($rin)); if ( vec($rout, fileno(CLIENT_WAITING), 1) ){ # 新たにクライアントがやってきた # ソケット名は毎回違う名前にする $new_socket = "CLIENT_$client_num"; $sockaddr = accept($new_socket, CLIENT_WAITING); # ホスト名、IPアドレス、クライアントのポート番号を取得 ($client_port, $client_iaddr) = unpack_sockaddr_in($sockaddr); $client_hostname = gethostbyaddr($client_iaddr, AF_INET); $client_ip = inet_ntoa($client_iaddr); print "接続: $client_hostname ($client_ip) ポート $client_port\n"; print "ソケット $new_socket を生成します。\n"; # クライアントに対してバッファリングしない select($new_socket); $|=1; select(STDOUT); # 接続中のクライアントをテーブルに登録 $data_sockets{$new_socket} = 1; # select に渡すビット列を更新 $rin = &set_bits(CLIENT_WAITING, keys %data_sockets); $client_num++; } elsif ( $ret ){ # 接続中のクライアントから、データが送信されてきた foreach $sock ( sort keys %data_sockets ){ # どのクライアントかを一つずつ確かめる print " check... $sock\n"; if ( vec($rout, fileno($sock), 1) ){ if ( $in = <$sock> ){ # 1行読んでそのまま返す print " $sock からの入力 .. $in"; print $sock "$in"; } else { # エラー発生=コネクション切断 print " コネクション切断 $sock\n"; close($sock); # ファイルハンドルを close delete $data_sockets{$sock}; # テーブルから削除 # select に渡すビット列を更新 $rin = &set_bits(CLIENT_WAITING, keys %data_sockets); } } } } } #---------------------------------------------------- # 1個以上のファイルハンドルを受け取り、fileno で各ファイルハンドルの # ディスクリプタ番号を調べ、それに対応するビットを立てたデータを返す。 # 例えば # fileno(CLIENT_WAITING)==3 # fileno(CLIENT_1) ==4 # fileno(CLIENT_3) ==6 # のとき、 # &set_bits(CLIENT_WAITING, CLIENT_1, CLIENT_3) # は # 01011000 # というデータを返す。 sub set_bits { @sockets = @_; print "select に渡すビット列 \$rin を生成します。\n"; $rin=""; foreach $sock (@sockets){ # $rin の、右から数えて fileno($sock) 番目のビットを1にする。 vec($rin, fileno($sock), 1)=1; printf(" fileno($sock) は %d なので \$rin は %s になります。\n", fileno($sock), &to_bin($rin), ); } return $rin; } #---------------------------------------------------- # 引数を受け取り、2進数の文字列(010111...)に変換して返す。 sub to_bin { return unpack "B*", $_[0]; }