システム・プログラム 電子・情報工学系 新城 靖 <yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~yas/coins/syspro-2004/2004-04-26
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~yas/
http://www.is.tsukuba.ac.jp/~yas/index-j.html
インターネット上のアプリケーションの多くは、TCP/IPという仕組みを用いて 通信を行っている。
TCP/IPは、信頼性のある(reliable)双方向のストリーム転送サービス (stream transport service)を提供する通信プロトコルである(図1)。ス トリームは、次のような性質がある転送サービスである。
なお、C言語のライブラリ関数である fopen(), fgets(), fputs() なども、 ストリームと呼ばれることがある。これは、もともとランダム・アクセス可能 で、メモリ中の配列と同じようにアクセスするすることもできるファイルを、 まるでプロセス間通信のストリームと同じように扱うことができることにも 関係している。
TCP/IPによる通信では、図2に示すように、4つのプロトコル(規約、約束事) の層が使われる。TCP/IP自身は、TCP層と IP層という2つのプロトコルに分解 される。このようにさまざまなプロトコルが決められ、全体として層をなして いる。この様子を、プロトコル・スタックと呼ぶ。
TCP は、IP という通信プロトコルを利用して実現されている。IPは、(信頼 性がない)データグラム(datagram)転送サービスを提供する通信プロトコル である。データグラムとは、次のような性質を持つ。
IPのデータグラムが配達されるときに使われる番号が、 IPアドレス である。IPアドレスとしては、現在、32ビットの整数が 使われてる。IPアドレスを表現する時には、普通、8ビットずつ区切って、 次のように4つ部分に分けて書かれる。たとえば、
12.34.56.78は
(((12 * 256)+34)*256+56)*256+78という数を表する。
TCPで通信をする時に、通信相手を識別するにはIPアドレスと ポート番号(port number) が必要になる。ポート番号は、同じホストの中で提供されている様々なサービスを 区別するために使われる。 ポート番号は、16ビットの整数であり、よく使われる アプリケーション では、あらかじめどの番号を使うかが決められている。これを well-knownポート番号(well-known port number) という。Unix では、1024 番より小さいポート番号を使うには、 特権ユーザ(スーパー・ユーザ、管理者)の権限が必 要であり、このようなポート番号は、 特権ポート番号(privileged port number) と呼ばれる。
TCP層の上には、応用層が定義されている。この層では、ftp, ssh, Netscape, sylpheed,sendmail などの、TCP/IP を利用するプログラムの間の会話の方法が定 義される。TCP/IPを使った通信は、まるでプロセス同士が電話で会話するよう に進められる。普通の電話では、日本語を話す人と英語を話す人は、電話で情 報交換を行うことができない。同様に、同じTCP/IPを使っていても、会話の方 法が違うと、まったく情報交換を行うことができない。ゆえに、TCP/IPの上に さらに、情報交換のためにさまざまなプロトコルが取り決められている。
TCP/IPの上に構築されているプロトコルの例を、表1に示す。
/etc/services に、他のポート番号が掲載されている。
IPのデータグラムを転送するためには、さまざまな物理的な媒体が使われる。 現在LANでは、イーサネットがよく使われいる。イーサネットは、同軸 ケーブル、より対線(Twisted Pair Cable)、または、光ファイバ を使ってデータを転送する。モデムなどを使ったシリアル回線では、PPP(Point to Point Protocol)というプロトコルの上に、IPデータグラムが流される。
データグラムは、ネットワーク通信では、最も基本的な転送サービスである。 IP上に構築された UDP(User Datagram Protocol)も、IPとほとんど同じ機能 を提供する。また、イーサネットやFDDIが提供する転送サービスも、データグ ラムである。
ネットワークに接続されている計算機の中で、ネットワークに1ヵ所の出入り 口(インタフェース)を持っているものは、ホストと呼ばれる。2ヵ所以上の 出入り口を持っている計算機は、ルータと呼ばれる。ルータは、ネットワーク とネットワークを接続するための計算機である。ルータは、入ってきたIPのパ ケットのIPアドレスを見て、どのネットワークに送ればよいかを判断する。
図1で、左端と右端にあり、4層全てそろっている部分がホストである。 TCP/IPの通信は、ホストとホストの間で行われる。中央の、2層しかない部分 は、ルータである。ルータの仕事は、IP層において行われる。
TCP/IP では、プロセスとプロセスが、電話で会話をするように通信が行われ る。普通の電話で人間同士が話をするには、まず電話番号を指定して、話相手 に電話をとってもらわなければならない。TCP/IP においても同様である。 TCP/IPでは、電話を掛ける方をクライアント・プロセス、電話を待つ方をサー バ・プロセスと言いう。
TCP/IPにおいて、プロセス間に形成されたストリーム通信路のことを、計算機 間に張られた物理的な回線に似ていることから、仮想的回線(virtual circuit)とも言う。TCP/IP では、回線を接続する段階では、クライアント・ プロセスとサーバ・プロセスは非対称である。一度仮想回線が接続された後は、 両方のプロセスは、TCP/IPのレベルでは、まったく対称的になる。
TCP/IPにおいてプロセス間に仮想回線を開設するには、IPアドレスとポート番 号が必要である。ポート番号は、同じIPアドレスを持つホスト上で動いている プロセスを区別するために使われる。
以下に、通信路が開設される手順を示す。
こうして一度通信路が開設されると、クライアントとサーバは、どちらからで もデータを送り始めることができる。
図3(a) TCP/IP通信路の開設(1)
図3(b) TCP/IP通信路の開設(2)
図3(c) TCP/IP通信路の開設(3)
TCP/IPにおける通信路開設において、クライアントは、サーバ側の接続要求受 付用ポートのポート番号を、事前に知っている必要がある。表1に、いくつか の応用層のプロトコルについて、公に利用目的が決められているポート番号を 示す。
クライアント側の通信用ポートのポート番号は、通常は、オペレーティング・ システムにより自動的に割り当てられる。サーバ側の通信用ポートのポート番 号も、同様である。
インターネットで通信をプログラムを利用する時、 「クライアント」と「サーバ」という分けて考える。
例:
コンピュータが1台しかない場合、プログラムは1つでよい。通信をする場合 には、プログラム(プロセス、コンピュータ)が2つになる。そのうちの1つ のプログラム(プロセス、コンピュータ)を、「クライアント」、もう1つを 「サーバ」という。
元々の意味
図? サービスの授受によるクライアントとサーバの定義
サービスを提供する方は、1つのプログラム(コンピュータ)で複数の利用者 の面倒をみる。その結果、1台のサーバに複数のクライアントがつながる。
図? 複数のクライアントによるサーバの共有
TCP/IP では、通信するプログラムとプログラムの間は、電話で会話をするよ うに通信が行われる。両方同時に話をすることは、(可能ではあるが)あまり 行われない。次のようなことを、繰り返すことになる。
図? 通信のパタンからみたクライアントとサーバの定義
TCP/IP の通信では、通信を始める前に、まず、通信路を作る作る必要がある。 これは、電話で話をする前に、まず、電話をかける操作を行うことと似ている。以上のように、クライアントとサーバは、いろいろな意味で使われる。これら の意味は、多くの場合、一致しているが、一致していないこともある。
通信を開始するパタンで、コンピュータ、プログラム、人間は、次の2つに分 類される。
図? 能動的なクライアントと受動的なサーバ
例:WWWサーバは、WWWクライアントから何か要求が来ない限り、ずっと 黙っている。
コンピュータを使う時には、人間が能動的になり、コンピュータが受動的にな る。
テレビを見ている時には、人間が受動的になり、テレビが能動的になる。
講義形式の授業では、サービスの授受では、教官がサーバで、学生がクライア ントになる。通信の開始の方法では、教官が能動的になり、学生が受動的にな る。
大学以上では、学生は、能動的になることが求められている。
UNIX オペレーティング・システム上で動作するプログラムがTCP/IPの機能を 使う場合、UNIXオペレーティング・システムが提供するソケットというインタ フェースを通じて利用することになる。ソケットは、TCP/IP をはじめとして、 XNS, OSI などさまざまな通信プロトコルを UNIX オペレーティング・システ ム上で使うために設計されたものである。TCP/IP だけを考えると、ソケット のインタフェースは、繁雑であり、使いにくくなっている。
UNIXでは、ソケットをドメイン(Protocol Family)と型で区別する。 下の表は、socket() システム・コールに与えるドメインと型である。
---------------------------------------------------------------------- ドメイン 型 option プロトコル ---------------------------------------------------------------------- PF_INET SOCK_STREAM 0 TCP PF_INET SOCK_DGRAM 0 UDP PF_INET SOCK_RAW ? IP,ICMPなど PF_UNIX SOCK_STREAM 0 同一ホスト内(UNIXドメイン)のストリーム PF_UNIX SOCK_DGRAM 0 同一ホスト内(UNIXドメイン)のデータグラム PF_NS SOCK_SEQPACKET ? XEROX NS protocol の順序付きパケット PF_NS SOCK_RDM ? XEROX NS protocol の信頼性のあるデータグラム ----------------------------------------------------------------------これ以外の組み合わせ、使えない。たとえば、PF_INETとSOCK_SEQPACKET を socket システム・コールで指定しても、うまくいかない。
TCP/IPで通信する時には、通信相手のIPアドレス(32ビットの整数、番号)が 必要になる。IPアドレスは、コンピュータにとって扱いやすいが、人間にとっ て分かりにくい。
人間にとってわかりやすい記号(文字列)を使ったコンピュータの名前から IPアドレスに変換するサービスがあれば便利である。このサービスを、 名前サービス(name service)、 という。 名前サービスを提供するプログラム(プロセス)を、名前サーバという。
名前から名前を指している番号に変換することを 名前解決(name resolution) という。
インターネットで使われている名前サービスは、 DNS(Domain Name System) と呼ばれる。 DNS では、膨大な数のホスト名を含む名前空間を階層的にドメイン(領域)に 分割して管理ている。 この空間の構造は、木構造と同じものでいる。
% 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 a.b.c.d 23ここで、a.b.c.d とは、32ビットのIPアドレスを8ビットずつに区切り、 それぞれの8ビットの整数を10進数で表記したものである。よって、実際のIP アドレスは、次のようしてに計算できる。![]()
((((a*256)+b)*256)+c)*256+d == XXXXXXXXXXよって、telnet コマンドに「.」を含まない10進数次のようにIPアドレスを 与えてもよい。
% telnet XXXXXXXXXX 23![]()
% telnet localhost echo注意:セキュリティ上の理由から、echo などの、システムプログラムの講義 くらいでしか役に立たないようなサービスを停止することが、最近では一般的 である。Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. hello
hello exit
exit quit
quit aaa
aaa ^] telnet> quit
Connection closed. %
![]()
以下は、telnet を使って、adonis9.coins.tsukuba.ac.jp 上で動作している echo サーバを利用している(adonis9 上のサービスは、情報学類内のコンピュー タからしかつながらないような設定をしている)。
% egrep echo /etc/servicesecho 7/tcp echo 7/udp at-echo 204/tcp # AppleTalk echo at-echo 204/udp echo 4/ddp # AppleTalk Echo Protocol % telnet adonis9.coins.tsukuba.ac.jp 7
Trying 130.158.86.29... Connected to adonis9.coins.tsukuba.ac.jp. Escape character is '^]'. hello
hello aaa
aaa ^] telnet> quit
Connection closed. %
![]()
% cp ~yas/syspro/ipc/echo-client-fdopen.c .このプログラムは、コマンドラインから2つ以上の引数をとる。第1引数で指 定されたホスト上の、第2引数で指定されたポートで動作しているサーバに接 続する。そして、キーボードから1行単位で入力し、そのサーバへ送る。echo サービスのサーバは、同じ文字列を送り返して来る。このプログラムは、サー バから送り返されてきた文字列を受取り、結果を画面に表示する。% make echo-client-fdopen
cc echo-client-fdopen.c -o echo-client-fdopen % ./echo-client-fdopen
Usage: ./echo-client-fdopen host port % ./echo-client-fdopen adonis7.coins.tsukuba.ac.jp 7
==> hello
sent: [hello ] received: [hello ] ==> aaa
sent: [aaa ] received: [aaa ] ==> ^D %
![]()
TCP/IPのクライアント側のプログラムで大事な標準のシステムコールとライブ ラリ関数は、次の通りである。
1: /* 2: echo-client-fdopen.c -- 文字列を送受信するクライアント(TCP/IP版) 3: ~yas/syspro/ipc/echo-client-fdopen.c 4: Created on: 1998/06/01 23:48:29 5: */ 6: #include <stdio.h> 7: #include <sys/types.h> /* socket() */ 8: #include <sys/socket.h> /* socket() */ 9: #include <netinet/in.h> /* struct sockaddr_in */ 10: #include <netdb.h> /* getaddrinfo() */ 11: 12: extern int echo_client( char *server, int portno ); 13: extern int fdopen_sock( int sock, FILE **inp, FILE **outp ); 14: 15: main( int argc, char *argv[] ) 16: { 17: char *server ; 18: int portno ; 19: if( argc != 3 ) 20: { 21: fprintf( stdout,"Usage: %s host port\n",argv[0] ); 22: exit( -1 ); 23: } 24: server = argv[1] ; 25: portno = strtol( argv[2],0,10 ); 26: echo_client( server, portno ); 27: }main() 関数は、コマンドラインの引数を調べて、echo_client() を読んでい る。strtol() で、引数で文字列として与えられた数を、int に変換している。
28: 29: #define BUFFERSIZE 1024 30: 31: int 32: echo_client( char *server, int portno ) 33: { 34: int sock ; 35: char sbuf[BUFFERSIZE]; 36: char rbuf[BUFFERSIZE]; 37: FILE *in, *out ; 38: 39: sock = tcp_connect( server, portno ); 40: if( sock<0 ) 41: exit( -1 ); 42: if( fdopen_sock(sock,&in,&out) < 0 ) 43: { 44: fprintf(stderr,"fdooen()\n"); 45: exit( 1 ); 46: } 47: printf("==> "); fflush(stdout); 48: while( fgets(sbuf,BUFFERSIZE,stdin) ) 49: { 50: fprintf(stdout,"sending: [%s]\n",sbuf ); 51: fprintf(out,"%s",sbuf ); 52: fgets( rbuf,BUFFERSIZE,in ); 53: printf("received: [%s]\n",rbuf ); 54: printf("==> "); fflush(stdout); 55: } 56: printf("\n"); 57: fclose( in ); 58: fclose( out ); 59: }echo_client() では、tcp_connect() という関数を呼び出している。この結果、 サーバとの間に TCP/IP通信路の開設され、通信可能なファイル記述子が返さ れる。このファイル記述子は、標準入出力(0,1,2)や open() システム・コー ルの結果と同じもので、 ファイルのコピー(システムコールの利用) で使った write() システムコールや read() システムコールの第一引数とし て使うことができる。つまり、write() システムコールを使うと、ネットワー クに対してデータを送り出すことができ、read() システムコールを使うとネッ トワークからデータを受け取ることができる。最後に不要になったら close() で解放する。
このプログラムでは、fdopen_sock() (後述)を使って、通信可能なファイル記 述子 com から2つの FILE * を作成している。1つは、入力用、1つは出力 用である。その結果、 高水準入出力ライブラリ を使って通信が行えるようになっている。fprintf() で出力用の FILE * に書 き込むと、ネットワークに対してデータが送り出される。入力用の FILE * に fgets() を行うと、ネットワークからデータを受け取ることができる。
TCP/IP の通信では、行単位(最後に\n)でデータを送受信することが多い。 echo サービスでは、1行送り、1行受け取る。他のサービスでは、1行送っ て複数行受け取ったり、受け取る方では行の概念がなくなるもの(HTTPで画像 データを受け取る場合など)もある。その場合は、fprintf() や fgets() では なくて、fwrite() や fread() を使う必要がある。
60: 61: /* 新城、システムプログラム講義用 */ 62: 63: int tcp_connect( char *server, int portno ) 64: { 65: struct addrinfo hints, *ai; 66: struct sockaddr_in addr ; /* sockaddr_storage */ 67: int s ; 68: int err ; 69: 70: if( (s = socket(PF_INET, SOCK_STREAM, 0)) < 0 ) 71: { 72: perror("socket"); 73: return( -1 ); 74: } 75: 76: memset( &hints, 0, sizeof(hints) ); 77: hints.ai_family = AF_INET ; 78: hints.ai_socktype = SOCK_STREAM ; 79: if( (err = getaddrinfo( server, NULL, &hints, &ai )) ) 80: { 81: fprintf(stderr,"unknown host %s (%s)\n",server,gai_strerror(err) ); 82: close( s ); 83: return( -1 ); 84: } 85: if( ai->ai_addrlen > sizeof(addr) ) 86: { 87: fprintf(stderr,"sockaddr too large (%d) > (%d)\n", 88: ai->ai_addrlen,sizeof(addr) ); 89: freeaddrinfo( ai ); 90: close( s ); 91: return( -1 ); 92: } 93: memcpy( &addr, ai->ai_addr, ai->ai_addrlen ); 94: addr.sin_port = htons( portno ); 95: freeaddrinfo( ai ); 96: 97: if( connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0 ) 98: { 99: perror( server ); 100: close( s ); 101: return( -1 ); 102: } 103: return( s ); 104: }tcp_connect() は、通信路の開 設の仕事のうち、クライアント側の仕事をする関数である。まず、クライア ント側のソケットを、socket() システムコールで作成している。 PF_INET と SOCK_STREAMの組み合わせなので、 TCP を使うことを意味する。
socket() の引数で、PF_INET の変りに、AF_INET と書いてもよい。ここでは、 Protocol を選んでいるので、PF_ が正しいが、実際には、PF_INET と AF_INET は同じであり、また、多くのテキストで混在されて使われいる。
ソケットが作成できたら、connect() システムコールで接続する。その第2引 数と第3引数で、接続先のアドレスを指定しなければならない。 接続先には、sockaddr を使う。 この構造体には、IP アドレスとポート番号が含まれている。
IP アドレスは、文字列で与えられた接続先のサーバのホスト名から ライブラリ関数 getaddrinfo() を使って調べている。(従来は、getaddrinfo() ではなく gethostbyname() が よく使われていた。getaddrinfo() は、IPv6 にも対応している。)
ポート番号は、引数で与えられたものをそのまま使う。ただし、 「 ネットワーク・バイトオーダ(ビッグエンディアン) 」 に変換する必要がある。ポート番号は、16 ビットなので、 ライブラリ関数 htons() (host to network, short)を使って変換している。 (変換する関数としては他に、htonl(), ntohl(), ntohs() がある。)
IP アドレスもネットワーク・バイトオーダになっているが、これは getaddrinfo() 関数で、struct addrinfo の ai_addr に保存それた段階そう なっている。
ポート番号として、整数(short)ではなく、/etc/services ファイルに含まれ た文字列で指定したいこともある。その場合は、getaddrinfo() 関数の第2引 数に文字列を渡す。
getaddrinfo() の中で malloc() されたメモリは、free() を直接呼び出して 解放するのではなく、freeaddrinfo() を呼び出して解放する。 (直接 free() を呼ぶと、ai_next の先が解放されない。)
105: 106: int 107: fdopen_sock( int sock, FILE **inp, FILE **outp ) 108: { 109: int sock2 ; 110: if( (sock2=dup(sock)) < 0 ) 111: { 112: return( -1 ); 113: } 114: if( (*inp = fdopen( sock2, "r" )) == NULL ) 115: { 116: close( sock2 ); 117: return( -1 ); 118: } 119: if( (*outp = fdopen( sock, "w" )) == NULL ) 120: { 121: fclose( *inp ); 122: *inp = 0 ; 123: return( -1 ); 124: } 125: setvbuf(*outp, (char *)NULL, _IOLBF, 0); 126: return( 0 ); 127: }fdopen_sock() は、dup() してファイル記述子を増やし、fdopen() を呼んで いる。fdopen() は、既に open() や pipe() 等で得られたファイル記述子から 高水準入出力ライブラリ が使えるようにするためのものである。 setvbuf() では、バッファリングを行わせないようにしている。これにより、 たとえば fprintf() を行うと、即座にネットワークに送り出される。
1: 2: /* 3: EchoClient.java -- 文字列を送受信するクライアント(TCP/IP版) 4: ~yas/syspro/ipc/EchoClient.java 5: Created on 2004/02/14 21:09:17 6: */ 7: 8: import java.net.*; 9: import java.io.*; 10: 11: class EchoClient 12: { 13: public static void main(String argv[]) throws IOException { 14: if( argv.length != 2 ) 15: { 16: System.err.println("Usage: % java EchoClient host port"); 17: System.exit( -1 ); 18: } 19: String server = argv[0]; 20: int portno = Integer.parseInt( argv[1] ); 21: echo_client( server, portno ); 22: } 23: 24: public static void echo_client( String server, int portno ) 25: throws IOException 26: { 27: Socket sock = new Socket( server, portno ); 28: BufferedReader in = new BufferedReader( 29: new InputStreamReader( sock.getInputStream() )); 30: PrintStream out = new PrintStream( sock.getOutputStream() ); 31: stdout.print("==> "); 32: String sline; 33: while( (sline = stdin.readLine())!= null ) 34: { 35: stdout.println("sending: ["+sline +"]"); 36: out.println( sline ); 37: String rline = in.readLine(); 38: stdout.println("received: ["+rline+"]"); 39: stdout.print("==> "); 40: } 41: stdout.println(""); 42: in.close(); 43: out.close(); 44: sock.close(); 45: } 46: static java.io.BufferedReader stdin = 47: new java.io.BufferedReader( new java.io.InputStreamReader(System.in) ); 48: static java.io.PrintStream stdout = System.out; 49: static java.io.PrintStream stderr = System.err; 50: 51: }
次の通信プロトコルのクライアントを1つ以上作りなさい。(以下の項目には、 各プロトコルを説明したページへのリンクが埋め込まれている。)
まず、telnet で、これらのサーバに接続しなさい。そして、それぞれのプロ トコルに従って、要求を打ち込み、どのような結果が返ってくるかを調べなさ い。次に、telnet で行った要求の送信と結果の受信を行うようなプログラムを 作りなさい。このとき、必要なパラメタは、main() の引数から取りなさい。
注意。それぞれのプロトコルでは、接続先として次のホストを使いなさい。
プログラムをつくる時には、行末の扱い(CR-LF)に注意しないさい。
HTTP, finger, SMTP, NNTP など、インターネットで使われている通信プロト コルの多くでは、行末を表す記号として、キャリッジ・リターン(carrige return,CR)とライン・フィード(Line feed, LF)の両方が必要であると定めら れている。Unix では、通常ライン・フィード(ニュー・ライン、New Line, NL と呼ばれることもある)だけが行末の記号として使われる。よって、画面 に文字列を表示し、改行したい場合は、次のようなプログラムが使われる。
printf("Hello,world\n");しかしながら、インターネットの上のプログラムを作成する時には、次のよう にしなければならないことが多い。
printf("Hello,world\r\n");ここで、'\r' がキャリッジ・リターン、'\n' がライン・フィードである。い ずれも、C言語のソース・プログラム上では2文字に見えるが、Cコンパイラ により、1文字に変換される。それぞれ、アスキーでは、13(0x0d), 10(0x0a) である。
規格上は、CR-LF が必要とされているが、実際にプログラムを作る時には、LF だけでも動くことがある。しかし、それはたまたまその特定のサーバのプログ ラムが良くできていて、LF だけでも動くようになっているからである。
% ./time-client host 37Mon May 19 02:49:05 JST 2003 %
![]()
このプログラムでは、TCP/IP でサーバに接続した後、何も送らずにサーバか ら4バイトの数を読み込む。その4バイトの数は、ネットワーク・バイト・オー ダになっているので、ntohl() で、ホストのバイト・オーダに変換する。この 値に、ある値で補正して、Unix で使われているtime_t に変換する。最後に、 strftime() や localtime() でカレンダーの形式に変換する。
RFC868 Time Protocol では、値は、1900年1月1日 0:00 (GMT) を基準にした 秒数を返す。time() システムコールや gettimeofday() システムコールでは、 1970 年を基準にしている。strftime() や localtime() を使う前に、差分を補正する必要がある。
この課題では、ポート番号 37 の time を使いなさい。13 の daytime を使っ てはならない。
接続先のホストとしては、次のどれかを使いなさい。