->back to toppage

課題内容

ソケットおよびHTMLを使ってWWWサーバから任意のURLのページを取得し、 標準出力に出力するプログラムを作成せよ。

-> HTTPクライアントプログラム
-> POPクライアントプログラム

HTTPクライアントプログラム

httpget.plは任意のURLのページを取得 する事ができるHTTPクライアントプログラムである。
実行結果も併せて示す。
OSの授業でも少し使っていたのでPerlを使用して、引数に任意のURLを入力して 標準出力するプログラムを作成した。
$ARGVを使用することで引数を 使用でき、その引数のホスト名をIPアドレスに変換。 HTTPクライアントなので、ポート番号を80に設定し、 そのポート番号とIPアドレスをまとめて構造体に変換する。 ホスト指定のポートに接続できたら"GET"でヘッダ、ボディを受け取り、 表示する。これにより任意のhtmlソースを表示することができる。

・my $host = $1
引数で例えば"http://www.yahoo.co.jp/"と入力されたとき $1は"www.yahoo.co.jp"の部分を示します。

・getservbyname('http','tcp')
これはHTTPのポート番号を使い、TCP/IPプロトコルを使うことを宣言しています。 HTTPのポート番号は80番と決まっているので、 そのまま80番を使うという意味です。

・inet_aton($1)
・inet_atonは、OSが$1をIPアドレスに変換します。

・socket(SOCKET, PF_INET, SOCK_STREAM, 0)
ソケットを使用するために、ここでソケットを生成します。
PF_INET:InterNet接続する
SOCK_STREAM:TCP接続する
という意味です。

・connect(SOCKET, $sock_addr)
ホスト指定のポートに接続します。$sock_addrは、前にポートと IPアドレスをまとめ、構造体に変換したものです。

・print SOCKET "GET /index.html HTTP/1.0\r\n";
・print SOCKET "\r\n";
wwwサーバに接続します。
GETはHEAD、BODY全てをダウンロードします。
例えばここで、もしHEADだけが欲しい場合、HEADを入力します。
最後の\r\nは改行です。\nだけではないのはHTTPプロトコルでの改行は \r\nと決められているので、これを使わなければなりません。
httpget.pl
#!/usr/local/bin/perl -w

use strict;
use Socket; # ソケットを使う

 # URL、http://host形式でないならエラー表示して終了
if( $ARGV[0] =~ m|^http://([-_\.a-zA-Z0-9]+)/?(.*)$|){
    my $host = $1;
}else{
    print "URLはhttp://host形式にしなさい\n";
    exit;
}

 # ポート番号を80に設定、TCP/IPを使用
my $port = getservbyname('http', 'tcp');

 # ホスト名をIPアドレスに変換、存在しない場合、エラー表示
my $iaddr = inet_aton($1) or die "$1が存在しません\n";

 # ポートとIPアドレスをまとめて、構造体に変換
my $sock_addr = pack_sockaddr_in($port, $iaddr);

 # ソケットを生成、生成失敗の場合、エラー表示
socket(SOCKET, PF_INET, SOCK_STREAM, 0)
    or die "ソケットを生成できません\n";

 # ホストの指定のポートに接続
 # サーバが存在しないならば、エラー表示
connect(SOCKET, $sock_addr)
    or die "$1のポート$portに接続できません\n";

 # ソケットに対してバッファリングしない
select(SOCKET); $|=1; select(STDOUT);

 # wwwサーバに接続する
print SOCKET "GET /index.html HTTP/1.0\r\n";
print SOCKET "\r\n";

# ヘッダを受け取り、改行のみならループを抜ける
while(<SOCKET>){
    m/^\r\n$/ and last;
}

# ボディ部分を表示
while(<SOCKET>){
    print $_;
}
実行結果
[Syogo-NAKAMURA:~] j03036% perl httpget.pl http://www.yahoo.co.jp/


<html>
<head>
<title>Yahoo! JAPAN</title>
<!--京-->
<STYLE TYPE="text/css">
.spacer { line-height: 110%; }
.spacer1 {line-height: 115%; }
#h,#hm{display:none}
</STYLE>

<script language=javascript>
function err(a,b,c) {
return true;
}
var d=document;
window.onerror=err;
</script>

<meta http-equiv="Content-Type" content="text/html; charset=euc-jp">
<meta name="description" content="日本最大級のポータルサイト。
検索、オークション、ニュース、メール、コミュニティ、ショッピング、
など80以上のサービスを展開。あなたの生活をより豊かにする
「ライフ・エンジン」を目指していきます。">
</head>

<body leftmargin="0" topmargin="0" marginwidth="0" 
marginheight="0" onLoad="document.sf1.p.focus()">
<center>

(省略)

POPクライアントプログラム

pop.plはメール受信を行う事ができる POPクライアントである。
実行結果も併せて示す。 POPクライアントのスクリプトもPerlを使用して作成した。
まず、POPサーバ名、ユーザ名、パスワードを入力する。 そして$ARGVを使用することで引数でメソッドを選択することが できるようにした。引数の指定は"RETR"か"LIST"である。 "RETR"はメールの内容を受け取り、表示する。"LIST"はメールの一覧を 受け取り、表示する。"RETR"の後にはメール番号を引数で 入力しなければならない。 メール番号は"LIST"で一覧を見ると確認できる。

