ソフトウエア基礎
ohmi@rsch.tuis.ac.jp
解答例

ネットワーク(1)

※ 既に情報通信システム基礎、情報通信システム論で、OSI参照モデルや TCP/IPの基礎は理解しているはずなので、 ネットワークの基本的な解説は省略する。

TCP/IP

世の中には、様々なネットワークプロトコルが存在するが、 Javaの標準ライブラリで扱えるのは、TCP/IPのみである。

network1-1

TCP/IPの階層モデルは、OSI参照モデルの7階層ほどは複雑でなく、 4階層である。OSIの上位3つの階層をプレゼンテーション層、下位2階層 がネットワークアクセス層と大雑把になっている。

JavaにおけるTCP/IPの扱い

標準のJavaで、TCP/IPしか扱えないという意味は、ネットワークアクセス層 が扱えない、そして、インターネット層と同レベルのIP以外のプロトコルが 扱えないということである。例えば、ネットワークアクセス層では、Ethernet のMACアドレスとIPアドレスを関連付けるARPがあるが、これは扱えない。 また、インターネット層で、IPと同レベルにある、ICMP(ping, traceroute で使用)も扱えない。

しかし、当然、上位のアプリケーション層(HTTP,SMTP,FTPなど)は、 プログラミング次第で扱うことができる。

この授業では、(HTTP,SMTPなどのアプリケーション層でなく)、 直接TCP/IPのレベル(トランスポート層とインターネット層)で ネットワークを利用するソケットという機構を使う。 このため、アプリケーション層を扱うためには、そのプロトコル (HTTP,SMTPなど)に合った処理を自分でプログラミングする必要がある。

ソケットを使用する低レベルのアクセスに対して、HTTP,SMTP、あるいは HTTPを使って受け取るHTMLを解釈するような高レベルのアクセスが簡単に できるクラスライブラリも用意されている。 また、Webサーバ上で、Javaプログラムを実行するServlet,JSPもネットワーク プログラミングの一種で、アプリケーション層の高レベルのアクセスと言える。 さらに、他のマシンで動作しているJavaプログラムを呼び出せるRMIという 高度な仕組もある。これらはこの授業では取り上げない。

既存サーバへのtelnetによるアクセス

まずは、Javaを離れて、既存のサーバに手動でアクセスを試みて、TCP/IPによる アクセスを体験する。そのためには、telnetをコマンドを使うと良い。

telnetコマンドは通常、他のUNIXマシンにリモートログインするものであるが、 ポート番号を指定することができ、 色々な種類のサーバにアクセスを試みることができる。 以下に主要なポート(Well-known port)番号の一部を示す。

telnetコマンドで接続できるのは、TCPを使うサービスに限られる。 UDPは利用できない。JavaではUDPによる通信も可能だが、ここでは取り上げない。
ポート番号サービス名説明
7echo入力をそのまま返す(出力)。接続テスト用。
13daytimeサーバ上の現在時刻を表示
20ftp-dataftpで転送するデータを送る
21ftpftpのコマンドを送る
22ssh 安全なリモートアクセス(telnet,rcpなどの代替)
23telnet 他のサーバにリモートログイン(端末利用)する。 暗号化されていないため、利用されなくなってきている。
25smtp 電子メールを送信する
43whois インターネットのネットワーク管理者用データベース
79fingerユーザ情報を提供する
80httpWebの基本プロトコル
110pop3 ユーザが電子メールを読み出すためのプロトコル
143imap ユーザが電子メールにアクセスするためのプロトコル

特定のポート番号に対してtelnetでアクセスする場合は、以下のようにする。

telnet <ホスト名> [ポート番号]

ホスト名には、IPアドレスや、インターネット上のホスト名などが使用できる。 ホスト名を指定した場合は、 telnetのプログラムがDNSに問い合わせてIPアドレスを求めて接続している。 また、ポート番号は代わりに、 サービス名(http,ftp,smtpなど)を使うこともできる。 また、ポート番号を省略すると本来のtelnetのポート(23番)に接続する。

例えば、現在使っているマシンのtelnetポートに接続するには、 以下のようにtelnet localhost 23と入力する。

