Httpサーバはファイルのアップロードとダウンロードを実現する(三)

27134 ワード

一、引用
前の2章の内容は基本的に全体の大まかな流れを説明した.Httpサーバを設計する時、私は4層の構造に設計して、最下層はネットワーク伝送層で、socketプログラミングです.次のレイヤはリクエストと応答レイヤで、RequestとResponseと呼ばれます.前の階層ではURL解析プロセスの進行層です.最上位レベルはインデックスレイヤとして設計されています.このレイヤの主要なマルチファイルの場合、ファイルにメモリ上のインデックスを作成し、ファイルの検索を高速化します.あるいは他の内容かもしれません.今回はブラウザに表示されるページの内容も含まれています.これらはすべて読者が自分で追加することができます.
この章を書くとき、上から説明すべきか、下から説明すれば読者に私のデザインをもっとはっきりさせることができるか分かりません.思考の中で......、最終的に底のsocket層から説明することを選んだ.これについて何か質問があれば、前の2章の内容を見ることができます.
二、socketプログラミング
パッケージを始めたとき、「Unixネットワークプログラミング」という本を読むことができます.主な内容は上にあります.主なコードはこの本とほぼ一致しています.ここで整理しています.私はすべてのAPIを説明することはできません.クライアントのAPIは説明しません.サーバーが使っているAPIについて話します.
説明を始めるとき、私は2種類のソケットを言う必要があります.後の内容のために説明すればいいです.ソケットプログラミングを開始するには、ソケットとリモート端子を接続する必要があります.この言葉には2種類のソケットが含まれています.1つはリスニングソケットであり、1つは接続ソケットである.例えば、傍受ソケットは私たちの寮の階下のおじいさんで、接続ソケットは私たちです.宅配便員という接続が階下に着いたとき、寮の階下のおじいさんに発見されたが、実は傍受ソケットが接続されていることに気づいた.それからおじいさんは私たちに、あなたの宅配便が来たと言って、それから私たちは階下で宅配便員にサインして宅配便を持って、これは接続を確立しました.
上から私たちが傍受ソケットが必要だと知っています.つまり、寮のおじいさんです.次のように作成されます.
  int socket(int family,int type,int protocal);
いいえ、戻り値はソケットで、実は整数、あるいはファイル記述子と呼ばれています.linuxではすべてのデバイスがファイルであるため、ある意味を一意の整数で表すことができます.-1を返すと、ソケットの作成に失敗します.一般的にTCPプログラミングパラメータの記入はlistenfd= socket(AF_INET,SOCK_STREAM,0);作成されたのはストリームソケットで、具体的な内容は上記の本を見ることができます.
次にソケットをバインドする必要があります.なぜですか?私たちはシステムにソケット(寮のおじいさん)を申請したが、彼はまだ職場を持っていないので、私たちは彼のために職場を手配する必要があります.作業場所はポートであり、唯一の作業場所を表すには、コンピュータがIPとポートを同時に作成してから一意を特定する必要があります.
  int bind(int sockfd,const struct sockaddr*myaddr,socklen_t addrlen);