・$connect_host、$username、$passwd
POPサーバ名、ユーザ名、パスワードを入力する。 スクリプトのパスワードを伏せることは今はできなかった。

・getservbyname('pop3', 'tcp');
POP3のポート番号(110)を使用し、TCP/IPプロトコルを使用する。

・print SOCKET " "
" "内を入力する。
USER $usernameならユーザ名を$usernameで送信し、 PASS $passwdで$usernameのパスワードを送信する。 間違っていたら、メールは届かない。 $method $mnumはメソッド、"RETR"か"LIST"である。 そして"RETR"の場合はメール番号を入力しなければならない。 最後のQUITは接続を切るという意味である。

・while(<SOCKET>){}
ここで出力する。
pop.pl
#!/usr/local/bin/perl -w

my $connect_host = 'nirai.ie.u-ryukyu.ac.jp'; # popサーバ
my $username = 'j040**'; # ユーザ
my $passwd = '*********'; # パスワード

use Socket;

if($ARGV[0] eq "RETR" || $ARGV[0] eq "retr"){
    $method = "RETR";
    if($ARGV[1] =~ m|^([0-9]+)$|){
	$mnum = $1;
    }else{
	print "メール番号を入力しなさい\n";
	exit;
    }
}elsif($ARGV[0] eq "LIST" || $ARGV[0] eq "list"){
    $method = "LIST";
}else{
    print "メソッドはRETRかLISTを入力しなさい\n";
    exit;
}

 # pop3プロトコルを使用、TCPプロトコルを使用
my $port = getservbyname('pop3', 'tcp');

 # サーバをIPアドレスに変換、サーバが間違っていたらエラー表示
my $iaddr = inet_aton($connect_host)
    || die "$connect_hostは存在しません\n";

 # portとIPアドレスをまとめて、構造体に変換
my $sock_addr = pack_sockaddr_in($port, $iaddr);

 # ソケット生成、失敗した時はエラー表示
socket(SOCKET, PF_INET, SOCK_STREAM, 0)
    || die "ソケットを生成できません\n";

 # 接続、接続失敗したらエラー表示
connect(SOCKET, $sock_addr)
    || die "$connect_hostのポート$portに接続できません\n";

 # SOCKETをバッファリングしない
select(SOCKET); $|=1; select(STDOUT);

print SOCKET "USER $username\r\n"; # ユーザ名を送信
print SOCKET "PASS $passwd\r\n"; # パスワードを送信
 # メソッドによってメール一覧を取得するか、内容を取得する
print SOCKET "$method $mnum\r\n";
while(<SOCKET>){
    m/^\.\r\n$/ && last;
    print $_;
}

print SOCKET "QUIT\r\n";
実行結果
<実行結果>
[Syogo-NAKAMURA:~] j03036% perl pop.pl RETR
Use of uninitialized value in pattern match (m//) at pop.pl line 11.
メール番号を入力しなさい

[Syogo-NAKAMURA:~] j03036% perl pop.pl list
Use of uninitialized value in concatenation (.) at pop.pl line 48.
+OK Qpopper (version 4.0.5) at nirai.ie.u-ryukyu.ac.jp starting.  <4665.1138281091@nirai.ie.u-ryukyu.ac.jp>
+OK Password required for j03036.
+OK j03036 has 5 visible messages (0 hidden) in 196537 octets.
+OK 5 visible messages (196537 octets)
1 190825
2 2283
3 1143
4 1143
5 1143

[Syogo-NAKAMURA:~] j03036% perl pop.pl retr 2
+OK Qpopper (version 4.0.5) at nirai.ie.u-ryukyu.ac.jp starting.  <4671.1138281155@nirai.ie.u-ryukyu.ac.jp>
+OK Password required for j03036.
+OK j03036 has 5 visible messages (0 hidden) in 196537 octets.
+OK 2283 octets
Return-Path: <j04039@ie.u-ryukyu.ac.jp>
X-Original-To: j03036@ie.u-ryukyu.ac.jp
Delivered-To: j03036@ie.u-ryukyu.ac.jp
Received: from localhost (localhost [127.0.0.1])
        by nirai.ie.u-ryukyu.ac.jp (Postfix) with ESMTP id 6810E30E9D4
        for <j03036@ie.u-ryukyu.ac.jp>; Tue, 24 Jan 2006 15:00:36 +0900 (JST)
Received: from nirai.ie.u-ryukyu.ac.jp ([127.0.0.1])
 by localhost (nirai.ie.u-ryukyu.ac.jp [127.0.0.1]) (amavisd-new, port 10024)
 with ESMTP id 23337-05 for <j03036@ie.u-ryukyu.ac.jp>;
 Tue, 24 Jan 2006 15:00:34 +0900 (JST)
Received: from pw039.st.ie.u-ryukyu.ac.jp (pw039.st.ie.u-ryukyu.ac.jp [133.13.52.39])

(省略)

感想

既存のサンプルをもとに、いらない部分、必要な部分、 付け足さなければできない部分を考えて作成した。それによって 短めでわかりやすいスクリプトができた。今回はPerlで作成したが、 CやJAVAでも時間があればやってみたいと思った。

参考文献

「68user's page」

->previous problem
->go to toppage
-> next problem