Http服務器實現文件上傳與下載(三)

1、引言html

  在前2章的內容基本上已經講解了整個的大體流程。在設計Http服務器時,我設計爲四層的結構,最底層是網絡傳輸層,就是socket編程。接着一層是請求和響應層,叫作Request和Response。在上一層是URL解析流程走向層。最頂層我設計爲索引層。這一層主要多文件時對文件進行內存上的索引,加快文件的查找。或者多是其餘內容。在這一次中也包括了一些瀏覽器顯示的頁面內容。這些都是能夠讀者自行添加。linux

  在寫這一章節時,我不知道該是從上往下講解,仍是從下往上講解能讓讀者更加清楚個人設計。在思考中.....,最終選擇從底層的socket層開始講解。若是你們對此還有什麼疑問能夠查看前兩章的內容。ios

2、socket編程編程

  在開始封裝的時候,你們能夠看看《Unix網路編程》這本書,主要的內容仍是在上面,主要的代碼和這本書上的大體一致。在這裏我在梳理一下。我不會講解全部的API,客戶端上的API我就不講解了,我只是說一下服務器用到的API。api

  在開始講解時,我須要說2種套接字。只要是爲後面的內容作一下解釋。當咱們開始socket編程時,須要建立一個套接字和遠程端進行鏈接的。在這句話中就包含了2種套接字。一種是監聽套接字,一種是鏈接套接字。好比說監聽套接字就是咱們宿舍樓下的大爺,而鏈接套接字就是咱們。當有一個鏈接叫作快遞員到達樓下時,被宿舍樓下大爺發現了,其實就是監聽套接字發現有鏈接進來了。而後大爺告訴咱們,你的快遞來了,而後咱們下樓向快遞員簽字拿快遞,這就是創建了鏈接。瀏覽器

  從上面得知咱們須要一個監聽套接字,也就是宿舍大爺。建立以下:服務器

  int socket(int family,int type,int protocal);網絡

  發現沒有,返回值就是套接字,其實就是一個整數,或者稱爲文件描述符。由於在linux下全部的設備都是文件,因此能夠用一個惟一的整數表明某個含義。該返回-1時表明建立套接字失敗。通常咱們TCP編程參數填寫會是listenfd= socket(AF_INET,SOCK_STREAM,0);建立的是流套接字,具體內容能夠看上面提到的書籍。socket

  接下來咱們須要綁定套接字,爲何?由於咱們向系統申請了一個套接字(宿舍大爺)後,但是他尚未工做地點呢,咱們須要爲他安排工做地點。工做地點也就是端口,要表示一個惟一的工做地點,計算機須要IP和端口同時制定才能肯定惟一。tcp

  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。在這三個參數中第一個仍是這個監聽套接字listenfd,第二個和第三個能夠獲取鏈接者的身份。來着的IP和端口。

  通常若是不用設爲NULL。例子以下:

     clientlen = sizeof(struct sockaddr_in);
     int fd = accept(listenfd,(struct sockaddr *)&clientaddr,&clientlen);

  當不爲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,可是最後兩個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\n時,空行,表示這一次讀完。
 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=='\n')
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)=='\n')
115             n=0;
116         return n;
117     }
118 }

  到這裏能夠看到這一層的內容已經完成,若是感興趣,請關注我,繼續查看下面的講解《Http服務器實現文件上傳與下載(四)

相關文章
相關標籤/搜索