pcxxxx[xxxx]522:~/% telnet localhost 23
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Red Hat Linux release 7.2 (Enigma)
Kernel 2.4.7-10 on an i686
login:

Webサーバへのアクセス

上記の要領で、telnetコマンドを使いWebサーバにアクセスできる。

Webサーバへのアクセスには、http(80番)を使用する。 httpは非常に簡単なプロトコルである。手順はたった3段階である。

なお、80番はhttpのデフォルトのポート番号であるが、 他の番号を使用することもできる。例えば、http://example.jp:81/ というURLは、81番のポートでhttpアクセスをする。
  1. クライアントはWebサーバに接続する。
  2. クライアントはhttpリクエストを送る(空行を送ると送信終了)。
  3. クライアントはhttpレスポンス(応答)を受信する。

例えば、以下のようにする。 青い背景の部分が自分で打ち込む部分、 茶色の字で書かれた部分 がhttpレスポンスである。

pcxxxx[s03xxxxx]523:~/%; telnet www.rsch.tuis.ac.jp 80
Trying 202.26.148.102...
Connected to www.edu.tuis.ac.jp.
Escape character is '^]'.
GET /~ohmi/software-basic/network1.html HTTP/1.1
Host: www.rsch.tuis.ac.jp
Connection: close
 
HTTP/1.1 200 OK
Date: Mon, 29 Nov 2004 15:30:47 GMT
Server: Apache/1.3.27 (Unix) mod_jk/1.2.3-dev PHP/4.2.2
Last-Modified: Mon, 29 Nov 2004 15:18:12 GMT
ETag: "1a8500-2a4-41ab3db4"
Accept-Ranges: bytes
Content-Length: 6433
Connection: close
Content-Type: text/html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html lang="ja">
<head>
<TITLE>ネットワーク(1)</TITLE>
(途中省略)
<HR>
<A href="index.html"><IMG src="icons/prev.gif" alt=""> ソフトウエア基礎</A>
<P></P>
<ADDRESS>ohmi@rsch.tuis.ac.jp </ADDRESS>
<hr>
   <p>
      <a href="http://validator.w3.org/check/referer"><img border="0"
          src="valid-html401.gif"
          alt="Valid HTML 4.01!" height="31" width="88"></a>
    </p>
</body></html>
Connection closed by foreign host.
pcxxxx[s03xxxxx]524:~/%;

