| 前へ << UDP を使ってみよう (1) | UDP を使ってみよう (3) >> 次へ | 
UDP プロトコルは単純ですから、ソケット操作自体も単純です。 まずはサーバ側の書き方から。socket(SOCKET, PF_INET, SOCK_DGRAM, 0); bind(SOCKET, pack_sockaddr_in(7000, INADDR_ANY)); recv(SOCKET, $buf, 10000, 0);TCP と比べてみましょう。
- まず socket でソケットを生成します。 TCP では SOCK_STREAM でしたが、UDP では SOCK_DGRAM とします。
- 任意のポートへ bind します (この例ではポート 7000 を使っています)。ここは TCP と全く同じです。
- データの取得は recv を使います。上記の書き方だと 10,000 バイトの データをソケットから読み取り、$buf に格納します。 recv の最後の引数はフラグで、ここでは無指定 (0) です。
UDP TCP 意味 socket (SOCK_DGRAM) socket (SOCK_STREAM) ソケット生成 bind bind ソケットとポートと結合 - listen ポート受け付け開始 - accept コネクションを取得 recv read (<SOCKET> で1行読み込み) データ読み込み 
一方クライアント側は以下のような手順になります。
socket(SOCKET, PF_INET, SOCK_DGRAM, 0); $iaddr = inet_aton('targethost'); $sock_addr = pack_sockaddr_in(7000, $iaddr); send(SOCKET, "hoge", 0, $sock_addr);こちらも TCP と比べてみます。
- socket でソケットを生成します。 ここでも SOCK_DGRAM として UDP を指定します。
- inet_aton と pack_sockaddr_in で、ソケットアドレス構造体を作ります。 この例では、targethost のポート 7000 になります。
- データ "hoge" を targethost:7000 に送ります。 send の第3引数はフラグで、ここでは無指定 (0) です。
UDP TCP 意味 socket (SOCK_DGRAM) socket (SOCK_STREAM) ソケット生成 inet_aton 
pack_sockaddr_ininet_aton 
pack_sockaddr_inソケットアドレス構造体を作成 - connect サーバに接続 send write (print SOCKET "...") データ送信 
今回作成したプログラムは、クライアントとサーバがペアになっています。 まずサーバを% ./udp-server-1.plとして起動します。すると UDP のポート 7000 に届いたデータを表示します。次に (サーバ側を実行させたまま) 同じマシン上で
% ./udp-client-1.pl localhost 7000としてクライアントを起動します。クライアントが起動すると、引数で指定した localhost:7000 に対して、
Hello 1 Hello 2 Hello 3 Hello 4 : Hello 99998 Hello 99999 Hello 100000と、「Hello 送信回数」というデータを 10万回サーバに送信します。 サーバは受信データ=Hello 1 (受信回数 1) 受信データ=Hello 2 (受信回数 2) 受信データ=Hello 3 (受信回数 3) 受信データ=Hello 4 (受信回数 4) :と受信したデータを表示しはじめます。普通に考えると、このまま 100,000 個のデータを受信し、最後は: 受信データ=Hello 99999 (受信回数 99999) 受信データ=Hello 100000 (受信回数 100000)となるように思えるのですが、UDP ですから そうはなりません。 FreeBSD 4.4-RELEASE で、クライアントとサーバを同じマシン上で実行した結果、 以下のようになりました。: 受信データ=Hello 512 (受信回数 512) 受信データ=Hello 513 (受信回数 513) 受信データ=Hello 1624 (受信回数 514) 受信データ=Hello 1625 (受信回数 515) : 受信データ=Hello 98533 (受信回数 3616) 受信データ=Hello 98534 (受信回数 3617) 受信データ=Hello 98535 (受信回数 3618)513 回目までは正しく送信できているのですが、 514〜1623 まではデータがサーバに届いていません。 その後も、89449〜97804、97871〜98469、98536〜100000 は紛失し、結局届いたのは 100,000 分の 3,618。 到達率 3.6%、つまりデータ損失率 96.4%というものすごいことになっています。1: #!/usr/local/bin/perl -w 2: 3: # $Id: udp-client-1.pl,v 1.1 2002/02/17 10:19:18 68user Exp $ 4: 5: use Socket; # Socket モジュールを使う 6: 7: use Errno; # Errno 定数モジュールを使う 8: 9: if ( @ARGV != 2 ){ 10: print "引数で [接続先ホスト名] [接続先ポート番号] を指定して下さい。\n"; 11: exit; 12: } 13: 14: # 接続先ホスト名を取得 15: $host = shift @ARGV; 16: 17: # 接続先ポート番号を取得 18: $port = shift @ARGV; 19: 20: # ホスト名を、IP アドレスの構造体に変換 21: $iaddr = inet_aton($host) 22: or die "$host は存在しないホストです。\n"; 23: 24: # ポート番号と IP アドレスを構造体に変換 25: $sock_addr = pack_sockaddr_in($port, $iaddr); 26: 27: # ソケット生成 28: socket(SOCKET, PF_INET, SOCK_DGRAM, 0) 29: or die "ソケットを生成できません。\n"; 30: 31: # send する回数 32: $num_of_senddata = 100000; 33: 34: # ENOBUFS が発生した回数 35: $num_of_enobufs = 0; 36: 37: for ( $i=1 ; $i<=$num_of_senddata ; ){ 38: if ( ! send(SOCKET, "Hello $i", 0, $sock_addr) ){ 39: # エラーが発生した 40: 41: if ( $! == Errno::ENOBUFS ){ 42: # 送信バッファがいっぱい (ENOBUFS) ならリトライ 43: 44: $num_of_enobufs++; 45: next; 46: 47: } else { 48: # ENOBUFS 以外なら終了 49: die "send に失敗しました ($i)。$!\n"; 50: } 51: } 52: $i++; 53: } 54: 55: print "$host:$port に対して send を $num_of_senddata 回実行しました。\n"; 56: print "ENOBUFS の発生回数 $num_of_enobufs\n";1: #!/usr/local/bin/perl -w 2: 3: # $Id: udp-server-1.pl,v 1.1 2002/02/17 10:19:18 68user Exp $ 4: 5: use Socket; # Socket モジュールを使う 6: 7: $port = 7000; # ポート番号は 7000 番 8: 9: # ソケット生成 10: socket(SOCKET, PF_INET, SOCK_DGRAM, 0) 11: or die "ソケットを生成できません。\n"; 12: 13: bind(SOCKET, pack_sockaddr_in($port, INADDR_ANY)); 14: 15: $recv_count = 0; 16: 17: while (1){ 18: recv(SOCKET, $buf, 10000, 0); 19: $recv_count++; 20: print "受信データ=$buf (受信回数 $recv_count)\n"; 21: 22: # スピード調整のためのウェイト 23: # select(undef, undef, undef, 0.01); 24: }
TCP で似たような動作をするプログラムを書いてみました。 これを実行すると、送信したデータは 100% 届くことがわかります。1: #!/usr/local/bin/perl -w 2: 3: # $Id: tcp-client-vs-udp.pl,v 1.1 2002/02/17 10:19:18 68user Exp $ 4: 5: use Socket; 6: 7: if ( @ARGV != 2 ){ 8: print "引数で [接続先ホスト名] [接続先ポート番号] を指定して下さい。\n"; 9: exit; 10: } 11: 12: $host = shift @ARGV; 13: $port = shift @ARGV; 14: 15: $iaddr = inet_aton($host) || die "$!"; 16: $sock_addr = pack_sockaddr_in($port, $iaddr) || die "$!"; 17: socket(SOCKET, PF_INET, SOCK_STREAM, 0) || die "$!"; 18: connect(SOCKET, $sock_addr) || die "$!"; 19: 20: $num_of_senddata = 100000; 21: 22: for ( $i=1 ; $i<=$num_of_senddata ; $i++ ){ 23: print SOCKET "Hello $i\n" || die "$!"; 24: } 25: 26: print "$host:$port に対して、データを $num_of_senddata 回送信しました。\n";1: #!/usr/local/bin/perl -w 2: 3: # $Id: tcp-server-vs-udp.pl,v 1.1 2002/02/17 10:19:18 68user Exp $ 4: 5: use Socket; 6: 7: $port = 7000; 8: 9: socket(SOCKET, PF_INET, SOCK_STREAM, 0) or die "$!"; 10: setsockopt(SOCKET, SOL_SOCKET, SO_REUSEADDR, 1) || die "$!"; 11: bind(SOCKET, pack_sockaddr_in($port, INADDR_ANY)) || die "$!"; 12: listen(SOCKET, SOMAXCONN) || die "$!"; 13: accept(NEWSOCK, SOCKET) || die "$!"; 14: 15: while (<NEWSOCK>){ 16: chomp; 17: $recv_count++; 18: print "受信データ=$_ (受信回数 $recv_count)\n"; 19: }
| 前へ << UDP を使ってみよう (1) | UDP を使ってみよう (3) >> 次へ |