#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define PORT *1{
      if(++i>=argc)error(NOOUT);
      out = open(argv[i], O_WRONLY|O_CREAT|O_TRUNC, 0666);
      if(out<0)error(NOOUT);
      continue;
    }

    /* ファイルパスの決定 */
    if(!strcmp(argv[i],"-f")){
      if(++i>=argc)error(NOURL);
      path = argv[i];
      continue;
    }

    /* 接続するサーバーの決定 */
    if(!strcmp(argv[i],"-h")){
      if(++i>=argc)error(NOSERVER);
      hostname = argv[i];
      continue;
    }

    /* プロキシサーバーの利用 */
    if(!strcmp(argv[i],"-p")){
      /* hostname = "proxy.csc.titech.ac.jp";*/
      if(++i>=argc)error(NOPROXY);
      hostname = argv[i];
      continue;
    }
  }

  /* サーバのマシン名(hostname)からサーバのネットアドレスを得る*/
  servhost = gethostbyname(hostname);
  if ( servhost == NULL ){
    fprintf(stderr, "%s unknown host\n",hostname);
    exit(2);
  }
  
  /* 初期化 */
  bzero(&server, sizeof(server));

  /* address familyの指定 */
  server.sin_family = AF_INET;
  
  /* サーバのIPアドレスをsin_addrにコピーする */
  bcopy(servhost->h_addr, &server.sin_addr, servhost->h_length);
  
  /* htons()を使いサーバのソケットのポート番号を */
  /* host format から network format に変換する */
  server.sin_port = htons(PORT);
  
  /* ソケット生成部分 */
  sock = socket(AF_INET, SOCK_STREAM, 0);
  
  if (sock < 0) {
    perror("opening stream socket");
    exit(1);
  }
  
  /* サーバにコネクション要求を送る */
  /* コネクション要求が受け付けられるまでブロックされる */
  if ( connect(sock, (struct sockaddr *)&server, sizeof(server))<0 ){
    perror("connecting stream socket");
    exit(1);
  }
  
  /* HTTPリクエストの送信部分 */
  write(sock, mesget, strlen(mesget) );
  write(sock, path, strlen(path) );
  write(sock, meshttp, strlen(meshttp)+1 );
  
  /* HTTPレスポンスを受信 */
  while(n = read(sock, buf, sizeof(buf)-1)){
    for(i=0; i


加えた機能とその実現方法の簡単な説明

まず、ソケット生成部分を完成させる。
以下に示すように、socket関数により、ソケットを生成する。
sock = socket(AF_INET, SOCK_STREAM, 0);

更に、write関数により、生成したソケットへhttpリクエストを送信する。
本プログラムでは、異なるファイルパスを指定しやすいように、
httpリクエストを三つに分割している。
一つ目の文字列(mesget)には、GET命令が入る。
二つ目の文字列(path)には、ファイルパスが入る。
三つ目の文字列(meshttp)には、HTTPのバージョンが入る。
pathには初期値として"/index.html"が
代入されており、また、
接続するサーバーのデフォルトの値として
"www1.csc.titech.ac.jp"が定義されているので、
本プログラムを引数なしに実行した場合には、
http://www1.csc.titech.ac.jp/index.html を取得するようになっている。

最後に、HTTPレスポンスを受信し、その内容を標準出力する。
そのためにまず、read関数によって、ソケットからレスポンスを読み取る。
読み取った文字列はbufに保存されるので、
読み取ったバイト数を示すread関数の戻り値分だけ、
bufをwrite関数によって標準出力する。
この作業が一度だけだと、
バッファのサイズを超えたレスポンスを取得できないので、
すべてのレスポンスを取得しread関数の戻り値が0になるまで、
この作業を繰り返すことになる。
だだし、この後説明するが、
メッセージヘッダとメッセージボディの出力先を変更できるようにするため、
やや複雑な構造となっている。

                                  • 2 メッセージボディの出力先の変更

本プログラムでは、コマンドライン引数を与えることによって、
メッセージボディの出力先を標準出力ではなく
指定したファイルに変更できるようになっている。
書き出すファイルを指定するには、
実行ファイルが/a.outである時、次のように指定する。
% ./a.out -o file
引数-oの次の引数がファイル名になるのである。
メッセージボディの出力先は変数outに定義されており、
デフォルトでは標準出力になっている。
書き出すファイルを指定すると、
そのファイルハンドラがoutに代入される。

さて、出力先を変更するのはメッセージボディだけであり、
メッセージヘッダは常に標準出力しなければならない。
そのため、HTTPレスポンスの中に\r\n\r\nという並びが出てくるまでは、
常に標準出力するようにし、
その並びが出てきた後は、outに出力するようにしている。

                                  • 3 接続する Web サーバと要求するファイルのパスの変更

メッセージボディの出力先と同様に、
コマンドライン引数を与えることによって
接続するウェブサーバーと要求するファイルのパスを変更できるようになっている。
以下に、例を示す。
% ./a.out -f /index.html -h www1.csc.titech.ac.jp
引数-fの後に来る引数が、ファイルのパスを示し、
また、引数-hの後に来る引数が、要求するウェブサーバーを示す。

ファイルのパスは、前述したように変数pathに定義されている。
デフォルトの値は"/index.html"である。
引数-fによってファイルのパスが指定されると、
指定されたパスがpathに代入される。

接続するウェブサーバーは変数hostnameに定義されている。
デフォルトの値は"www1.csc.titech.ac.jp"である。
引数-hによって接続するウェブサーバーが指定されると、
指定されたパスがhostnameに代入される。

                                  • 4 代理サーバ経由で外部の Web サーバへアクセス

接続するウェブサーバーと同様に、
コマンドライン引数を与えることによって
代理サーバーを指定できる。
以下に、例を示す。
% ./a.out -p proxy.csc.titech.ac.jp -f http://sato-www.cs.titech.ac.jp/pro3/
引数-pの後に来る引数が、接続する代理サーバーを示す。
また、代理サーバ経由で外部のウェブサーバーへ接続する場合は、
ファイルパスの代わりに完全なURLを与える。
これで、代理サーバ経由で外部のウェブサーバーにアクセスできるはずである。

代理サーバとはいっても、単に接続するウェブサーバーを変更するだけであるから、
引数-pによって指定された代理サーバーは、
通常の接続するウェブサーバーを指定した場合と同様に、
hostnameに指定した代理サーバーが代入される。


演習の感想等

メッセージヘッダとボディの出力先を変更する部分のプログラムが汚い。
ほかの部分はまあまあきれいに書けたと思います。

*1:u_short)80) /* エラーコードを定義 */ enum {NOOUT, NOURL, NOSERVER, NOPROXY}; /* エラーを出力 */ void error(int code){ switch(code){ case NOOUT: fprintf(stderr,"Output file not created.\n"); break; case NOURL: fprintf(stderr,"No filepass!\n"); break; case NOSERVER: fprintf(stderr,"No Hostname!\n"); break; case NOPROXY: fprintf(stderr,"No proxyhost!\n"); break; } exit(1); } int main(int argc, char *argv[]){ int sock, i, n, rnflag=0; /* ソケットのためのファイルディスクリプタなど */ struct hostent *servhost; /* ホスト名と IP アドレスを扱うための構造体 */ struct sockaddr_in server; /* ソケットを扱うための構造体 */ char *hostname ="www1.csc.titech.ac.jp"; /* 接続するサーバ名 */ char *path ="/index.html"; /* 要求するファイルのパス */ char *mesget = "GET "; char *meshttp = " HTTP/1.0\r\n\r\n"; int out = STDOUT_FILENO; char buf[BUFSIZ]; for(i=1; i