前へ << HTTP クライアントを作ってみよう(1) | HTTP クライアントを作ってみよう(3) >> 次へ |
1: #!/usr/local/bin/perl -w 2: 3: # $Id: http-client.pl,v 1.3 2003/03/23 11:28:03 68user Exp $ 4: 5: use strict; 6: 7: use Socket; # Socket モジュールを使う 8: 9: # 接続先ホスト名 10: my $host = 'www.cs.gunma-u.ac.jp'; 11: 12: # HTTP プロトコルを使う 13: my $port = getservbyname('http', 'tcp'); 14: 15: # ホスト名を、IP アドレスの構造体に変換 16: my $iaddr = inet_aton($host) 17: or die "$host は存在しないホストです。\n"; 18: 19: # ポート番号と IP アドレスを構造体に変換 20: my $sock_addr = pack_sockaddr_in($port, $iaddr); 21: 22: # ソケット生成 23: socket(SOCKET, PF_INET, SOCK_STREAM, 0) 24: or die "ソケットを生成できません。\n"; 25: 26: # 指定のホストの指定のポートに接続 27: connect(SOCKET, $sock_addr) 28: or die "$host のポート $portに接続できません。\n"; 29: 30: # ファイルハンドル SOCKET をバッファリングしない 31: select(SOCKET); $|=1; select(STDOUT); 32: 33: # WWW サーバに HTTP リクエストを送る 34: print SOCKET "GET /index.html HTTP/1.0\r\n"; 35: print SOCKET "\r\n"; 36: 37: # ヘッダ部分を受け取る 38: while (<SOCKET>){ 39: # 改行のみの行ならループを抜ける 40: m/^\r\n$/ and last; 41: } 42: 43: # ボディ部分を受け取り、表示 44: while (<SOCKET>){ 45: print $_; 46: }思ったより短いでしょう? では少しずつ解説しましょう。
10: my $host = 'www.cs.gunma-u.ac.jp';まず、接続先のホスト名を決めます。
13: my $port = getservbyname('http', 'tcp');この行はおまじないです。HTTP プロトコルを使うから 'http' なんです。
では `TCP' とは何かというと、TCP/IP というプロトコルを使うことを宣言しているのです。 「プロトコルは HTTP と言ったり、TCP と言ったり、いったいどっちなんだ?」 と思った人へ。TCP と HTTP は層 (レイヤ) が違います。TCP はトランスポート層、 HTTP はアプリケーション層です。HTTP というのはただの文字列のやりとりですが、 その下には TCP という より低レベルな仕組みが働いています。 TCP を使うと、データが相手先に届いたかどうか、エラーが発生していないかどうか などを、OS が影でチェックしてくれます。
→ 関数説明: getservbyname
16: my $iaddr = inet_aton($host) 17: or die "$host は存在しないホストです。\n";$host には 'www.cs.gunma-u.ac.jp' という文字列が入っています。他のホストに接続するためには ホスト名でなく、IP アドレス (を4バイトの構造体にしたもの) を使わないといけません。 そこで、inet_aton を使って、ホスト名を IP アドレスに変換します。 inet_aton を呼び出すと、OS が勝手に DNS サーバに問い合わせて IP アドレスに変換してくれます。
もし変換に失敗した場合はそのホストは存在しない (存在するかもしれないけど、DNS に問い合わせても IP アドレスに変換できない) ということなので終了します。
→ 関数説明: inet_aton
20: my $sock_addr = pack_sockaddr_in($port, $iaddr);IP アドレスとポート番号をひとまとめにした構造体を生成します。 これは次の connect のために必要になります。
→ 関数説明: pack_sockaddr_in
23: socket(SOCKET, PF_INET, SOCK_STREAM, 0) 24: or die "ソケットを生成できません。\n";ソケットを生成します。 ソケットというのは、プロセスと外界を繋ぐための出入口のようなものです。 UNIX では、ネットワークごしのデータのやりとりには、必ずソケットを使います。 PF_INET、SOCK_STREAM というのは定数で、Socket モジュールが 値をセットしてくれます。
ソケットを使うと、ファイルを読み書きするのと同じ書き方で、 他のホストとデータのやりとりができます。 普通のファイルハンドルをオープンする場合、open(IN,"...") などとしますが、ソケットの場合は socket(SOCKET, ...) でオープンします。接続した後は、ファイルハンドルと同じように
print SOCKET "....";で、データの送信ができます。
→ 関数説明: socket
26: # 指定のホストの指定のポートに接続 27: connect(SOCKET, $sock_addr) 28: or die "$host のポート $portに接続できません。\n";いよいよ接続 (connect) です。これが正常に行えたらあとはサーバとの データのやりとりに入ります。もし connect に失敗したら www.cs.gunma-u.ac.jp でポート80を見張っている WWW サーバが存在しないのです。 サーバがいないとクライアントがどれだけがんばっても通信はできません。
あるいは、www.cs.gunma-u.ac.jp というホストがない、例えばマシンの電源が 入っていなかったり、ネットワークに接続されていないのかもしれません。
→ 関数説明: connect
31: select(SOCKET); $|=1; select(STDOUT);ソケットに対してバッファリングしないようにします。これもおまじないだと思って下さい。
ほとんどの説明で「これはおまじない」と書きましたよね。 もちろん意味を理解しているにこしたことはありませんが、 TCP/IP プログラミングをする限り、 この部分はほとんど共通なのです。POP3 クライアントでも SMTP クライアントでも 違うのは「接続するホスト名」と「ポート番号」だけです。つまり
$port = getservbyname(プロトコル名,'tcp'); $iaddr = inet_aton(接続するホスト名)この部分を書き換えるだけで、その他は全て同じです。
34: print SOCKET "GET /index.html HTTP/1.0\r\n"; 35: print SOCKET "\r\n";ソケットに対してデータを送ります。これは telnet で行ったものと同じですね。 改行が \n (LF) でなく \r\n (CRLF) なのは、「HTTP プロトコルの改行は \r\n である」 と RFC で決められているからです。
37: # ヘッダ部分を受け取る 38: while (<SOCKET>){ 39: # 改行のみの行ならループを抜ける 40: m/^\r\n$/ and last; 41: } 42: 43: # ボディ部分を受け取り、表示 44: while (<SOCKET>){ 45: print $_; 46: }サーバから送られてきたデータを、ソケットを通じて1行ずつ読み込みます。
telnet で試したように、サーバはヘッダ・空行・ボディを順に送ってきます。 とりあえず表示したいのはボディなので、空行(改行のみの行)がくるまでは、 読み込んだ内容は表示しません。
注意してほしいのは、ソケットというのはデータ送信とデータ受信を 同じファイルハンドルで扱うということです。 送信は print OUT "..."; 、受信は while (<IN>){ などということはなくて、送受信を SOCKET という1つのファイルハンドルで 行います。
このクライアントは後始末を何もしていません。 これは HTTP プロトコルの流れが
前へ << HTTP クライアントを作ってみよう(1) | HTTP クライアントを作ってみよう(3) >> 次へ |
ご意見・ご指摘は Twitter: @68user までお願いします。