この例は、今表示しているページ (http://www.rsch.tuis.ac.jp/~ohmi/software-basic/network1.html) にtelnetを使ってアクセスした例である。なお、このHTMLファイルは、 Shift-JISコードで書かれているため、端末もShift-JISを表示するように 設定しないと文字化けが起こる。

まず、HTTPを使ったURLは、以下のような構造になっている。

この例ではポート番号(80番)が省略されている。 ポート番号を指定する場合は、
http://<ホスト名>:<ポート番号>/<パス>
となる。
http://<ホスト名>/<パス>

この例では、ホスト名がwww.rsch.tuis.ac.jp、 パスが~ohmi/software-basic/network1.htmlである。

以下では、1.サーバへの接続、2.HTTPリクエストの送信、 3.HTTPレスポンスの受信と手順を追って説明する。

1.サーバへの接続

まずは、telnet ホスト名 ポート番号 という 形式で、Webサーバに接続する。そして、HTTPリクエストを打ち込む。

2.HTTPリクエストの送信

Webサーバへの接続に成功したら、HTTPリクエストを送る。

HTTPリクエストは以下のような形式をとる。

リクエスト行(1行)
ヘッダ(複数行)
空行

リクエスト行は以下のような形式となる。

<メソッド名> /<パス> <HTTPのバージョン>

通常WebサーバにあるHTMLファイルを得るには、 メソッドとしてGETを使用する。メソッドは他にも種類がある。 CGIやServletなどサーバ側のプログラムを呼び出す場合は、POSTメソッドも 使用することがあるが、ここでは触れない。

パスの一番先頭に / が必要なことに注意せよ。アクセス使用としている Webサーバの中で絶対パスとして指定しているからである。

また、HTTPのバージョンは、ここではHTTP/1.1を使用する。 Webが普及を始めた初期では HTTP/1.0が使われたが、現在は1.1が主流である。

リクエスト行の次に、ヘッダが続く。ヘッダは <フィールド名> : <値> という形式をとる。ヘッダの種類は非常にたくさんあるが、 ここでは、Host: Connection: のみ入力している。 Host: は、URL中のホスト名をサーバに教えている。 また、Connection: close で、Webサーバから応答があったら、 接続を切るようにしている。

これを省略すると、Keep-Alive(つなぎっぱなし)という接続状態になる。 Keep-Aliveでは、サーバからの応答が終わると、また HTTPリクエストを要求できる。つまり、上記の2.HTTPリクエスト, 3. HTTPレスポンスを何回も繰り返すことができる。 HTTP/1.0では、一回切りの接続しかできず、効率が悪かった。 このため、HTTP/1.1では、 接続はつなぎっぱなしで何度もHTTPの要求ができるKeep-Alive接続が導入された。

ヘッダを入力したら、空行を打てば HTTPリクエストは終了する。 つまりEnterを続けて二回打てば良い。

以上をまとめると、HTTPリクエストは以下のようになる。

GET /<パス> HTTP/1.1
Host: <ホスト名>
Connect: close
空行

3.HTTPレスポンスの受信

HTTPリクエストを送り終えたら、サーバからHTTPレスポンスが返ってくる。

HTTPレスポンスは以下のような形式になっている。

ステータス(1行)
ヘッダ(複数行)
空行
本体(Webページの内容)

ステータスは、 <HTTPバージョン> <ステータスコード>という形式になっている。 この場合、アクセスに成功して HTTP/1.1 200 OK、 つまりステータスコードが "200 OK"という応答になっている。

エラーなど他のステータスコードもある。主要なものを以下に示す。

ステータスコード意味
200 OKリクエストが正しく受け入れられ、応答が返る
400 Bad Requestリクエストの構文に誤りがある
401 Unauthorized アクセス権が必要なページでユーザ名とパスワードが入力されていない、または無効。
403 Forbidden サーバがアクセスを拒否している(ファイルのアクセス権の問題が多い)
404 Not Found リクエストしたページが見つからなかった
500 Internal Server Error サーバ内部のエラー(CGIプログラムが異常終了した場合などに出る)
501 Not Implemented サーバに用意されていない機能を要求した
503 Service Unavailable サーバが要求を処理できない(サーバが過負荷または保守の時)

続いてヘッダ部分には、サーバの情報や、そのページの情報などが付く。 そして、空行の後は、本体(この場合HTMLファイルの内容)が続き、それが 終われば接続が切れる。 ちなみに、画像ファイルなどのバイナリデータも、HTTPレスポンスとしては、 上記と全く変わらず、本体の部分はバイナリデータがそのまま返ってくる。 そのデータの大きさは、ヘッダの Content-Lengthで分かる。また、データ の種類は Content-Type で分かる。データの種類は MIMEという形式が使われて いる。

SMTPサーバ(MTA)へのアクセス

ここで、SMTPを使って電子メールを送信してみる。

SMTPは、メールサーバ(MTA)とメールサーバ間のメール転送を行なうための プロトコルである。ユーザがメーラー(MUA)からメールを送信する時も、 メールサーバに対してSMTPを使って送信する。メールサーバは、 SMTPを話すサーバということで、SMTPサーバと呼ぶこともある。

SMTPのポート番号は、25である。telnetでメールサーバのポート25番に アクセスすれば良い。以下にメール送信の典型例を示す。

pcxxxx[s03888xx]506:~% telnet mailhost.edu.tuis.ac.jp 25
Trying 202.26.148.110...
Connected to mailhost.edu.tuis.ac.jp.
Escape character is '^]'.
220 edu.tuis.ac.jp ESMTP Sendmail 8.12.11/8.11.6; Tue, 30 Nov 2004 22:25:45 +0900
HELO pc23xx.edu.tuis.ac.jp
250 edu.tuis.ac.jp Hello pc3201.edu.tuis.ac.jp [192.168.131.1], pleased to meet you
MAIL FROM: s03888xx@edu.tuis.ac.jp
250 2.1.0 s03888xx@edu.tuis.ac.jp... Sender ok
RCPT TO: s03999yy@edu.tuis.ac.jp
250 2.1.5 s03999yy@edu.tuis.ac.jp... Recipient ok
DATA
354 Enter mail, end with "." on a line by itself
Subject: test mail
To: s03999yy@edu.tuis.ac.jp

This is a test mail.
--
Taro Johou <s03888xx@edu.tuis.ac.jp>
.
250 2.0.0 iAUDPjaF025588 Message accepted for delivery
QUIT
221 2.0.0 edu.tuis.ac.jp closing connection
Connection closed by foreign host.
pcxxxx[s03888xx]507:~%

水色の背景の部分がこちらからSMTPサーバに送信している内容(キーボードから 入力する)で、茶色の部分がSMTPサーバからの応答である。この例のように、 いくつかのSMTPコマンドを使って、一通のメールを送る。 この手順を順番に説明する。

まず、telnetコマンドでSMTPサーバに接続すると、 SMTPサーバからの応答メッセージがある。 ここにはサーバの種類やバージョンが表示される。この例の場合は、 Sendmail 8.12.11/8.11.6 である。応答メッセージは、種類により 番号が振られている。この場合は220番の応答である。

差出人と受取人のメールアドレスが不適切な場合は、エラーが出る。 例えば、最近のメールサーバは、差出人と受取人の双方がメールサーバと 関係ないアドレスの場合、relay denied とエラーを出す。このような メール送信は Third-party Relay と言って、 SPAMを送る場合などに悪用される行為であり、 現在ではほとんどのメールサーバで禁止されている。

次に、HELOコマンドを送る。SMTPで通信を開始する時の挨拶である。 HELOの後には、クライアントのFQDNを書く。FQDNはFull Qualified Domain Nameの略で、インターネット上で特定できる完全な(省略なしの)ホスト名である。 例えば、pc2377.edu.tuis.ac.jpはFQDNである。 対して、同じホストを指すpc2377という省略形はFQDNではない。 HELOコマンドを送ると、pleased to meet you とSMTPサーバから挨拶の返事が返ってくる。

次に、メールの差出人(From)のメールアドレスを伝える。 これには、MAIL FROM: コマンドを使う。

そして、メールの受取人(To)のメールアドレスを伝える。 これには、RCTP TO: コマンドを使う。

次に、メールの本文を送る。これにはDATAコマンドを使う。 DATAと打ち込むと、「メールを入力して、"."だけの行で終了です」 という内容の英文が出る。そこで、メールのヘッダと本文を打ち込み、 終ったら、"."だけ打って改行する。これで本文の入力が完了する。

メールのヘッダと本文の区切りは、空行である。上記の 例では、SubjectヘッダとToヘッダのみ記述している。

最後に、QUITコマンドを打つと、接続が切れて、メール送信が完了する。

以上のように、現状のSMTPでは正しいユーザがメールを送信しているかの認証がなくて もメール送信できる。つまり、メールアドレスを偽ることができる(最近のSPAM の大多数は偽っている)。 クライアントを認証するSMTP AUTHという仕組みも開発されたが、 十分な普及には至っていないのが現状である。

Javaによるインターネットアドレスの処理

ここでは、JavaでIPアドレスやホスト名(ドメイン名)を扱う方法を取り上げる。

Javaでインターネット上のアドレスを扱うには、InetAddressクラスを使う。 InetAddressクラスを使うことで、ホスト名からIPアドレスへの変換(DNSの正引き)、 IPアドレスからホスト名への変換(DNSの逆引き)などが行なえる。

IPアドレスやホスト名といったインターネットアドレスを扱うには、 InetAddressのオブジェクトを生成する。以下はその例である。

import java.net.*;

public class InetAddressTest1 {
    public static void main(String[] args){
        try {
            InetAddress address = InetAddress.getByName("www.edu.tuis.ac.jp");
            System.out.println(address);
        } 
        catch (UnknownHostException e) {
            System.out.print("This host is unknown");
        }
    }
}

これを実行すると以下のようになる。

www.edu.tuis.ac.jp/202.26.148.102

InetAddress.getByName(name)メソッドで、特定のインターネットアドレス のオブジェクトを得ることができる。nameには、ホスト名以外にも IPアドレスを指定することもできる。

また、現在使用しているマシンのインタネットアドレスを得る場合は、 InetAddress.getLocalHost()メソッドを使う。以下はその例である。

import java.net.*;

public class InetAddressTest2 {
    public static void main(String[] args){
        try {
            InetAddress address = InetAddress.getLocalHost();
            System.out.println(address);
        } 
        catch (UnknownHostException e) {
            System.out.print("This host is unknown");
        }
    }
}

実行すると以下のような結果が出る。
※当然マシンが異なれば結果は異なる。

pc3201/192.168.131.1

このように環境によっては、FQDNではなく、省略されたホスト名が出る。

InetAddressオブジェクトからホスト名を求めたり、IPアドレスを求める例を 以下に示す。

import java.net.*;

public class InetAddressTest3 {
    public static void main(String[] args){
        try {
            InetAddress address = InetAddress.getByName("www.tuis.ac.jp");
            System.out.println("HostName is " + address.getHostName());
            System.out.println("IP address is " + address.getHostAddress());
        } 
        catch (UnknownHostException e) {
            System.out.print("This host is unknown");
        }
    }
}

実行すると以下のような結果が出る。

HostName is www.tuis.ac.jp
IP address is 202.26.157.12

つまり、ホスト名を求めたければ、getHostName()メソッドを呼べば、 戻り値として文字列で返ってくる。IPアドレスを求めたければ、 getHostAddress()メソッドを呼べば、戻り値として文字列で返ってくる。

課題net-1

UNIXでは、ポート番号とサービス名の対応が、 /etc/services というファイルで定義されている。 このファイルを見て、69番のポートのサービス名と、 "kerberos"(もしくは "kerberos-sec")というサービス名のポート番号を調べよ。

課題net-2

現在使用しているマシンのdaytimeポートに対して、 telnetコマンドを使ってアクセスせよ。その結果を報告せよ。

課題net-3

ホスト "www.edu.tuis.ac.jp" のdaytimeポートに対して、 telnetコマンドを使ってアクセスせよ。その結果を報告せよ。 この結果はどういう意味であるか推測して答えよ。

課題net-4

telnetを使って、 http://www.rsch.tuis.ac.jp/~ohmi/software-basic/network-test.html にアクセスせよ。その結果を報告せよ。

課題net-5

telnetを使って、 http://www.rsch.tuis.ac.jp/~ohmi/software-basic/network-test2.html と http://www.rsch.tuis.ac.jp/~ohmi/software-basic/network-test3.html にアクセスせよ。それらの結果を報告し、どういうことが起こったのか説明せよ。

課題net-6

telnetを使って、 自分の作成したWebページにアクセスせよ。その結果を報告せよ。

課題net-7

telnetを使って、 自分のメールアドレスにメールを送信せよ。当然差出人も自分とする。 その結果とメーラで受信したメールの内容(メールヘッダも含む)を報告せよ。

課題net-8

telnetを使って、 自分のメールアドレスにメールを送信せよ。差出人のメールアドレスは "hogehoge@ohmi.rsch.tuis.ac.jp" とする。 その結果とメーラで受信したメールの内容(メールヘッダも含む)を報告せよ。

課題net-9

IPアドレス202.26.148.102のホスト名を求める Javaプログラムを作成せよ。また、結果を報告せよ。

課題net-10

ホスト名 www.yahoo.co.jp のIPアドレスを求める Javaプログラムを作成せよ。また、何度か実行してみて結果を見よ。 そこで気づいたことを説明せよ。 余力があれば、なぜそのようになるか調べて説明せよ。

課題net-11

nslookupコマンドのように、ホスト名を入力するとそのIPアドレスを、 IPアドレスを入力するとそのホスト名を出力するJavaプログラムを作成せよ。 なお、Javaの実行時の引数を入力とせよ(以下を参照)。
ヒント: 入力文字列に数字でも"."でもない文字が含まれていればホスト名、 それ以外はIPアドレスであると判定すれば良い。

public class ArgTest {
    public static void main(String[] args) {
        if (args.length != 1) {
            System.err.println("Usage: java ArgTest <arg1>");
        } else {
            System.out.println("arg1 = " + args[0]);
        }
    }
}

ソフトウエア基礎

ohmi@rsch.tuis.ac.jp

Valid HTML 4.01!