ソケットおよびHTMLを使ってWWWサーバから任意のURLのページを取得し、
標準出力に出力するプログラムを作成せよ。
-> HTTPクライアントプログラム
-> POPクライアントプログラム
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.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でも時間があればやってみたいと思った。