ネットワークプログラミング

Menu Menu


目的

この実験では、ネットワークプログラミングを詳細に学ぶ。インターネットで使われている通信規約であるTCP/IPや、Ethernet の構造、リピータやルータなどに関する知識を実際のコンピュータ上で試す。さまざまなネットワーク管理のためのコマンドについても勉強する。Mailサーバ、NNTPサーバやWWWのサーバなどのプロトコルを調べる。そして、C言語とJavaによりクライアントのプログラムを作成、および、サーバのプログラムを作成する。さらにモーバイル・エージェントに関してもプログラムを行う。

    情101	ソフトウエア基礎 I	2
    情102	ソフトウエア基礎 II	2
    情103	ソフトウエア基礎演習 I	1
    情104	ソフトウエア基礎演習 II	1
    情211	情報通信	2


課題の選択

以下の 課題は必修とする。

		■課題 サービス
		■課題 IPアドレスとイサーネットアドレス (必修)
		■課題 ARP (必修)
		■課題 ブロードキャスト
		■課題 ルータ
		■課題 telnet を使った finger サーバへのアクセス

以下の課題は3つのグループのうち一つを選択。
		グループ1

		■課題 telnet を使った WWW サーバへのアクセス

		グループ2

		■課題 telnet を使ったニュースサーバへのアクセス
		■課題 NNTP コマンド

		グループ3

		■課題 SMTP の Verify コマンド
		■課題 メール転送の詳細

以下の課題は3つのグループのうちjava と C の二つの別なグループを選択する。

		グループ1

		■課題 rfinger コマンド
		■課題 wcat コマンド
		■課題 javaのwcat

		グループ2

		■課題 nncat コマンド
		■課題 javaによるNNTPクライアント

		グループ3

		■課題 mverify コマンド
		■課題 javaによるSMTPクラアント

以下の課題は、グループを 一つ以上を選択する

		グループ1

		■課題 http-simple のコンパイルと実行
		■課題 ファイルの内容を返す http-simple
		■課題 WWW サーバからのプログラムの実行
		■課題 アクセス制限
		■課題 javaによるサーバ

		グループ2

		■課題javaによるデータグラム・ブロードキャスト

		グループ3

		■課題javaによる移動エージェント


ネットワーク

ネットワークは、物理的な線、例えば、

	電話線
	ISDN
	Ethrenet cable
		10BASE-5,10BASE-2,10BASE-T
	光ケーブル
	ATM交換機

などの上にプロトコルを定義することによって作られる。プロトコルには、

	TCP/IP (Transmission Control Protocol/Internet Protocol)
	Netware
	NETBEUI
	X.25

など、さまざまなものがある。これらは任意に組み合わされる。電話線上のTCP/IPやEthernet上のNetware、X.25上のTCP/IPなどである。

情報工学科ではインターネットの主流であるTCP/IPを中心に勉強する。これらの上に、さらに、様々なサービスが定義される。

	File Transfer (FTP)
	Mail (SMTP)
	Domain Name Service (BIND)
	Information Server (WWW)
	News (NNTP)
	Computer Supported Cooperated Work
	Personal Information Service (LDAP, ICQ)

これらのサービスは、必然的にプロトコルに依存する。()内は、TCP/IPの場合のサービス名である。WWW使用したければTCP/IPを用いる必要がある。


TCP/IPの詳細

TCP/IPは、信頼性のある(reliable)双方向のストリーム転送サービス(stream transport service)を提供する通信プロトコルである(図1)。ストリームとは、通信する2つのプロセス間に結合(connection)が形成され、複数回に分けて送り出したデータでも、順番が入れ替わらないが、データの区切りがわからなくなるような転送サービスである。UNIXのパイプは、双方向ではなく、単方向であるが、同じストリームに分類される転送サービスである。

なお、C言語のライブラリ関数である fopen(), fgets(), fputs() なども、ストリームと呼ばれることがある。これは、もともとランダム・アクセス可能で、メモリ中の配列と同じようにアクセスするすることもできるファイルを、まるでプロセス間通信のストリームと同じように扱うことができることに由来する。ストリームの元の意味は、プロセス間通信である。


TCP/IPによる通信では、図2に示すように、4つのプロトコル(規約、約束事)の層を構成される。TCP/IP自身は、TCP層と IP層という2つの層に分解される。このようにさまざまなプロトコルが決められ、全体として層をなしている。この様子を、プロトコル・スタックと呼ぶ。

TCP は、IP という通信プロトコルを利用して実現されている。IPは、(信頼性がない)データグラム(datagram)転送サービスを提供する通信プロトコルである。データグラムでは、データの送り手と受けての間に結合が形成されず、途中で順番が変ることや失われることがある。データグラムは、書留めではない郵便に似ている。IPのデータグラムが配達されるときに使われる番地が、IP アドレスである。IPアドレスとしては、現在32ビットの整数が使われている。

TCP層の上は、応用層がある。この層では、ftp, rlogin, WWW, mnews, sendmail などの、TCP/IP を利用するプログラムの間の会話の方法が定義される。TCP/IPを使った通信は、まるでプロセス同士が電話で会話をしているように進められる。普通の電話では、日本語を話す人と英語を話す人は、電話で情報交換ができない。同様に、同じTCP/IPを使っていても、会話の約束事が違うと、まったく情報交換ができない。ゆえに、TCP/IPの上にさらに、情報交換のためにさまざまなプロトコルが取り決められている。

TCP/IPの上に構築されているプロトコルの例を、表1に示す。ポート番号については、後述する。


表1 TCP/IPの上に構築されているプロトコルの例

表1 TCP/IPの上に構築されているプロトコルの例
ポート番号 名前目的
21 FTP(File Transfer Protocol) ファイル転送
23 Telnet 遠隔ログイン(telnet)
25 SMTP(Simple Mail Transfer Protocol) 電子メールの転送
79 finger fingerコマンド
80 HTTP(HyperText Transfer Protocol) WWWのデータ転送
119 NNTP(Network News Transfer Protocol) ネットワーク・ニュースの記事の転送
513 login 遠隔ログイン(rlogin)

IPのデータグラムを転送するためには、さまざまな物理的な媒体が使われる。現在LANでは、インターネットやFDDIがよく使われいる。イーサネットは、同軸ケーブルやより対線(Twisted Pair)を使ってデータを転送する。FDDIは、光ケーブルを使っている。モデムなどを使ったシリアル回線では、PPP(Point to Point Protocol)というプロトコルの上に、IPデータグラムが流される。

データグラムは、ネットワーク通信では、最も基本的な転送サービスである。IP上に構築された UDP(User Datagram Protocol)も、IPとほとんど同じ機能を提供する。また、イーサネットやFDDIが提供する転送サービスも、データグラムである。


■課題 サービス

ping, telnet、ftp、smtp、nntp、DNS等の主なサービスではTCP、UDPのどちらが使用されているか調べなさい。またその理由をTCPとUDPの性質に基づいて説明しなさい。/etc/services の役割は何か?


IPアドレスとドメイン名

TCP/IPによる通信は、通信相手のIPアドレス(32ビットの整数)とポート番号さえわかれば、可能である。しかしながら、人間にとってIPアドレスは扱いやすいものではない。人間にとってわかりやすい記号の名前から、IPアドレスに変換するサービスがあれば便利である。このサービスを、名前サービス(DNS, Domain Name Service)、それを行うプログラムを名前サーバという。

インターネットにおける名前サービスは、名前空間をドメイン(領域)に分割して、階層的管理することで実現されている。これを、ドメイン・ネーム・サービス(Domain Name Service, DNS)という。DNSという言葉は、名前サービスを提供するプログラム(名前サーバ)を差すこともある。

DNSでは、主に名前をIPアドレスへ変換するサービスが使われている。その他に、名前から電子メールの配送先、名前から名前サーバが動いているホストの名前、名前から、名前サーバ自身の管理情報、逆にIPアドレスから名前を引くためにも使われる。

	


■課題 IPアドレスとイサーネットアドレス (必修)

32bitのIPアドレスは通常、

	133.14.48.2

のように、8bitづつ区切った4つの数値(オクテット octet)で表す。

イサーネット上では個々のホストにイサーネットアドレス(Ethernet address)と呼ばれる48bitの番号が付いている。これらは、16進数12桁、あるいは、:で区切った6個の2桁の16進数

	0:4:ac:90:60:22

で表すことが多い。

