CSAPP:Tiny Web服務器

Web 服務器,一個多麼莊嚴而神聖的名詞!沒有接觸之前,它神祕而不可侵犯;學習研究之後,它又是如此地平易近人。今天,讓我們一起走近web服務器。

此時,想像一下,當你自己動手實現了一個服務器時,你會是怎麼的興奮;尤其是將一個真正的瀏覽器指向我們自己實現的服務器時,看着它顯示自己本機上的文本及圖片時,那將是怎樣的激動時刻呀!

下面就來看看它的實現全過程:

Tiny的準備


Tiny的main程序


Tiny是一個迭代服務器,監聽在命令行中確定的端口上的連接請求。在通過open_listenedfd函數打開一個監聽套接字以後,Tiny執行典型的無限服務循環,反覆地接受一個連接(accept)請求,執行事務(doit),最後關閉連接描述符(close)。

Tiny的doit函數


rio_readinitb(&rio,fd) :將程序的內部緩存區與描述符相關聯。

rio_readlineb(&rio,buf,MAXLINE) :從內部緩存區讀出一個文本行至buf中,以null字符來結束這個文本行。當然,每行最大的字符數量不能超過MAXLINE。

sscanf(buf,"%s %s %s",method,uri,version) :作爲例子,一般此時buf中存放的是“GET / HTTP/1.1”,所以可知method爲“GET”,uri爲“/”,version爲“HTTP/1.1”。其中sscanf的功能:把buf中的字符串以空格爲分隔符分別傳送到method、uri及version中。

strcasecmp(method,"GET") :忽略大小寫比較method與“GET”的大小,相等的話返回0。

read_requesthdrs(&rio) :讀並忽略請求報頭。

parse_uri(uri,filename,cgiargs) :解析uri,得文件名存入filename中,參數存入cgiargs中。

stat(filename,&sbuf) :將文件filename中的各個元數據填寫進sbuf中,如果找不到文件返回0。

S_ISREG(sbuf,st_mode) :此文件爲普通文件。

S_IRUSR & sbuf.st_mode :有讀取權限。

serve_static(fd,filename,sbuf.st_size) :提供靜態服務。

serve_dynamic(fd,filename,cgiargs) :提供動態服務。

從doit函數中可知,我們的Tiny Web服務器只支持“GET”方法,其他方法請求的話則會發送一條錯誤消息,主程序返回,並等待下一個請求。否則,我們讀並忽略請求報頭。(其實,我們在請求服務時,直接不用寫請求報頭即可,寫上只是爲了符合HTTP協議標準)。

然後,我們將uri解析爲一個文件名和一個可能爲空的CGI參數,並且設置一個標誌位,表明請求的是靜態內容還是動態內容。通過stat函數判斷文件是否存在。

最後,如果請求的是靜態內容,我們需要檢驗它是否是一個普通文件,並且可讀。條件通過,則我們服務器向客服端發送靜態內容;相似的,如果請求的是動態內容,我就覈實該文件是否是可執行文件,如果是則執行該文件,並提供動態功能。

Tiny的clienterror函數


向客戶端返回錯誤信息。

sprintf(buf,"------------"):將字符串“------------”輸送到buf中。

rio_writen(fd,buf,strlen(buf)):將buf中的字符串寫入fd描述符中。

Tiny的read_requesthdrs函數


Tiny不需要請求報頭中的任何信息,所以我們這個函數就是來跳過這些請求報頭的。具體做法就是讀這些請求報頭,直到空行,然後返回。OK!

Tiny的parse_uri函數


根據uri中是否含有cgi-bin來判斷請求的是靜態內容還是動態內容。如果沒有cgi-bin,則說明請求的是靜態內容。那麼,我們需把cgiargs置NULL,然後獲得文件名,如果我們請求的uri最後爲 “/”,則自動添加上home.html。比如說,我們請求的是“/”,則返回的文件名爲“./home.html”,而我們請求“/logo.gif”,則返回的文件名爲“./logo.gif”。如果uri中含有cgi-bin,則說明請求的是動態內容。那麼,我們需要把參數拷貝到cgiargs中,把要執行的文件路徑寫入filename。舉例來說,uri爲/cgi-bin/adder?3&5,則cigargs中   存放的是3&5,filename中存放的是“./cgi-bin/adder”,OK!

index(uri,'?')找出uri字符串中第一個出現參數‘?’的地址,並將此地址返回。

 

Tiny的serve_static函數

打開文件名爲filename的文件,把它映射到一個虛擬存儲器空間,將文件的前filesize字節映射到從地址srcp開始的虛擬存儲區域。關閉文件描述符srcfd,把虛擬存儲區的數據寫入fd描述符,最後釋放虛擬存儲器區域。

Tiny的serve_dynamic函數

Tiny通過派生一個子進程並在子進程的上下文中運行一個cgi程序(可執行文件),來提供各種類型的動態內容。

setenv("QUERY_STRING",cgiargs,1) :設置QUERY_STRING環境變量。

dup2 (fd,STDOUT_FILENO) :重定向它的標準輸出到已連接描述符。此時,任何寫到標準輸出的東西都直接寫到客戶端。

execve(filename,emptylist,environ) :加載運行cgi程序。

====================from csapp.c========================

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章