模擬實現Web服務器

因爲最近想對HTTP協議裏面的操作細節有更深層更詳細的的理解,就自己模擬實現了一個基於HTTP協議線程池版本的Web服務器的小項目。

我所的開發環境是CentOS Linux release 7.4.1708,Vim,g++。.
設計思路:
1.首先http協議是基於TCP通信的,所以我們得先實現TCP通信(socket編程通信)。

class Server{//封裝成一個類
    private:
        int port;
        int sock;
    public:
        Server(int _port=8080):port(_port),sock(-1)
        {}
        void Init()
        {
        sock=socket(AF_INET,SOCK_STREAM,0);
        if(sock<0)
        {
        cerr<<"socket err!"<<endl;
        exit(2);
        }
        struct sockaddr_in local;
        local.sin_family=AF_INET;
        local.sin_addr.s_addr=htonl(INADDR_ANY);
        local.sin_port=htons(port);
        if(bind(sock,(struct sockaddr *)&local,sizeof(local))<0)//綁定
        {
        cerr<<"bind err!"<<endl;
        exit(3);
        }
        if(listen(sock,8)<0)//監聽
        {
        cerr<<"listen err!"<<endl;
        exit(4);
        }
        }
        void Run()
        {
            struct sockaddr_in peer;
            socklen_t len=0;
        while(1)
        {
            int nsock=accept(sock,(struct sockaddr *)&peer,&len);//接收新連接
            if(nsock<0)
            {
            cerr<<"accept err!"<<endl;
            exit(5);
            }
            cout<<"Get a new link..."<<endl;
            
            pthread_t pd;//讓線程去處理,後面改成線程池版本。
            int *p=&nsock;
            pthread_create(&pd,NULL,Entry::HandleRequest,(void *)p);//在HandleRequest函數裏面進行請求處理,它在另一個類中。
           
        }
        }
        ~Server()
        {
        if(sock>=0)
        {
        close(sock);
        }
        }
};

2.當通信建立好了之後,客戶端可以向服務端發起請求,客戶端可以使用GET,POST方法請求資源。因爲將來可能同時有很多的用戶同時訪問服務器,所以在服務器端可以維護一個線程池,實現多用戶高效率,而當服務器收到一個請求後,就從線程池裏面喚醒一個線程,讓這個線程爲用戶請求提供服務。接下來服務端需要對請求進行相應的分析。比如分析得到請求的方法,請求的資源路徑url,判斷資源是否合法並產生出相應的錯誤碼,以及判斷是否攜帶參數,如果有參數,參數在哪個部分等等。(可在源碼中查看具體代碼實現)

3.如果這個請求攜帶參數或者請求的資源是一個可執行程序等情況,則要調用CGI程序。

4.之後服務器將客戶端請求的資源以html頁面的形式響應給客戶端,並可以差錯處理。比如,客戶端請求的資源不存在時,返回一個404頁面。大致思路就是這個樣子,其中實現起來也有很多細節需要注意。
項目特點:
1、支持客戶端/服務器模式,客戶端能夠使用 GET、POST 方法請求資 源,並利用線程池實現多用戶,高效率。
2、客戶向服務器請求服務時,只需傳送請求方法和路徑。
3、允許傳輸多種類型的數據對象,正在傳輸的類型由 Content-Type 加以 標記處理。

當然啦在項目中也遇見了一些問題。
第一個問題,我原本的思路的是,收到不帶參的請求時,服務器構造響應的html頁面就是服務器的首頁,在這個html文件中,標題和正文部分有中文,但是開始的時候我沒有注意編碼格式的問題,就導致後面測試的結果頁面出現亂碼,那這個問題如何解決呢。如果你的網頁裏面出現了中文,在頭部沒有加 ,將會導致中文亂碼,這是編碼格式,相當於是告訴給瀏覽器用什麼方式來讀這頁代碼。是設置網頁文件展示時使用的字符集(編碼),那其實除了網頁文件展示時有編碼以外,網頁文件本身還有編碼。必須兩者統一時纔不會亂碼。
第二個問題,不能顯示圖片(這個問題是沒有將所有發送的情況考慮完全,只考慮到目錄、可執行程序,但沒有考慮到如果請求的是一個路徑明確的普通文件) 後來的解決辦法是,測試請求一個路徑明確的test.html文件,加入調試信息 ,將問題定位在:如果請求的資源存在,應該如何處理。對於普通文件,找到後並回顯給瀏覽器;如果是目錄,應答的是默認頁面;如果是可執行程序,執行後返回結果 就可以了

測試功能:
可以在本地瀏覽器上面做測試,瀏覽器爲客戶端。
1.在測試之前需要先關閉Linux的防火牆,在命令行輸入命令systemctl status firewalld可查看防火牆情況,輸入命令 systemctl stop firewalld 關閉防火牆,如果彈出下面窗口,你輸入當前用戶的密碼完成認證就可以了。
在這裏插入圖片描述
2.在Linux命令行上可用命令 ifconfig 查看當前Linux下的網絡情況,找到它的ip.
在這裏插入圖片描述
3.啓動服務器端(用到了端口號),然後用本地瀏覽器連接這個ip,加端口號,此時瀏覽器(客戶端)發起請求,服務器端收到請求構造響應發送響應。因爲是以html的形式響應的,故在瀏覽器(客戶端)可直接看到響應的頁面。
這個是請求的是首頁:
在這裏插入圖片描述
如果請求的資源不存在等情況,會返回一個404頁面,如下圖:
在這裏插入圖片描述
完整的項目源碼模擬Web服務器
若有問題 歡迎交流~

發佈了95 篇原創文章 · 獲贊 16 · 訪問量 8776
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章