自分の使っているコンピュータのDNS名とIPアドレスとイーサネットアドレスを調べよ。この時、

	man ifconfig
	man netstat
	man hostname
	man arp

を参考にすること。pw では、/etc/netsart に、これらの情報がある。

DNS名とIPアドレスの対応は、DNS server に

	nslookup コマンド

を使って問い合わせることができる。pw や nw のIPアドレスを nslookup コマンドを使って調べよ。また、各研究室のコンピュータではどうか? 情報処理センタのコンピュータではどうか? Internet 上のコンピュータでは? どのDNS が答えたかに注目せよ。また、Unix での /etc/resolv.conf に関する情報を man -k を使って調べよ。


ネットワークとインターネット

インターネットとは、複数のネットワークがつながったものである。特に、Internet というと、アメリカのArpanetを起源とした Unix と TCP/IP を中心に構成されたインターネットをさす。

internetの図

ネットワークとは、ネット=網、ワーク=作品、ということで網状の構成をもつものという意味である。ここでは、特にコンピュータどうしを接続したものをさす。ネットワークには基本的には二通りある。

  1. バス型のネットワーク

    同じ線に複数のコンピュータを並列に接続するもの。安価であり、代表的なイサーネット(Ethernet)は爆発的に普及した。長さに制限があり(数百メートル)ビル内などの接続に向いている。特に、同じ線に接続された、すべてのコンピュータに同時にデータ(パケットと呼ばれる)を送る機能(ブロードキャスト broadcast)があるのが普通であり、それが特色になっている。

  2. ポイント対ポイント型のネットワーク

    二つのコンピュータを繋げるもの。比較的長い距離をつなげられる。企業や大学間の接続、また、家庭や1台のコンピュータをInternetにつなげる時には、この方式を使う。この型のネットワークにはブロードキャストというのは意味がない。送方向に同時に通信できるものを全二重(full duplex)と呼び、一度には一方向しかデータが送れないものを半二重(half duplex)と呼ぶ。電話は、通常、半二重であり、なんらかのプロトコルで全二重を模倣(Simulate)している。

    最近では、ネットワークの能力を高めるために、この二つを合わせたような星型の接続を行うことがおおい。

    networkの図

    Internetでは、このようなさまざまな形のネットワークが相互に接続されている。


    ホストとルータ

    ネットワークに接続されている計算機の中で、ネットワークに1ヵ所の出入り口(インタフェース)を持っているものは、ホストと呼ばれる。2ヵ所以上の出入り口を持っている計算機は、ルータと呼ばれる。ルータを使うことにより、ネットワークとネットワークを接続することができる。ルータは、入ってきたIPのパケットのIPアドレスを見て、どのネットワークに送ればよいかを判断する。ルータ専用に設計された計算機は特にスィッチング(Switching)ネットワークとか、スィッチングハブなどと呼ばれる。

    さらに、細かい固定長パケットを交換回線網上で高速に通信する機構を持つものをATM (Asynchronous Transmission Mode)という。琉球大学では、ATM を学科間の接続に使用している。

    図1で、左端と右端にあり、4層全てそろっている部分がホストである。TCP/IPの通信は、ホストとホストの間で行われる。中央の、2層しかない部分は、ルータである。ルータの仕事は、IP層において行われる。

    ルータが接続するネットワークには以下の三種類の接続(または、さらにそれらの混合)が考えられる。

    	ポイント対ポイント型のネットワークとポイント対ポイント型のネットワーク
    	バス型のネットワークとポイント対ポイント型のネットワーク
    	バス型のネットワークとバス型のネットワーク
    
    
    この時に問題になるのはブロードキャストの扱いである。それぞれの扱いの差により、リピータやルータというように呼ばれる。

    	ブロードキャストをそのまま各ネットワークに配る (リピータ)
    	ブロードキャストを通さない (ルータ)
    
    
    ルータを越えてブロードキャストを効率的に行うのは難しい。特に放送するホストやネットワークを限定して放送を実現するプロトコルをマルチキャストMulticastという。


    IPアドレスのネットワーク部分とホスト部分

    インターネットのように、複数のネットワークからなる場合、IPアドレスは、ネットワーク部分とホスト部分を持つ。インターネットが木構造を持つ場合は、IPアドレスも同様の木構造を持つ。

    例えば、琉球大学全体では、

    	32bit中16bitがネットワーク、16bitがホスト (Class Bと呼ばれる)
    
    
    だが、情報工学科では、
    	32bit中20bitがネットワーク、12bitがホスト
    
    
    という構成になっている。より小さな学科では、
    	32bit中24bitがネットワーク、8bitがホスト (Class C)
    
    
    などが使われる。

    ネットワーク部分を32bitから抜き出すbit maskを定義することができる。これはnetmaskと呼ばれる。ifconfig コマンドでは IPアドレスの他にnetmaskも定義する必要がある。


    ■課題 ARP (必修)

    IPアドレスは、イサーネット上で ARP (Address Resolution Protocol)を通して、イサーネットアドレスと結びつけられる。
    1. ネットワークドライバが「このIPアドレスを持つイサーネット装置はどれか」を問い合わせるブロードキャスト(放送)パケットを出す

    2. 該当する装置が「自分のイサーネットアドレス」を返す

    3. そのIPアドレス/イサーネットアドレスの組をarp tableに登録する

    arp コマンドを使って arp table を表示し、ping コマンドで arp table にないアドレスにパケットを送ることにより、arp table にイサーネットアドレスが登録される様子を観察せよ。


    ■課題 ブロードキャスト

    ifconfig コマンドを使って、コンピュータが接続しているTCP/IPのブロードキャストアドレスを調べよ。また、そのブロードキャストアドレスに対してpingをしてみるとどうなるか。この結果からイサーネットのブロードキャストの信頼性について考察せよ。


    ■課題 ルータ

    前の課題から、イサーネットではブロードキャストか届く範囲でしか通信できないことがわかる。この範囲は物理的な制約とプロトコル的な制約できまる。物理的な制約は、パケットをそのまま複製するリピータによって逃げることができるが、プロトコル的な制約は逃げられない。また、リピータでも繋げられる個数に制限が存在する。

    逆に、ブロードキャストを多用するプロトコルでは、千を越えるコンピュータをネットワークに接続することはできない。したがって、WAN (Wide Area Network)を実現するためには、複数のイサーネットをルータで接続する。このルータのアドレスを知らないと、パケットをLAN(Local Area Network) を越えてInternetに出すことはできない。この情報をrouting information (ラウティング情報)という。netstat -r コマンドでコンピュータのラウティング情報を表示してみよ。また traceroute コマンドを使って、Internet上の有名なサイトまでのパケット中継の様子を調べよ。


    仮想回線

    TCP/IP では、プロセスとプロセスが、電話で会話をするように通信が行われる。普通の電話で人間同士が話をするには、まず電話番号を指定して、話相手に電話をとってもらわなければならない。TCP/IPでは、電話を掛ける方をクライアント・プロセス、電話を待つ方をサーバ・プロセスと言いう。

    TCP/IPにおいて、プロセス間に形成されたストリーム通信路のことを、計算機間に張られた物理的な回線に似ていることから、仮想的回線(virtual circuit)とも言う。TCP/IP では、回線を接続する段階では、クライアント・プロセスとサーバ・プロセスは非対称である。一度仮想回線が接続された後は、両方のプロセスは、TCP/IPのレベルでは、まったく対称的になる。

    TCP/IPでプロセス間に仮想回線を開設するには、IPアドレスとポート番号が使われる。ポート番号は、同じIPアドレスを持つホスト上で動いているプロセスを区別するために使われる。

    図3に、通信路が開設される手順を示す。

    1. サーバ・プロセスがポート番号を指定して、要求受付用ポートを作る。サーバ・プロセスは、クライアント・プロセスからの接続要求を待つ。要求受付用ポートでは、データの送受信はできない。

    2. クライアント・プロセスが通信用ポートを作る。このポートを、サーバ・プロセスが動いているIPアドレスと、サーバ・プロセスが作った通信ポートのポート番号を使って、接続要求を行う(図3(a))。

    3. 接続要求が受け付けられると、サーバ・プロセスには、新たに通信用ポートが作られる(図3(b))。これは、特定のクライアントとの通信のために使われる。

      こうして一度通信路が開設されると、クライアントとサーバは、どちらからでもデータを送り始めることができる。

    表1に示されているポート番号の例は、公に利用目的が決められているものである。それ以外に、1024以上のポート番号は、特に利用目的が決められていないので、空いていれば利用者が自由に使ってもよい。


    プロセス間通信におけるクライアント・サーバ・モデル

    プロセス間通信は、本来自由に行うことができる。どのプロセスも自由にメッセージを送信する権利がある。

    クライアント・サーバ・モデルは、本来対称的なプロセスを最初にメッセージを送る方(クライアント・プロセス)と受ける方(サーバ・プロセス)に分類することで、プロセス間通信を構造化し、わかりやすくするものである。TCP/IPの接続におけるクライアントとサーバの役割は、このプロセス間通信におけるクライアント・サーバ・モデルの1つの例になっている。

    その他に、クライアントとサーバという言葉は、サービスを受ける方と提供する方の意味で使われることがある。インターネットにおけるプロセス間通信では、多くの場合、サービスの授受の関係におけるクライアントとサーバと、プロセス間通信におけるクライアントとサーバが一致している。


    ソケット

    UNIX 上で動作するプログラムがTCP/IPの機能を使う場合、UNIXが提供するソケットというインタフェースを通じて利用することになる。ソケットは、TCP/IP を始めとして、XNS, OSI などさまざまな通信プロトコルを UNIX 上で使うために設計されたものである。TCP/IP だけを考えると、ソケットのインタフェースは、繁雑であり、使いにくくなっている。


    telnet コマンドの使い方

    telnet コマンドは、通常、次のように使われる。

        -----------------------------------------------------------------------
        % telnet host1
        -----------------------------------------------------------------------
    
    
    これは、次の省略形である。

        -----------------------------------------------------------------------
        % telnet host1 telnet
        -----------------------------------------------------------------------
    
    
    ここで、第2引数の "telnet" は、TCP/IP のポート番号を示す記号である。この記号は、/etc/services というファイルに次のように格納されている。
        -----------------------------------------------------------------------
        telnet		23/tcp
        -----------------------------------------------------------------------
    
    
    telnet コマンドは、/etc/services ファイルを検索し、与えられた記号からポート番号(この例では23)を得る。また、host1 の IP アドレスを、/etc/hosts や DNS から得る。telnet コマンドは、この IP アドレスとポート番号の2つを使って、TCP/IP の接続を行う。(注意:NISが動いている場合には、/etc/services や /etc/hosts の代わりに、NISのデータベース(マップ)が検索される。)

    telnet コマンドでは、次のように、ホストのIPアドレスとポート番号を数字で打ち込むこともできる。

        -----------------------------------------------------------------------
        % telnet 133.13.30.131 23
        -----------------------------------------------------------------------
    
    
    ここで、133.13.30.131 とは、32ビットのIPアドレスを8ビットづつに区切り、それぞれの8ビットの整数を10進数で表記したものである。よって、実際のIP アドレスは、次のようしてに計算できる。
    	((((133-256)+13)-256)+30)-256+131
    	== 2232229507
    
    
    よって、telnet コマンドに次のようIPアドレスを与えてもよい。
        -----------------------------------------------------------------------
        % telnet 2232229507
        -----------------------------------------------------------------------
    
    

    telnet コマンドによる fingerd へのアクセス

    finger コマンドは、利用者の情報を表示するコマンドである。finger コマンドは、ネットワーク経由で遠隔の利用者の情報を調べることもできる。そのような finger コマンドの利用例を以下に示す。

        -----------------------------------------------------------------------
    
    
        % finger kono@pw001↓
    
    
        [pw001] 
        Login name: yas       			In real life: Yasushi SHINJO
        Directory: /home/teacher/yas        	Shell: /usr/local/bin/tcsh
        On since Sep 08 19:48:13 on pts/0 from top.ie.u-ryukyu.
        9 minutes 29 seconds Idle Time
        No Plan.
        Login name: j94070    			In real life: Yasushi [j94] Shinjo
        Directory: /home/b5/y94/j94070      	Shell: /usr/local/bin/tcsh
        No Plan.
        Login name: j94077    			In real life: Yasushi SHINJO
        Directory: /home/b2/y94/j94077      	Shell: /usr/local/bin/tcsh
        No Plan.
        % 
        -----------------------------------------------------------------------
    
    
    ここで、下線を付けた部分が、利用者のタイプである。

    この例で、finger コマンドは、クライアントとして、ホスト pw001 のポート番号79(finger)のポートに対して、TCP/IP による仮想回線を開設する。そして、次のような文字列を送る。

        -----------------------------------------------------------------------
        kono↓
        -----------------------------------------------------------------------
    
    
    すると、pw001 上のサーバ(fingerd, finger daemon)は、上の例で示した情報をクライアントに返す。サーバは、データ転送が完了すると、TCP/IP の結合を切断する。finger コマンド(クライアント)は、受け取ったデータをを整形して利用者に対して表示する。


    ■課題 telnet を使った finger サーバへのアクセス

    telnet コマンドを使って、finger サーバにアクセスして、自分の情報を検索してみなさい。アクセス先のホストとしては、pw001-pw005, nirai, naha の中から選びなさい。その様子を、レポートに添付しなさい。script コマンドや emacs 中のshell を使うとよい。また、nw001-nw020 に対して行ってみなさい。その結果と、前の結果を比較しなさい。


    telnet コマンドによる WWW サーバへのアクセス

    WWW (the World-Wide Web)では、TCP/IP の上にさらにHTTP(HyperText Transfer Protocol)と呼ばれるプロトコルを構築し、データの転送を行っている。Netscape や Lynx などのブラウザは、WWW サーバとの間に TCP/IP による通信路を開設する。そして、クライアントは、必要なデータを得るための命令を送る。この命令の形式を定めたものが、HTTP である。HTTP 通信プロトコルを受け付けるサーバを、HTTP サーバと呼ぶ。

    表2に、HTTP で定義されている命令の例を示す。これらの命令に対して、サーバは、表3に定義されたような応答を行う。


    表2 HTTPで定義されている命令(methods)の例

    HTTPで定義されている命令(methods)の例
    命令 説明
    GET 情報を得る(ヘッダと本体の両方)
    HEAD 情報のヘッダのみを得る
    POST 新しく情報を作る


    表3 HTTPで定義されている状態コードの例

    HTTPで定義されている状態コードの例
    状態コード 説明
    200 OK(エラーなし)
    301 要求されたデータが移動した
    400 要求の形式にエラーがある。
    404 要求されたデータが見つからない。


    URL を持つデータへのアクセス

    たとえば、次のような URL を持つデータをアクセスすることを考える。

    	http://www.ie.u-ryukyu.ac.jp:80/Welcome.html
    
    
    Netscape などのクライアントは、まずホスト名 www.ie.u-ryukyu.ac.jp とポート番号80 を使ってサーバとの間に TCP/IP の通信路を開設する。そして、次のような文字列を送る。

        -----------------------------------------------------------------------
        GET /Welcome.html HTTP/1.0↓
        ↓
        -----------------------------------------------------------------------
    
    
    ここで、"GET" が命令の種類、"/Welcome.html" は、要求しているデータを表わす URL (ファイル名)、"HTTP/1.0" は、使っているプロトコルのバージョンである。次の空行は、命令の終りを意味するものであり、必要である。

    すると、サーバは、次のようなデータを送る。

        -----------------------------------------------------------------------
        HTTP/1.0 200 Document follows
        MIME-Version: 1.0
        Server: CERN/3.0pre6
        Date: Thursday, 07-Sep-95 13:14:34 GMT
        Content-Type: text/html
        Content-Length: 782
        Last-Modified: Monday, 07-Aug-95 03:55:13 GMT
        <HEAD>
        <TITLE>University of Ryukyus WWW Home Page.</TITLE>
        </HEAD>
        <BODY>
        <IMG SRC="title.gif">
        <H3>
        Welcome to the University of the Ryukyus.
        </H3>
        <HR>
        <H2>
        <UL>
        <LI> <A HREF="http://www.ie.u-ryukyu.ac.jp:20080/Welcome.html"> English</A>
        <LI> <A HREF="http://www.ie.u-ryukyu.ac.jp:20080/Welcome-j.html">
    	    Japanese (any,mixed)</A>
        <LI> <A HREF="http://www.ie.u-ryukyu.ac.jp:8002/Welcome-j.html">
    	    Japanese (EUC only)</A>
        <LI> <A HREF="http://www.ie.u-ryukyu.ac.jp:8003/Welcome-j.html">
    	    Japanese (JIS only)</A>
        <LI> <A HREF="http://www.ie.u-ryukyu.ac.jp:8004/Welcome-j.html">
    	    Japanese (Shift-JIS only)</A>
        <LI> <A HREF="kanji-menu.html"> Kanji code menu </A>
        </UL>
        </H2>
        <HR>
        <ADDRESS>
        <a >Web Master </a>of University of the Ryukyus.
        </ADDRESS>
        </BODY>
        -----------------------------------------------------------------------
    
    
    最初の行が、状態行(status line)と呼ばれる、要求が成功したか失敗したかわ表わしている。"200" とは、成功したという意味である。2行目から最初の空行までは、これから送るデータのメタ情報である。具体的には、データの型や、サーバのバージョン、データが更新された日付と時刻、バイト数などが記録されている。

    最初の空行の次が、データの本体である。この例では、HTMLで記述されたデータが返されている。サーバは、データ転送が完了すると、TCP/IP の結合を切断する。

    クライアントは、受け取ったデータをを整形して利用者に対して表示する。たとえば、インライン・イメージとして指定されたデータを続けてサーバに要求して展開したり、フォントを変えたりして表示する。


    ■課題 telnet を使った WWW サーバへのアクセス

    telnet コマンドを使って、WWW サーバにアクセスし、データを画面に表示させなさい。そのデータの URL とデータの先頭の20行程度を報告書に添付しなさい。


    telnet コマンドによる NNTP サーバへのアクセス

    NNTP(Network News Transfer Protocol) とは、ネットワーク・ニュースの記事の転送や、記事の読み書きを行うためのプロトコルである。mnews や GNUS などのネットワーク・ニュースを読み書きするソフトウェアは、NNTP サーバとの間に TCP/IP による結合を形成する。そして、クライアントは、記事を要求する文字列や、ニュース・グループの一覧を要求する文字列を送る。これに対してサーバは、要求された記事やニュース・グループの一覧を返す。表4に、クライアントからサーバへ送られるNNTPのコマンド、表5に、サーバからクライアントへ返され応答を示す。


    表4 NNTPのコマンド

    NNTPのコマンド
    GROUP ニュース・グループ名
    	    ニュース・グループを選択する。結果として、記事の数、記事の番号
    	    の上限と下限が返される。
    
    
    ARTICLE 記事番号
    	    その記事の内容を得る。ニュース・グループが選択されている状態の
    	    時に使える。
    
    
    ARTICLE <message-id>
    	    メッセージID &lt;message-id&gt; の記事の内容を得る。
    
    
    HELP
    	    ヘルプ・メッセージの表示
    
    
    QUIT
    	    終了
    
    


    表5 NNTPの応答

    NNTPの応答
    応答コード 説明
    100 ヘルプのテキストが続く。
    200 要求受け付け可能である(投稿可能)。
    201 要求受け付け可能である(投稿不可能)。
    205 通信路を切断する。
    211 ニュース・グループが選ばれた。
    		    記事の数、記事番号の上限、下限、ニュース・グループ名。
    
    
    400 サービスを中断する。
    411 そのようなニュース・グループがない。
    421 もうそのニュース・グループには次の記事がない。
    500 コマンドが認識できなった。
    501 コマンドの文法に誤りがあった。
    502 アクセスが制限されている。


    telnet コマンドを利用して、NNTP サーバに接続

    以下に、telnet コマンドを利用して、NNTP サーバに接続した様子を示す。
        -----------------------------------------------------------------------
    
    
          1: % telnet spn.ie.u-ryukyu.ac.jp nntp↓
    
    
          2: Trying 133.13.31.5 ...
          3: Connected to spn.ie.u-ryukyu.ac.jp.
          4: Escape character is '^]'.
          5: 200 spn NNTP[auth] server version 1.5.12.1 (1 Jan 1995) ready at Thu Sep  7 22:56:28 1995 (posting ok).
    
    
          6: help↓
    
    
          7: 100 This server accepts the following commands:
          8: ARTICLE     BODY         GROUP
          9: HEAD        LAST         LIST
         10: NEXT        POST         QUIT
         11: STAT        NEWGROUPS    HELP
         12: IHAVE       NEWNEWS      SLAVE
         13: DATE
         14: 
         15: Additionally, the following extensions are supported:
         16: 
         17: XHDR        Retrieve a single header line from a range of articles.
         18: LISTGROUP   Retrieve a list of valid article-numbers.
         19: XOVER       Return news overview data.
         20: XGTITLE     Same as LIST NEWSGROUPS (for backward compatibility).
         21: 
         22: Bugs to Stan Barber (Internet: nntp@academ.com)
         23: .
    
    
         24: quit↓
    
    
         25: 205 spn closing connection.  Goodbye.
         26: Connection closed by foreign host.
         27: %
        -----------------------------------------------------------------------
    
    
    ここで、下線で示した部分が、キーボードからのタイプである。この例では、ホスト spn.ie.u-ryukyu.ac.jp のポート番号119(nntp)のポートに、TCP/IPにより接続を試みている。2行目から4行目は、telnet コマンドによる定型的な表示である。ここで、通信路が確立されている。すると、サーバは、200 という応答を返している。これは、NNTP で定義されている応答であり、サーバが、要求を受け付け可能であり、かつ、要求としては投稿要求も受け付けることを意味している。200 以降の文字列は、コメントである。

    第6行では、"help" というコマンドをサーバに送っている。これに対して、サーバは、100 という応答に続けて、受け付け可能なコマンドなど、簡単な使い方を返している。23行目に「.」からなる行がある。これが、1つのコマンドに対する応答の終りを示している。

    次に、24行において、"quit" というコマンドをサーバに送っている。これにたいして、サーバは、205 という応答を返し、続いて TCP/IP の結合を切断している。26行目は、telnet コマンドが生成したメッセージである。


    ■課題 telnet を使ったニュースサーバへのアクセス

    telnet コマンドを使って、NNTP サーバ spn.ie.u-ryukyu.ac.jp にアクセスし、ネットワーク・ニュースの記事を画面に表示させなさい。その記事のニュース・グループ、ニュース・グループ内の番号、記事の先頭の20行程度を報告書に添付しなさい。このとき、表NNTP のコマンドを使うとよい。


    ■課題 NNTP コマンド

    その他の NNTP のコマンドを使ってみなさい。NNTP の定義は、RFC977 というドキュメントにある。RFC977は、/usr/open/doc/rfc/rfc977.txt にある。


    telnet コマンドによる SMTP サーバへのアクセス

    SMTP(Simple Mail Transfer Protocol)とは、電子メールの転送を行うためのプロトコルである。このプロトコルは、MTA (Mail Transfer Agent) と呼ばれるプログラムの間で電子メールを配送するために使われている。MTA の例としては、UNIX 上で動作するsendmailと呼ばれるプログラムがあげられる。MTA は、一方が SMTP サーバ、もう一方が SMTP クライアントとなり、電子メールを転送する。表6に、SMTPで定義されている要求、表7に応答を示す。


    表6 SMTPで定義されている手続き

        -----------------------------------------------------------------------
    
    
    SMTPで定義されている手続き
    MAIL FROM:<reverse-path>
    	    <td>新しいメールの転送を開始する。
    	    &lt;reverse-path&gt;は、エラーが起きた時の送り返し先のアドレス。
    
    
    RCPT TO:<forward-path>
    	    <td>メールの送り先として &lt;forward-path&gt; を指定する。
    
    
    DATA
    	    <td>メールのデータを送り始める。
    
    
    VRFY <login-name>
    	    <td>メールの受け手を確認する。
    
    
    EXPN <ml-name>
    	    <td>メーリング・リストの受取人を表示する。
    
    
    SEND FROM:<reverse-path>
    	    <td>メールを転送する。
    
    
    HELO <domain>
    	    <td>最初に接続した時に自分自身を相手に知らせる。
    
    
    QUIT
    	    <td>接続を切る。
    
    


    表7 SMTPの応答コード

    SMTPの応答コード
    コード 説明
    220 <domain> というドメインで要求受け付け可能である。
    250 そのアドレスへのメールは、受け付け可能である。
    251 そのアドレスは、ローカルには受取人がいない。


    telnet コマンドを利用して、SMTP サーバに接続

    以下に、telnet コマンドを利用して、SMTP サーバに接続した様子を示す。

        -----------------------------------------------------------------------
    
    
          1: % telnet pw001 smtp↓
    
    
          2: Trying 133.13.30.131 ...
          3: Connected to pw001.
          4: Escape character is '^]'.
          5: 220 pw001.st.ie.u-ryukyu.ac.jp Sendmail ALPHA-6.58/6.4J.6-UReecs-UG-21.0 ready at Fri, 8 Sep 1995 00:25:05 +0900
    
    
          6: vrfy root↓
    
    
          7: 250 <root@pw001.st.ie.u-ryukyu.ac.jp>
    
    
          8: quit↓
    
    
          9: 221 pw001.st.ie.u-ryukyu.ac.jp closing connection
         10: Connection closed by foreign host.
         11: %
        -----------------------------------------------------------------------
    
    
    ここで、下線で示した部分が、キーボードからのタイプである。この例では、ホスト pw001 のポート番号25(smtp)のポートに、TCP/IPにより接続を試みている。2行目から4行目は、telnet コマンドによる定型的な表示である。ここで、接続が確立されている。すると、サーバは、220 という応答を返している。これは、SMTP で定義されている応答であり、サーバが、コマンド要求を受け付け可能であり、

    第6行では、"vrfy" (verify) というコマンドをサーバに送っている。これに対して、サーバは、250 という応答に続けて、電子メールのアドレスを返している。

    次に、8行において、"quit" というコマンドをサーバに送っている。これにたいして、サーバは、221 という応答を返し、続いて TCP/IP の結合を切断している。10行目は、telnet コマンドが生成したメッセージである。


    ■課題 SMTP の Verify コマンド

    telnet コマンドを使って、pw001 と pw002 にアクセスし、自分のログイン名と存在しないログイン名について、VRFY で調べてみなさい。そして、ホストによる応答の違いを比較しなさい。


    ■課題 メール転送の詳細

    pw001 以外のホストで、mail コマンドに -v オプションを付けて、メールを送って、その様子を観察しなさい。すると、SMTP による電子メールの配送の様子が観察される。ただし、mail -v では、電子メールのデータ(DATAの部分)は、表示されない。ここでデータとは、To: 行や From: 行などのヘッダも含む。SMTP のレベルにおいては、データの部分の To: 行は、配送には使われない。その代わりに、SMTP で定義されている RCPT が使われる。

    SMTP コマンドを使って電子メールを送ってみなさい。SMTP の定義は、RFC821 というドキュメントにある。RFC821は、/usr/open/doc/rfc/rfc821.txt にある。mail -v の結果を参考にするとよい。


    ネットワーク・プログラミング


    クライアントの作成

    telnet でやったことを、C言語のプログラムで実現する。stream ライブラリを使う。


    streamライブラリ initport() と fdopen2()

    この実験では、Don Libes 氏により開発された stream ライブラリを用いる。これは、UNIX 上で TCP/IP を使うプログラムを書きやすくするためのライブラリである。このライブラリを使うことで、UNIX固有の繁雑なソケットを利用することなく、もともとの TCP/IP のモデル(3.1節の図3参照)に近い形でTCP/IPを利用するプログラムを書くことが可能になる。


    initport()

    steam ライブラリでは、initport() という関数が、中心的な役割を果たす。initport() は、TCP/IP のクライアント側(接続を行う方)でも、サーバ側(接続されるのを待つ方)でも使われる。この項では、クライアント側の使い方を示す。

    initport() は、クライアント側では、次の2種類の形式で使われる。

        -----------------------------------------------------------------------
        #include <sys/types.h>
        #include <sys/socket.h>
        #include <netinet/in.h>
        #include "inet.h"
        int s = initport(PORT_NUMBER(port), CLIENT,SOCK_STREAM,host);
        int s = initport(PORT_NAME(name),CLIENT,SOCK_STREAM,host);
        int port ;
        char -name ;
        char -host;
        -----------------------------------------------------------------------
    
    
    前者は、ポート番号を指定して、TCP/IP の接続を確立するものである(3.1節の図3参照)。重要な引数は、TCP/IP のポート番号 port と、ホスト名 host である。その他の引数 CLIENT と SOCK_STREAM は、定数であ。これらは、クライアント側で使う限り、必ず指定されなければならない。

    結果として、1つのファイル記述子 s が返される。これは、TCP/IP の双方向ストリームに対応している。よって返されたファイル記述子に対して次のように write() システム・コールでデータを書き込むと、そのデータは、接続先に送り届けられる。

    	write( s, buf, size );
    
    
    逆に、接続先から送られてきたデータは、read() システム・コールにより受け取ることができる。
    	read( s, buf, size );
    
    
    1つのファイル記述子にたいして、read(),write() もと利用することができる。これは、ファイルを読み書き(O_RDWR)モードで開いた時と似ている。

    もう1つの形式は、ポート番号の代わりにポート名(サービス名)を指定する方法である。たとえば、"telnet" というサービスの場合、次のようにして使うことができる。

    	s = initport(PORT_NAME("telnet"),CLIENT,SOCK_STREAM,host);
    
    
    エラーが生じると、initport() は、-1 を返す。

    なお、ネットワーク・プログラミングの教科書の多くは、ソケットを直接利用するように書かれている。この実験においても、stream ライブラリを使うことなく、直接ソケットを利用してプログラムを作成してもよい。


    fdopen2()

    fdopen2() は、この実験のために標準のライブラリ関数 fdopen() を拡張して作っ関数である。fdopen() は、open() システム・コールなどで開いたファイルについても、fprintf(), fgets(), fputs() などのバッファリング機能付の入出力関数を使いたい時に使われる。たとえば、次のようにして使われる。

        -----------------------------------------------------------------------
        FILE -f ;
    	    fd = open("file1",O_RDONLY);
    	    ....
    	    f = fdopen(fd, "r")
    	    fgets( buff,sizeof(buff),f );
        -----------------------------------------------------------------------
    
    
    これにより、open() で開いたファイルについても、バッファリング機能付の入出力関数が使えるようになる。fdopen() は、open() 以外にも、pipe() や dup() の結果にも使われる。

    fdopen2() は、TCP/IPの通信用ポートに対応しているのように、入出力可能なファイル記述子について、2つの入出用と出力用の FILE - を返す。

        -----------------------------------------------------------------------
        FILE -fin, -fout ;
    	    fd = initport(PORT_NAME("finger"),CLIENT,SOCK_STREAM,host);
    	    ...
    	    fdopen2(fd,&fin,&fout)
    	    fprintf( fout, "%s\n", who );
    	    fflush( fout );
    	    fgets( buff, sizeof(buff), fin );
        -----------------------------------------------------------------------
    
    
    fdopen2() の第1引数 fd には、initport() や connect(), accept() で作られた入出力可能(read()システム・コールもwrite() システム・コールも使える)ファイル記述子を指定する。結果として作られた FILE - は、第2、第3 引数として与えられた FILE - へのポインタで示された番地に格納される。fdopen2() は、エラーが起きた時には、0, 成功した時には、0 以外が返される。

    fdopen2() のソース・プログラムは、 /usr/open/classes/slab/info2/lib/fdopen2.c にある。


    コンパイルとリンク

    initport() を使うプログラムは、コンパイル時に "inet.h" というヘッダ・ファイルを読み込まなければならない。これは、/usr/open/classes/slab/info2/include/ に置かれている。よって、コンパイル時に、cc コマンドに次のようなオプションを与える必要がある。

    	% cc -I/usr/open/classes/slab/info2/include/ -c file.c
    
    
    また、initport() などの関数は、次のいずれかのファイルに格納されている。

        /usr/open/classes/slab/info2/lib/libinfo2-sun4.a	Sun用
        /usr/open/classes/slab/info2/lib/libinfo2-rs6000.a	IBM用
        (BSD/OS用は/usr/open/classes/slab/info2の下にソースから自分で作成すること)
    
    
    これを利用するためには、リンク時に次のように指定する。

        % cc -o commmand file.o /usr/open/classes/slab/info2/lib/libinfo2-sun4.a
    
    
    または、次のように -L, -l オプションを用いて指定する。

        % cc -o commmand file.o -L/usr/open/classes/slab/info2/lib/ -linfo2-sun4
    
    
    以下の実験で使うコマンドについては、/usr/open/classes/slab/info2/2-tcp/Makefile に、コンパイルとリンクのための設定が格納されている。これを利用するとよい。


    finger クライアントのコンパイルと実行

    遠隔のfingerサーバに対して問い合わせを行うコマンド、rfinger (remote finter)のソース・プログラムを、以下のファイルに示す。

        -----------------------------------------------------------------------
        /usr/open/classes/slab/info2/2-tcp/rfinger.c:
        -----------------------------------------------------------------------
          1: 
          2: /-
          3:         rfinger.c -- Remote Finger / 遠隔専用 finger コマンド。
          4:         $Header: /home/h1/yas/slab-info2-inet/2-tcp/RCS/rfinger.c,v 1.3 1995/09/11 13:42:45 yas Exp $
          5:         Start: 1995/09/08 21:04:33
          6: -/
          7: #include <stdio.h>
          8: #include <sys/types.h>
          9: #include <sys/socket.h>
         10: #include <netinet/in.h>
         11: #include "inet.h"
         12: 
         13: main( argc,argv )
         14:     int argc ;
         15:     char -argv[] ;
         16: {
         17: /- 引数は、コマンド名を入れて3個。 -/
         18:         if( argc != 3 )
         19:         {
         20:             fprintf( stdout,"Usage: %s host \"who\"\n",argv[0] );
         21:             exit( -1 );
         22:         }
         23:         rfinter( argv[1],argv[2] );
         24: }
         25: 
         26: #define BUFFSIZE        1024
         27: 
         28: rfinter( host,who )
         29:     char -host ;        /- ホスト名 -/
         30:     char -who ;         /- 調べたい人のキー -/
         31: {
         32:     int fd ;
         33:     char buff[BUFFSIZE] ;
         34:     int count ;
         35: 
         36: /- buff で確保されているメモリよりも長い who については、エラーにする。-/
         37:         if( strlen(who) >= BUFFSIZE-2 )
         38:         {
         39:             fprintf(stderr,"search key too long: \n%s\n",who );
         40:             exit( -1 );
         41:         }
         42: /-
         43: initport() 関数により、TCP/IPのクライアント側における通信用ポートを設
         44: 定する。このとき、ポート名と接続先のホストを指定する。この関数により、
         45: TCI/IPの通信路が開設され、read(), write() により通信を行うことが可能に
         46: なる。エラーが起きたときには、-1 が返される。
         47: -/
         48:         fd = initport(PORT_NAME("finger"),CLIENT,SOCK_STREAM,host);
         49:         if( fd < 0 )
         50:         {
         51:             fprintf(stderr,"initport(,,host:%s) = %d\n",
         52:                     host, fd);
         53:             exit( -1 );
         54:         }
         55: 
         56: /-
         57: 通信内容を、バッファ buff に作る。このとき、sprintf() を使うと便利であ
         58: る。これは、printf() と同じ形式の引数をとることができる。結果は、画面
         59: に表示されるのではなく、buff (string)に保存される。最後の '\n' は、必
         60: 要である。
         61: -/
         62:         sprintf( buff,"%s\n", who );
         63:         count = strlen( buff );
         64: 
         65: /-
         66: 通信用ポートに対応しているファイル記述子を引数としてwrite() システム・
         67: コールを発行することで、TCP/IPによるプロセス間通信が行われる。これによ
         68: り、buff 番地から count バイトのデータが、通信相手に送られる。
         69: -/
         70:         if( write(fd,buff,count) != count )
         71:         {
         72:             perror("write() to socket");
         73:             exit( -1 );
         74:         }
         75: /-
         76: 通信用ポートに対応しているファイル記述子を引数として read() システム・
         77: コールを発行することにより、TCP/IPによるプロセス間通信が行われる。これ
         78: により、通信相手から送られてきたデータが、buff 番地から始まる BUFFSIZE 
         79: バイトの領域へ書き込まれる。read() システム・コールのリターン・バリュー
         80: は、実際に送られてきたデータのバイト数である。
         81: -/
         82:         while( (count=read(fd,buff,BUFFSIZE)) > 0 )
         83:         {
         84:             if( write(1,buff,count) == -1 )
         85:             {
         86:                  perror("stdout");
         87:                  exit( 1 );
         88:             }
         89:         }
         90:         close( fd );    /- TCP/IP connection を閉じる。-/
         91:         close( 1 );     /- 標準出力のファイルを閉じる。-/
         92: }
         93: /- --------------------- rfinger.c --------------------- -/
         94: static char rcsid[] =
         95: "$Header: /home/h1/yas/slab-info2-inet/2-tcp/RCS/rfinger.c,v 1.3 1995/09/11 13:42:45 yas Exp $" ;
        -----------------------------------------------------------------------
    
    

    ■課題 rfinger コマンド

    rfinger.c をコンパイルして、実行してみなさい。

        -----------------------------------------------------------------------
        % cp /usr/open/classes/slab/info2/2-tcp/{rfinger.c,Makefile} .
        % make rfinger
        % ./rfinger pw001 $USER
        -----------------------------------------------------------------------
    
    
    報告書には、自分の情報を rfinger で調べた結果を示しなさい。ログイン名で引いた時と、finger name として設定している文字列で引いたときの結果を示しなさい。もし、finger name をまだ設定していないならば、chfn コマンドで設定しなさい。このとき、設定の前後で、表示や検索の様子がどう変わるかを調べて報告しなさい。


    Javaによるfinger

    Java で finger を実現するためには、Class Socket を使う。以下の プログラムで、
    	      theSocket = new Socket(hostname, port);
    
    
    がSocketのインスタンス(実体)を取って来ている部分である。これから、
    	      ps = new PrintStream(theSocket.getOutputStream());
    
    
    によってストリームを取り出す。後は、このストリームに対して、
    	      while((s = theFingerStream.readLine()) != null) ...
    
    
    を繰り返すことによりデータをソケットから読み出すことができる。

    このプログラムをコンパイルするには、

    	javac Finger.java
    
    
    とする。すると、
    	Finger.class
    
    
    が出来るので、
    	java Finger pw001
    
    
    により実行することができる。

    実際にプログラムをコンパイルして実行できることを確認せよ。

    java Program は、applet と呼ばれるHTMLから呼ばれるプログラムとしても動作させることができる。applet の作成方法を自分で調べ、Finger.java を書き直し、FingerApplet.java を作り、適当なHTMLページより Finger を起動してみよ。これらのjavaプログラムは、HTMLページを表示するブラウザ(NetscapeやInternet Explore)で実行される。様々なブラウザで実行してみて、その結果に関して考察せよ。


    WWW クライアントの作成


    ■課題 wcat コマンド

    WWWサーバからデータを得るプログラムを作りなさい。そのプログラムの名前を、wcat とする。wcat のプログラムの骨組みを/usr/open/classes/slab/info2/2-tcp/wcat.c に示す。これをコピーして、使いなさい。

        -----------------------------------------------------------------------
        % cp /usr/open/classes/slab/info2/2-tcp/wcat.c .
        -----------------------------------------------------------------------
    
    
    wcat コマンドは、次のように3つの引数を与えて利用するものとする。

        -----------------------------------------------------------------------
        % ./wcat host port file
        -----------------------------------------------------------------------
    
    
    ここで、host は、ホスト名、port は、TCP/IP のポート番号、file は、得るべきファイル名である。これは、URL の文法で記述すると、次のようになる。

    	http://host:port/file
    
    
    なお、wcat では、ポート番号の引数を省略しないものとする(省略可能なように工夫してもよい)。HTTP プロトコルで用いられる標準のポート番号は、80である。

    wcat のプログラムを作る時に、rfinger.c を参考にするとよい。initport() では、ポート名(サービス名)ではなく、ポート番号を用いる形式を利用するとよい。

        -----------------------------------------------------------------------
    	    fd = initport(PORT_NUMBER(port),CLIENT,SOCK_STREAM,host);
        -----------------------------------------------------------------------
    
    
    HTTP では、ヘッダ部分の行末に、キャリッジ・リターン(carrige return, CR)とライン・フィード(Line feed, LF)の両方が必要であると定められている。UNIX では、通常ライン・フィード(ニュー・ライン、New Line, NL と呼ばれることもある)だけが行末の記号として使われる。よって、画面に文字列を表示し、改行したい場合は、次のようなプログラムが使われる。

    	printf("Hello,world\n");
    
    
    これは、HTTP では、次のようにしなければならない。

    	printf("Hello,world\r\n");
    
    
    ここで、'\r' がキャリッジ・リターン、'\n' がライン・フィードである。いずれも、ソース・プログラム上では2文字に見えるが、Cコンパイラにより、1文字に変換される。それぞれ、アスキーでは、13(0x0d), 10(0x0a) である。


    ■課題 javaのwcat

    wcat.c と同様のプログラムである、 FetchURL.java をコンパイルして実行せよ。また、プログラムを変更して、applet としても実行できるようにせよ。


    NNTP クライアントの作成


    ■課題 nncat コマンド

    NNTPサーバから記事を1つ得るプログラムを作りなさい。そのプログラムの名前を、nncat とする。nncat のプログラムの骨組みを /usr/open/classes/slab/info2/2-tcp/nncat.c に示す。これをコピーして、使いなさい。

        -----------------------------------------------------------------------
        % cp /usr/open/classes/slab/info2/2-tcp/nncat.c .
        -----------------------------------------------------------------------
    
    
    nncat コマンドは、次のように3つの引数を与えて利用するものとする。

        -----------------------------------------------------------------------
        % ./nncat host newsgroup number
        -----------------------------------------------------------------------
    
    
    ここで、host は、NNTPサーバが動いているホストの名前newsgroup は、ニュース・グループ、number は記事番号である。HTTP プロトコルで用いられる標準のポート番号は、119(nntp) である。initport() では、PORT_NAME("nntp") の形式を利用するとよい。

    報告書には、作成した nncat コマンドの動作例を示しなさい。NNTPサーバが動いているホストとしては、spn (spn.ie.u-ryukyu.ac.jp) を指定しなさい。

    NNTP でやり取りされるデータの行末には、キャリッジ・リターン(carrige return, CR)とライン・フィード(Line feed, LF)の両方が必要であると定められている。UNIX では、通常ライン・フィード(ニュー・ライン、New Line, NL と呼ばれることもある)だけが行末の記号として使われる。よって、画面に文字列を表示し、改行したい場合は、次のようなプログラムが使われる。

    	printf("Hello,world\n");
    
    
    これは、NNTP では、次のようにしなければならない。

    	printf("Hello,world\r\n");
    
    
    ここで、'\r' がキャリッジ・リターン、'\n' がライン・フィードである。いずれも、ソース・プログラム上では2文字に見えるが、Cコンパイラにより、1文字に変換される。それぞれ、アスキーでは、13(0x0d), 10(0x0a) である。

    余裕があれば、画面に表示する前に、UNIX に合わせて行末のキャリッジ・リターンのコードを削除するようにしなさい。


    ■課題 javaによるNNTPクライアント

    wcat.c と同様のプログラムをJava言語を使って作りなさいGenericClient.javaとする。

    ログラムの骨組みを/usr/open/classes/slab/info2/6-tcp/{gclient.c, GenericClient.java}、房ィす。これを修正して、javaによるNNTPクライアント作成せよ。また、プログラムを変更して、applet としても実行できるようにせよ。

    このプログラムは、琉大内部のニュースグループを外部から読むことを可能にすると思われる。実際にそのようなことが可能かどうか確かめよ。また、これに関するjavaの対処法に関して考察せよ。


    SMTP クライアントの作成


    ■課題 mverify コマンド

    SMTPサーバに対して、VRFY 手続きを送り、受取人のアドレスを確認するプログラムを作りなさい。そのプログラムの名前を、mverify とする。mverify のプログラムの骨組みを /usr/open/classes/slab/info2/2-tcp/mverify.c に示す。これをコピーして、使いなさい。

        -----------------------------------------------------------------------
        % cp /usr/open/classes/slab/info2/2-tcp/mverify.c .
        -----------------------------------------------------------------------
    
    
    mverify コマンドは、次のように2つの引数を与えて利用するものとする。

        -----------------------------------------------------------------------
        % ./mverify host who
        -----------------------------------------------------------------------
    
    
    ここで、host は、SMTPサーバ(sendmailデーモン)が動いているホストの名前、who は確認するアドレスである。SMTPプロトコルで用いられる標準のポート番号は、25(smtp) である。initport() では、PORT_NAME("smtp") の形式を利用するとよい。

    報告書には、作成した mverify コマンドの動作例を示しなさい。NNTPサーバが動いているホストとしては、pw001 を指定しなさい。自分のアドレスを確認してみなさい。

        -----------------------------------------------------------------------
        % ./mverify pw001 $USER
        -----------------------------------------------------------------------
    
    

    ■課題 javaによるSMTPクラアント

    wcat.c と同様のプログラムである、 FetchURL.java を修正して、javaによるSMTPクライアント作成せよ。また、プログラムを変更して、applet としても実行できるようにせよ。

    このプログラムは、個人のメールを外部から出す可能にすると思われる。実際にそのようなことが可能かどうか確かめよ。また、これに関するjavaの対処法に関して考察せよ。


    サーバのプログラミング


    WWWサーバの改良

    これまでHTTP, NNTP, または、SMTP のクライアントのプログラムを作成した。この節では、簡単な HTTP サーバのプログラムを解読し、機能拡張と改良を行う。


    initport()(サーバ側)

    これまでstreamライブラリのinitport()関数のうち、クライアント側の使い方を示した。ここでは、サーバ側の使い方を示す。サーバ側では、initport() は、次のような形式で使われる。

        -----------------------------------------------------------------------
        #include <sys/types.h>
        #include <sys/time.h>
        #include <sys/socket.h>
        #include <netinet/in.h>
        #include "inet.h"
        s = initport( PORT_NUMBER(port),SERVER,SOCK_STREAM );
        int port ;
        -----------------------------------------------------------------------
    
    
    これにより、サーバ側に受け付け用のTCP/IPのポートが作られる(3.1節の図3参照)。そのポート番号は、引数portにより指定される。initport()に対するその他の引数 (SERVER, SOCK_STREAM) は、定数である。これらは、サーバ側で使う限り、必ず指定されなければならない。

    結果として、1つのファイル記述子 s が返される。これは、TCP/IP のサーバ側における受け付け用のポートと対応している。受け付け用のポートとは、ポート番号を保持し、クライアントからの接続要求を受け付けるためのものである。このポートを通じて、データを送受することはできない。すなわち、このファイル・記述子に対して、read() システム・コールや write() システム・コールにより、入出力を行うことができない。実際に入出力を行うためのポートは、次の select_server_stream() によって得られる。

        -----------------------------------------------------------------------
        int client = select_server_stream(s,&fds);
        int s ;
        fd_set fds ;
        -----------------------------------------------------------------------
    
    
    select_server_stream() の引数 s は、initport() により返されたファイル記述子である。fdsは、select_server_stream() 内部で複数の接続扱うための作業用変数である。fd_set とは、ファイル記述子の集合(配列)を保持するためのビット配列である。最初に FD_ZERO() により初期化されなければならない。それ以後は、select_server_stream() により管理される。

    select_server_stream() は、クライアントからの接続要求が来るか、クライアントからのデータが到着するまで待つ。クライアントからの接続要求が来ると、それを受け付ける。その結果として、クライアントとの間で双方向のストリームにより通信可能なファイル記述子を返す。

    select_server_stream() は、1つのサーバ・プロセスで複数のクライアントを同時に扱う事を可能にする。サーバ・プロセスは、複数のクライアントの間に、複数のTCP/IPの接続を同時に保持することができる。そして、それらの接続の中で、データが届いたものを選び、返す機能がある。

    select_server_stream() により返されたファイル記述子は、クライアントからのデータが到着している、入出力可能なTCP/IPの通信ポートに対応している。サーバの処理は、この通信ポートから要求を読みだし、それに応じた処理わ行い、結果をこの通信ポートに書き出すことである。

    なお、ネットワーク・プログラミングの教科書の多くは、ソケットを直接利用するように書かれている。この実験においても、stream ライブラリを使うことなく、直接ソケットを利用してプログラムを作成してもよい。


    httpd-simple

    ここでは、簡単な HTTP サーバ(WWWのサーバ)を例に、TCP/IPのサーバのプログラムの概要を示す。サーバのソース・プログラムを、次のファイルに示す。

        -----------------------------------------------------------------------
    
    
        /usr/open/classes/slab/info2/2-tcp/http-simple.c:
    
    
        -----------------------------------------------------------------------
    
    

    ■課題 http-simple のコンパイルと実行

    http-simple.c をコンパイルして、実行してみなさい。

        -----------------------------------------------------------------------
        % cp /usr/open/classes/slab/info2/2-tcp/{http-simple.c,Makefile} .
        % make http-simple
        %
        -----------------------------------------------------------------------
    
    
    ウィンドウを2つ用意しなさい。1つのウィンドウで、次のように http-simple を実行しなさい。
        -----------------------------------------------------------------------
        % ./http-simple ~/public_html/
        http://pw001:13520/
        -----------------------------------------------------------------------
    
    
    ~/public_html/ は、このサーバが提供するデータを含んでいるディレクトリを指定する。あらかじめ作成しておくこと。ここで、 http-simple が表示した URL を記憶しなさい。実際には、ホスト名やポート番号が異なるはずである。

    ★注意: http-simple は、サーバ・プロセスなので、自動的に終了しない。実験が終了したら、必ず^Cやkillコマンドでこのプロセスを終了さなさい。もし、実験に使用したプロセスが残されていた場合、この減点の対象とする。

    もう1つのウィンドウで、次のように Netscape を起動しなさい。(lynx でもよい。)

        -----------------------------------------------------------------------
        % Netscape http://pw001:13520/Welcome.html
        -----------------------------------------------------------------------
    
    
    ここで Netscape の引数は、先ほど ./http-simple が起動時に表示した URL に引き続き、何か適当な文字列(これ例では、"Welcome.html")を続けたものである。

    あらかじめ Netscape を起動していた場合、Open URL を使って、URL http://pw001:13520/Welcome.html の資源を得なさい。すると、次のような画面が現われるはずである。

        -----------------------------------------------------------------------
        request[/Welcome.html]
        -----------------------------------------------------------------------
    
    
    これは、http-simple.c:perform() の中で、次の fprintf() の呼び出しに対応している。
        -----------------------------------------------------------------------
    	     fprintf( fout,"request[%s]\n",filename );
        -----------------------------------------------------------------------
    
    

    ■課題 ファイルの内容を返す http-simple

    http-simple.c は、要求されたURLを、そのままクライアントに返す機能しかない。これを改良して、ファイルを開き、その内容を返すようにしなさい。与えられたファイル名を使ってファイルを、fopen() で開き、fread() で読み込み、fwrite() でクライアントに返すとよい。このとき、ファイル名の拡張子が".html" の場合には、"Content-Type: text/html\r\n" を返しなさい。また、".gif" の場合には、"Content-Type: image/gif\r\n" を返しなさい。実際の HTTP サーバにならい、ファイルの大きさや日付を返すようにしてもよい。

    ファイルが見つからない時には、HTTP のエラーである 404 を返しなさい。

    HTTP で、ヘッダと本体を分ける空行を忘れないようにしなさい。


    ■課題 WWW サーバからのプログラムの実行

    課題13では、ファイルを開き、その内容を返していた。ファイルの内容ではなく、プログラムを実行してその結果を返すようなプログラムを作りなさい。実行するプログラムとしては、次のようなものが考えられる。

    (1) who (2) finger (3) ps

    プログラムを実行し、その結果を取り込むためには、popen() ライブラリ関数を使うとよい。

    内容の型としては、"Content-Type: text/plain\r\n" を付けるとよい。余裕があれば、"Content-Type: text/html\r\n" にして、その他の文字列とともに、プログラムの出力に <CODE>,</CODE>や <PRE>,</PRE> を付けて返すようにしなさい。


    ■課題 アクセス制限

    http-simple.c は、どのホストからの要求も受け付けている。特定のホストからの要求しか受け付けないように、改良しなさい。現在のプログラムは、クライアントのIPアドレスをprint_client() により表示している。この課題では、IPアドレスを表示するだけでなく、その内容を調べて、アクセスを許すか許さないかを決めなさい。たとえば、次のような方法が考えられる。

    (1) 特定のホストだけからのアクセスを許す。(2) bw0? というホストからのアクセスを許す。(3) 133.13.30.- というパタンのIPアドレスを持つホストからのアクセスのみを許す。(4) 133.13.-.- というパタンのIPアドレスを持つホストからのアクセスのみを許す。(5) -.u-ryukyu.ac.jp というホスト名を持つホストからのアクセスのみを許す。

    IP アドレスからホスト名を得るには、ライブラリ関数gethostbyaddr()を使うとよい。


    ■課題 javaによるサーバ

    javaによるサーバは、サーブレットなどと呼ばれる。

    HttpMirror.javaをコンパイルして、実行してみなさい。HttpMirror を次のように実行しなさい。

    	----------------------------------------------------------------------
    	% java HttpMirror 13520
    	----------------------------------------------------------------------
    
    
    このサーバはリクエストされたファイルを返す代わりに、そのリクエストをクライアントにそのままミラーで応答する。

    HttpMirror.java を修正して、http-simple と同様の拡張をおこなえ。

    	HTMLファイルの中身を返す
    	プログラムを実行した結果を返す
    	アクセス制限を実現する
    
    
    ★注意: http-simple、HttpMirrorは、サーバ・プロセスなので、自動的に終了しない。実験が終了したら、必ず^Cやkillコマンドでこのプロセスを終了さなさい。もし、実験に使用したプロセスが残されていた場合、この減点の対象とする。


    ■課題 javaによるデータグラム・ブロードキャスト

    ブロードキャストは、特にイサーネット上で実装されているブロードキャスト・パケットを用いて、同一ネットワーク・セグメント上の不特定多数のホストに同時にパケットを送ることである。

    JavaのMulticastSocketを使ったロードキャストを行なうプログラムを作成しなさい。MulticastSniffer.java、MulticastSender.java の骨組みは

    		/usr/open/classes/slab/info2/6-tcp/java
    
    
    の下にある。

    まず、パケットがSenderから複数のSinifferに同時に送られることを確認するだけのプログラムを作成する。

    すべてのメッセージが確実に到着するかしないかを確認せよ。ブロードキャスト・パケットに関する信頼性に関して考察せよ。

    放送する複数のホストを前もって決めておき、そのホスト全部にパケットを同時に送ることをマルチキャストという。信頼性のあるマルチキャスト方式に関して調べ、java での実装に関して報告せよ。


    ■課題 javaによる移動エージェント

    次に移動エージェントプログラミングを実現するAgletsパッケージが /usr/open/java/Aglets1.0.3/ にある。

    これに含まれるサンプルプログラムを使用してみなさい。 /usr/open/java/Aglets1.0.3/samples にある。

    Agletsに関する ドキュメント

    これらのサンプルを参考にエージェントの巡回、並列派遣、クローンニグとJavaのソケットを使用した、エージェントシステムを作成し、そのシステムを説明しなさい。


    報告書

    この章の全ての実験課題について、1通の報告書を提出しなさい。それぞれの実験について、作成したプログラムとその説明(4.1を除く)、および、その実行結果を付けなさい。この実験では、プログラムの動きを説明するフローチャートを付加する必要はない。開発環境と実行環境(計算機、オペレーティング・システムのバージョン、コンパイラ)を載せなさい。

    実験について、作成したプログラムとその説明(4.1を除く)、および、その実行結果を付けなさい。この実験では、プログラムの動きを説明するフローチャートを付加する必要はない。開発環境と実行環境(計算機、オペレーティング・システムのバージョン、コンパイラ)を載せなさい。

    その他、この実験を通じて得たことを報告しなさい。

    この実験に要した時間(単位は時間)を書きなさい。

    この実験指導書の改善点を書きなさい。


    一般的な注意:

    報告書を、日本語で記述しなさい。プログラム、表、図、数式の羅列は、日本語ではない。それらは、必ず本文中から参照しなさい。たとえば、「○○を図1に示す」、あるいは、「○○を以下に示す。<以下、参照しているものを続ける>」といった形式を使うとよい。数学における証明のように論理的に記述する。主語と述語を明確に書くとよい。


    参考資料

    /usr/open/doc/inet/protocols/http-1.0.txt
    	HTTP 1.0 の仕様。テキスト形式。
    
    
    http://www.w3.org/hypertext/WWW/Protocols/Overview.html
    	HTTPプロトコルについて。
    
    
    /usr/open/doc/rfc/rfc821.txt
    	SMTP (Simple Mail Transfer Protocol)
    
    
    /usr/open/doc/rfc/rfc977.txt
    	NNTP (Network News Transfer Protocol)
    
    

    付録


    streamライブラリ(sized_ioライブラリ)の README ファイル

    README ファイル

    streadm ライブラリ(sized_io ライブラリ)の入手先。

        archieで sized_io を検索するとよい。
    
    
    または、 これを使おう。


Shinji KONO / Tue Jan 24 12:14:24 2012