0が正常に返された場合は、-1が返されます.最初のパラメータは、さっき作成したソケットlistenfdを表します.struct sockaddrは汎用ソケット構造ですが、実際にはstruct sockaddr_を伝送しています.in{}のような構造では,これらのAPIが比較的古いため,主にvoid*というタイプが以前になかったため,強制変換が必要である.socklen_tは符号のない整数タイプである.呼び出し方法は一般的に
bind(listenfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
このserveraddrの充填は以下の通りです.
              bzero(&serveraddr,sizeof(serveraddr));              serveraddr.sin_family=AF_INET;              serveraddr.sin_addr.s_addr=htonl(INADDR_ANY);              serveraddr.sin_port=htons(PORT);
serveraddrのタイプは確かにstruct sockaddr_in;
   今も寮のおじいさんのために仕事の場所を手配して、次はおじいさんに仕事をさせただけで、さもなくば申請して何をしに来ますか...
  int listen(int sockfd,int backlog);
正常に0を返すのに失敗した場合-1.最初のソケットが先ほどのリスニングソケットであるか、backlogというパラメータは主に未完了接続キューと完了接続キューの問題に関連します.最大の接続数として理解すればいいので、具体的には上の本を見てください.
最後におじいさんが速達員を見つけて、彼の身分を確定した後、私たちを呼んで、これは次のapiです.
  int accept(int sockfd,struct sockaddr*chliaddr,socklent_t *addrlen);
成功すれば接続ソケットを返します.それは私たち自身です.そうしないと-1を返します.この3つのパラメータのうち1番目はこのリスニングソケットlistenfdであり、2番目と3番目は接続者のアイデンティティを取得することができる.来ていたIPとポート.
一般的にNULLに設定しない場合.例は次のとおりです.
     clientlen = sizeof(struct sockaddr_in);     int fd = accept(listenfd,(struct sockaddr *)&clientaddr,&clientlen);
クライアントにデフォルト値を記入しないと、このパラメータは取得長と呼ばれますが、まずサイズを設定する必要があります.
次は速達員と通信しました.私たちは速達員が私に渡した速達を受け取ります.この行為はreadです.コンテンツを自分のストレージスペースに書き込むことです.
  ssize_t read(int sockfd,void*buf,size_t nbyte);
読み取りに成功して実際に読み取ったバイト数を返し、0を返すと搬送済み、0未満は入力エラー、bufは自分で格納して受け取った内容、nbyteはこの空間の大きさを表す.ここでsockfdが今記入しているのは接続ソケットで、私たち自身で、おじいさんではありません.
  ssize_t write(int sockfd,const void* buf,size_t nbytes);
この関数はbuf中のnbytesバイトの内容をsockfdソケットに書き込み,相手に送信する.実際に何バイト書いたかを返すのに成功し、-1を返すことに失敗しました.
ここでは、使用するAPIについて説明しましたが、最後の2つのreadとwriteは、いくつかのlinuxシステムでは、システムが中断されると、readまたはwriteが停止する可能性があるため、少し特殊です.これは、関数を再呼び出しする必要があります.データを正しく取得するためには,いくつかのコードを記入する必要があり,この2つの関数を呼び出すだけでは不十分である.
次はこのソケットのコードセグメントです.ソケットをTCPという名前空間のSocketクラスにカプセル化する.
ヘッダファイル(include/socket.h)
 1 /*
 2  * tcp.h
 3  *
 4  */
 5 
 6 #ifndef SOCKET_H_
 7 #define SOCKET_H_
 8 #include<iostream>
 9 #include<sys/socket.h>
10 #include<sys/types.h>
11 #include<netinet/in.h>
12 #include<errno.h>
13 #include <string.h>
14 //#include <unistd>
15 namespace TCP{
16     class Socket {
17     public:
18         Socket();
19         ~Socket();
20         int server_socket();
21         int server_listen();
22         int server_accept();
23         int server_bind();
24         void  server_init();
25         void  getClient(sockaddr_in* caddr);
26         int server_read(int fd,char*recvBuf,ssize_t maxlen);
27         int server_write(int fd,char*sendBuf,ssize_t maxlen);
28         void server_close(int confd) ;
29     private:
30         int __readline(int fd,char*recvBuf,ssize_t maxlen) ;
31         int __writen(int fd,char*sendBuf,ssize_t maxlen) ;
32         int listenfd;
33         int confd;
34         struct sockaddr_in serveraddr;
35         socklen_t serverlen;
36         static const int PORT=80;
37     };
38 }
39 #endif /* SOCKET_H_ */

ソースファイル(src/socket.h)
  1 #include "socket.h"
  2 namespace TCP{
  3     Socket::Socket() {
  4     }
  5     Socket::~Socket() {
  6     }
  7     int Socket::server_socket() {
  8         listenfd= socket(AF_INET,SOCK_STREAM,0);
  9         if(listenfd !=-1){
 10             std::cout<<"server_socket() ...succeed"<<std::endl;
 11         }else{
 12             std::cout<<"server_socket() ...failed"<<std::endl;
 13         }
 14         return listenfd;
 15     }
 16 
 17     int Socket::server_listen() {
 18         int ret = listen(listenfd,100);
 19         if(ret ==0){
 20             std::cout<<"server_listen() ...succeed"<<std::endl;
 21         }else{
 22             std::cout<<"server_listen() ...failed"<<std::endl;
 23         }
 24         return ret;
 25     }
 26     void Socket::server_close(int confd) {
 27         close(confd);
 28     }
 29     int Socket::server_accept() {
 30         clientlen = sizeof(struct sockaddr_in);
 31         int fd = accept(listenfd,(struct sockaddr *)&clientaddr,&clientlen);
 32         if(fd !=-1){
 33             std::cout<<"server_accept() ...succeed"<<std::endl;
 34         }else{
 35             std::cout<<"server_accept() ...failed"<<std::endl;
 36         }
 37         return fd;
 38     }
 39     int Socket::server_bind() {
 40          int ret =bind(listenfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr));
 41          if(ret ==0){
 42              std::cout<<"server_bind() ...succeed"<<std::endl;
 43          }else{
 44              std::cout<<"server_bind() ...failed"<<std::endl;
 45          }
 46          return ret;
 47     }
 48     void  Socket::server_init() {
 49         bzero(&serveraddr,sizeof(serveraddr));
 50         serveraddr.sin_family=AF_INET;
 51         serveraddr.sin_addr.s_addr=htonl(INADDR_ANY);
 52         serveraddr.sin_port=htons(PORT);
 53 
 54     }
 55     ssize_t Socket::server_read(int fd,char*recvBuf,ssize_t maxlen) {
 56         long  long havedreadCount=0;
 57         int readCount=0;
 58         while(1){
 59             readCount = __readline(fd,recvBuf+havedreadCount,maxlen);
 60             havedreadCount+=readCount;
 61             //std::cout<<"readCount:"<<readCount<<std::endl;
 62             if(readCount==0)//    \r
, , 。
63 break; 64 } 65 return 0; 66 67 } 68 ssize_t Socket::server_write(int fd,char*sendBuf,ssize_t maxlen){ 69 70 return __writen(fd,sendBuf,maxlen); 71 } 72 int Socket::__writen(int fd,char*sendBuf,ssize_t maxlen){ 73 size_t nleft; 74 ssize_t nwritten; 75 const char *ptr; 76 ptr=sendBuf; 77 nleft=maxlen; 78 //int count=0; 79 80 while(nleft>0){ 81 if((nwritten=write(fd,ptr,nleft))<=0){ 82 if(nwritten<0&& errno==EINTR) 83 nwritten=0; 84 else{ 85 return -1; 86 } 87 } 88 nleft-=nwritten; 89 ptr+=nwritten; 90 } 91 return maxlen; 92 } 93 int Socket::__readline(int fd,char*recvBuf,ssize_t maxlen) { 94 ssize_t n,rc; 95 char c,*ptr; 96 ptr=recvBuf; 97 for(n=1;n<maxlen;n++){ 98 again: 99 if((rc=read(fd,&c,1))==1){ 100 *ptr++=c; 101 //std::cout<<c; 102 if(c=='
') 103 break; 104 }else if(rc ==0){ 105 *ptr=0; 106 return n-1; 107 }else{ 108 if(errno ==EINTR) 109 goto again; 110 return -1; 111 } 112 } 113 *ptr=0; 114 if(n==2&&*(ptr-2)=='\r'&&*(ptr-1)=='
') 115 n=0; 116 return n; 117 } 118 }

ここまで来るとこの層の内容が完成しているのが見えますが、興味があれば、私に注目して、次の説明「Httpサーバー実現ファイルアップロードとダウンロード(四)」を引き続き見てください.