




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、本文是我學(xué)習(xí)<深入理解計(jì)算機(jī)系統(tǒng)>中網(wǎng)絡(luò)編程部分的學(xué)習(xí)筆記。1. Web基礎(chǔ) web客戶端和服務(wù)器之間的交互使用的是一個(gè)基于文本的應(yīng)用級(jí)協(xié)議HTTP(超文本傳輸協(xié)議)。一個(gè)web客戶端(即瀏覽器)打開一個(gè)到服務(wù)器的因特網(wǎng)連接,并且請(qǐng)求某些內(nèi)容。服務(wù)器響應(yīng)所請(qǐng)求的內(nèi)容,然后關(guān)閉連接。瀏覽器讀取這些內(nèi)容,并把它顯示在屏幕上。 對(duì)于web客戶端和服務(wù)器而言,內(nèi)容是與一個(gè)MIME類型相關(guān)的字節(jié)序列。常見的MIME類型: MIME類型 &
2、#160;描述text/html HTML頁(yè)面text/plain 無(wú)格式文本image/gif GIF格式編碼的二進(jìn)制圖像image/jpeg JPEG格式編碼的二進(jìn)制圖像 web服務(wù)器以兩種不同的方式向客服端提供內(nèi)容:(1)靜態(tài)內(nèi)容:取一個(gè)磁盤文件,并將它的內(nèi)容返回給客戶端(2)動(dòng)態(tài)內(nèi)容:執(zhí)行一個(gè)可執(zhí)行文件,并將它的輸出返回給客戶端 統(tǒng)一資源定位符:URL:80/index.html表示因特網(wǎng)主機(jī) 上一個(gè)稱為 index.html
3、 的HTML文件,它是由一個(gè)監(jiān)聽端口80的Web服務(wù)器所管理的。 HTTP默認(rèn)端口號(hào)為80可執(zhí)行文件的URL可以在文件名后包括程序參數(shù), “?”字符分隔文件名和參數(shù),而且每個(gè)參數(shù)都用“&”字符分隔開,如::8000/cgi-bin/adder?123&456表示一個(gè) /cgi-bin/adder 的可執(zhí)行文件,帶兩個(gè)參數(shù)字符串為 123 和 456確定一個(gè)URL指向的是靜態(tài)內(nèi)容還是動(dòng)態(tài)內(nèi)容沒有標(biāo)準(zhǔn)的規(guī)則,常見的方法就是把所有的可執(zhí)行文件都放在 cgi-bin 目錄中 2. HTTPHTTP標(biāo)準(zhǔn)要求每個(gè)文本行都由
4、一對(duì)回車和換行符來結(jié)束 (1)HTTP請(qǐng)求一個(gè)HTTP請(qǐng)求:一個(gè)請(qǐng)求行(request line) 后面跟隨0個(gè)或多個(gè)請(qǐng)求報(bào)頭(request header), 再跟隨一個(gè)空的文本行來終止報(bào)頭 請(qǐng)求行: <method> <uri> <version>HTTP支持許多方法,包括 GET,POST,PUT,DELETE,OPTIONS,HEAD,TRACE。URI是相應(yīng)URL的后綴,包括文件名和可選參數(shù)version 字段表示該請(qǐng)求所遵循的HTTP版本 請(qǐng)求報(bào)頭:<header name> : <header d
5、ata> 為服務(wù)器提供了額外的信息,例如瀏覽器的版本類型HTTP 1.1中 一個(gè)IP地址的服務(wù)器可以是 多宿主主機(jī),例如 可以存在于同一服務(wù)器上。HTTP 1.1 中必須有 host 請(qǐng)求報(bào)頭,如 host:80 如果沒有這個(gè)host請(qǐng)求報(bào)頭,每個(gè)主機(jī)名都只有唯一IP,IP地址很快將用盡。 (2)HTTP響應(yīng)一個(gè)HTTP響應(yīng):一個(gè)響應(yīng)行(response line) 后面跟隨0個(gè)或多個(gè)響應(yīng)報(bào)頭(response header),再跟隨一個(gè)空的文本行來終止報(bào)頭,最后跟隨一個(gè)響應(yīng)主體(response body) 響應(yīng)行:<version&
6、gt; <status code> <status message>status code 是一個(gè)三位的正整數(shù) 狀態(tài)代碼狀態(tài)消息描述200成功 處理請(qǐng)求無(wú)誤301 永久移動(dòng) 內(nèi)容移動(dòng)到位置頭中指明的主機(jī)上400錯(cuò)誤請(qǐng)求服務(wù)器不能理解請(qǐng)求403 禁止 服務(wù)器無(wú)權(quán)訪問所請(qǐng)求的文件404未發(fā)現(xiàn) 服務(wù)器不能找到所請(qǐng)求的文件501 未實(shí)現(xiàn)服務(wù)器不支持請(qǐng)求的方法505 HTTP版本
7、不支持服務(wù)器不支持請(qǐng)求的版本 兩個(gè)最重要的響應(yīng)報(bào)頭:Content-Type 告訴客戶端響應(yīng)主體中內(nèi)容的MIME類型Content-Length 指示響應(yīng)主體的字節(jié)大小響應(yīng)主體中包含著被請(qǐng)求的內(nèi)容。 3.服務(wù)動(dòng)態(tài)內(nèi)容(1) 客戶端如何將程序參數(shù)傳遞給服務(wù)器GET請(qǐng)求的參數(shù)在URI中傳遞, “?”字符分隔了文件名和參數(shù),每個(gè)參數(shù)都用一個(gè)"&"分隔開,參數(shù)中不允許有空格,必須用字符串“%20”來表示HTTP POST請(qǐng)求的參數(shù)是在請(qǐng)求主體中而不是 URI中傳遞的 (2)服務(wù)器如何將參數(shù)傳遞給子進(jìn)程GET /cgi-bin/adder?123
8、&456 HTTP/1.1它調(diào)用 fork 來創(chuàng)建一個(gè)子進(jìn)程,并調(diào)用 execve 在子進(jìn)程的上下文中執(zhí)行 /cgi-bin/adder 程序在調(diào)用 execve 之前,子進(jìn)程將CGI環(huán)境變量 QUERY_STRING 設(shè)置為"123&456", adder 程序在運(yùn)行時(shí)可以用unix getenv 函數(shù)來引用它 (3)服務(wù)器如何將其他信息傳遞給子進(jìn)程 環(huán)境變量 描述QUERY_STRING程序參數(shù)SERVER_PORT 父進(jìn)程偵
9、聽的端口REQUEST_METHODGET 或 POSTREMOTE_HOST客戶端的域名REMOTE_ADDR 客戶端的點(diǎn)分十進(jìn)制IP地址CONTENT_TYPE只對(duì)POST而言,請(qǐng)求體的MIME類型CONTENT_LENGTH只對(duì)POST而言,請(qǐng)求體的字節(jié)大小 (4) 子進(jìn)程將它的輸出發(fā)送到哪里一個(gè)CGI程序?qū)⑺膭?dòng)態(tài)內(nèi)容發(fā)送到標(biāo)準(zhǔn)輸出,在子進(jìn)程加載并運(yùn)行CGI程序之前,它使用UNIX dup2 函數(shù)將它標(biāo)準(zhǔn)輸出重定向到和客戶端相關(guān)連的已連接描述符因此,任何CGI程序?qū)懙綐?biāo)準(zhǔn)輸出的東西都會(huì)直接到達(dá)客戶端 4. 綜合: Tiny web 服務(wù)器
10、60;(1) main程序Tiny是一個(gè)迭代服務(wù)器,監(jiān)聽在命令行中傳遞來的端口上的連接請(qǐng)求,在通過調(diào)用 open_listenfd 函數(shù)打開一個(gè)監(jiān)聽套接字以后,執(zhí)行無(wú)限服務(wù)器循環(huán),不斷接受連接請(qǐng)求(第16行),執(zhí)行事務(wù)(第17行),并關(guān)閉連接它的那一端(第18行) 1 int main(int argc, char *argv) 2 3 int listenfd, connfd, port, clientlen; 4 struct sockaddr_in clientaddr; 5 6 /* Check command line args */ 7 if (argc != 2) 8 fprin
11、tf(stderr, "usage: %s <port>n", argv0); 9 exit(1);10 11 port = atoi(argv1);12 13 listenfd = Open_listenfd(port);14 while (1) 15 clientlen = sizeof(clientaddr);16 connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen); /line:netp:tiny:accept17 doit(connfd); /line:netp:tiny:d
12、oit18 Close(connfd); /line:netp:tiny:close19 20 (2) doit函數(shù)doit函數(shù)處理一個(gè)HTTP事物,首先讀和解析請(qǐng)求行(request line)(第11-12行),注意,我們使用rio_readlineb函數(shù)讀取請(qǐng)求行。Tiny只支持GET方法,如果客戶端請(qǐng)求其他方法,發(fā)送一個(gè)錯(cuò)誤信息。然后將URI解析為一個(gè)文件名和一個(gè)可能為空的CGI參數(shù)字符串,并且設(shè)置一個(gè)標(biāo)志表明請(qǐng)求的是靜態(tài)內(nèi)容還是動(dòng)態(tài)內(nèi)容(第21行)如果請(qǐng)求的是靜態(tài)內(nèi)容,就驗(yàn)證是否為普通文件,有讀權(quán)限(第29行) 如果請(qǐng)求的是動(dòng)態(tài)內(nèi)容,就驗(yàn)證是否為可執(zhí)行文件(第3
13、7行),如果是,就提供動(dòng)態(tài)內(nèi)容(第42行) 1 void doit(int fd) 2 3 int is_static; 4 struct stat sbuf; 5 char bufMAXLINE, methodMAXLINE, uriMAXLINE, versionMAXLINE; 6 char filenameMAXLINE, cgiargsMAXLINE; 7 rio_t rio; 8 9 /* Read request line and headers */10 Rio_readinitb(&rio, fd);11 Rio_readlineb(&rio, buf, MA
14、XLINE); /line:netp:doit:readrequest12 sscanf(buf, "%s %s %s", method, uri, version); /line:netp:doit:parserequest13 if (strcasecmp(method, "GET") /line:netp:doit:beginrequesterr14 clienterror(fd, method, "501", "Not Implemented",15 "Tiny does not implemen
15、t this method");16 return;17 /line:netp:doit:endrequesterr18 read_requesthdrs(&rio); /line:netp:doit:readrequesthdrs19 20 /* Parse URI from GET request */21 is_static = parse_uri(uri, filename, cgiargs); /line:netp:doit:staticcheck22 if (stat(filename, &sbuf) < 0) /line:netp:doit:beg
16、innotfound23 clienterror(fd, filename, "404", "Not found",24 "Tiny couldn't find this file");25 return;26 /line:netp:doit:endnotfound27 28 if (is_static) /* Serve static content */ 29 if (!(S_ISREG(sbuf.st_mode) | !(S_IRUSR & sbuf.st_mode) /line:netp:doit:readab
17、le30 clienterror(fd, filename, "403", "Forbidden",31 "Tiny couldn't read the file");32 return;33 34 serve_static(fd, filename, sbuf.st_size); /line:netp:doit:servestatic35 36 else /* Serve dynamic content */37 if (!(S_ISREG(sbuf.st_mode) | !(S_IXUSR & sbuf.st_mo
18、de) /line:netp:doit:executable38 clienterror(fd, filename, "403", "Forbidden",39 "Tiny couldn't run the CGI program");40 return;41 42 serve_dynamic(fd, filename, cgiargs); /line:netp:doit:servedynamic43 44 (3)clienterror函數(shù)clienterror函數(shù)檢查一些明顯的錯(cuò)誤,并把它報(bào)告給客戶端 void c
19、lienterror(int fd, char *cause, char *errnum, char *shortmsg, char *longmsg) char bufMAXLINE, bodyMAXBUF; /* Build the HTTP response body */ sprintf(body, "<html><title>Tiny Error</title>"); sprintf(body, "%s<body bgcolor=""ffffff"">rn"
20、, body); sprintf(body, "%s%s: %srn", body, errnum, shortmsg); sprintf(body, "%s<p>%s: %srn", body, longmsg, cause); sprintf(body, "%s<hr><em>The Tiny Web server</em>rn", body); /* Print the HTTP response */ sprintf(buf, "HTTP/1.0 %s %srn&quo
21、t;, errnum, shortmsg); Rio_writen(fd, buf, strlen(buf); sprintf(buf, "Content-type: text/htmlrn"); Rio_writen(fd, buf, strlen(buf); sprintf(buf, "Content-length: %drnrn", (int)strlen(body); Rio_writen(fd, buf, strlen(buf); Rio_writen(fd, body, strlen(body); (4)read_requesthd
22、rs 函數(shù)Tiny不使用請(qǐng)求報(bào)頭中的任何信息,僅僅調(diào)用 read_requesthdrs函數(shù)來讀取并忽略這些報(bào)頭。注意,終止請(qǐng)求報(bào)頭的空文本行是由 回車和換行符組成的,在第6行中檢查 1 void read_requesthdrs(rio_t *rp) 2 3 char bufMAXLINE; 4 5 Rio_readlineb(rp, buf, MAXLINE); 6 while(strcmp(buf, "rn") /line:netp:readhdrs:checkterm 7 Rio_readlineb(rp, buf, MAXLINE); 8 printf(
23、"%s", buf); 9 10 return;11 (5)parse_uri 函數(shù)Tiny假設(shè)靜態(tài)內(nèi)容的主目錄就是當(dāng)前目錄,可執(zhí)行文件的主目錄是 ./cgi-bin/ 任何包含字符串 cgi-bin 的URI都認(rèn)為是對(duì)動(dòng)態(tài)內(nèi)容的請(qǐng)求。首先將URI解析為一個(gè)文件名和一個(gè)可選的CGI參數(shù)字符串。如果請(qǐng)求的是靜態(tài)內(nèi)容(第5行),就清除CGI參數(shù)串(第6行),然后將URI轉(zhuǎn)換為一個(gè)相對(duì)的unix 路徑名,例如 ./index.html如果URI是用'/' 結(jié)尾的(第9行) ,我們就把默認(rèn)的文件名加在后面(第10行) 如果請(qǐng)求的是動(dòng)態(tài)
24、內(nèi)容(第13行),就會(huì)抽取所有的CGI參數(shù)(第14-20行),并將URI剩下的部分轉(zhuǎn)換為一個(gè)相應(yīng)的unix文件名(第21-22行) 1 int parse_uri(char *uri, char *filename, char *cgiargs) 2 3 char *ptr; 4 5 if (!strstr(uri, "cgi-bin") /* Static content */ /line:netp:parseuri:isstatic 6 strcpy(cgiargs, ""); /line:netp:parseuri:clearcgi 7 strcp
25、y(filename, "."); /line:netp:parseuri:beginconvert1 8 strcat(filename, uri); /line:netp:parseuri:endconvert1 9 if (uristrlen(uri)-1 = '/') /line:netp:parseuri:slashcheck10 strcat(filename, "home.html"); /line:netp:parseuri:appenddefault11 return 1;12 13 else /* Dynamic co
26、ntent */ /line:netp:parseuri:isdynamic14 ptr = index(uri, '?'); /line:netp:parseuri:beginextract15 if (ptr) 16 strcpy(cgiargs, ptr+1);17 *ptr = '0'18 19 else 20 strcpy(cgiargs, ""); /line:netp:parseuri:endextract21 strcpy(filename, "."); /line:netp:parseuri:beginc
27、onvert222 strcat(filename, uri); /line:netp:parseuri:endconvert223 return 0;24 25 (6)serve_static 函數(shù)Tiny提供四種不同的靜態(tài)內(nèi)容:HTML文件、無(wú)格式的文本文件、GIF編碼格式圖片、JPEG編碼格式圖片serve_static 函數(shù)發(fā)送一個(gè)HTTP響應(yīng),其主體包含一個(gè)本地文件的內(nèi)容。首先我們通過檢查文件名的后綴來判斷文件類型(第7行),并且發(fā)送響應(yīng)行和響應(yīng)報(bào)頭給客戶端(第8-12行)。注意用一個(gè)空行終止報(bào)頭第16行,我們使用 unix mmap函數(shù)將被請(qǐng)求文件映射到一個(gè)虛
28、擬問存儲(chǔ)器空間,調(diào)用mmap將文件srcfd的前filesize個(gè)字節(jié)映射到一個(gè)從地址srcp開始的私有只讀虛擬存儲(chǔ)器區(qū)域。一旦文件映射到存儲(chǔ)器,就不再需要它的描述符了,關(guān)閉這個(gè)文件(第17行)。第18行執(zhí)行的是到客戶端的實(shí)際文件傳動(dòng)。rio_writen 函數(shù)拷貝從srcp位置開始的filesize個(gè)字節(jié)(已經(jīng)被映射到了所請(qǐng)求的文件) 到客戶端的已連接描述符。第19行釋放了映射的虛擬存儲(chǔ)器區(qū)域,避免潛在的存儲(chǔ)器泄漏 1 void serve_static(int fd, char *filename, int filesize) 2 3 int srcfd; 4 char *sr
29、cp, filetypeMAXLINE, bufMAXBUF; 5 6 /* Send response headers to client */ 7 get_filetype(filename, filetype); /line:netp:servestatic:getfiletype 8 sprintf(buf, "HTTP/1.0 200 OKrn"); /line:netp:servestatic:beginserve 9 sprintf(buf, "%sServer: Tiny Web Serverrn", buf);10 sprintf(bu
30、f, "%sContent-length: %drn", buf, filesize);11 sprintf(buf, "%sContent-type: %srnrn", buf, filetype);12 Rio_writen(fd, buf, strlen(buf); /line:netp:servestatic:endserve13 14 /* Send response body to client */15 srcfd = Open(filename, O_RDONLY, 0); /line:netp:servestatic:open16 sr
31、cp = Mmap(0, filesize, PROT_READ, MAP_PRIVATE, srcfd, 0);/line:netp:servestatic:mmap17 Close(srcfd); /line:netp:servestatic:close18 Rio_writen(fd, srcp, filesize); /line:netp:servestatic:write19 Munmap(srcp, filesize); /line:netp:servestatic:munmap20 21 22 /*23 * get_filetype - derive file type from
32、 file name24 */25 void get_filetype(char *filename, char *filetype) 26 27 if (strstr(filename, ".html")28 strcpy(filetype, "text/html");29 else if (strstr(filename, ".gif")30 strcpy(filetype, "image/gif");31 else if (strstr(filename, ".jpg")32 strcpy(filetype, "image/jpeg");33 else34 strcpy(filetype, "text/plain");35 (6)serve
溫馨提示
- 1. 本站所有資源如無(wú)特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 紡織材料的創(chuàng)新使用與環(huán)境影響試題及答案
- 美工合同協(xié)議書
- 賣房合同協(xié)議書照片
- 勞動(dòng)合同和就業(yè)協(xié)議書
- 司機(jī)違章合同協(xié)議書
- 泵管租賃合同協(xié)議書范本
- 合同封頂協(xié)議書
- 簡(jiǎn)易承包協(xié)議書合同
- 房屋維護(hù)合同協(xié)議書
- 蛋糕合同協(xié)議書
- 2021-2022學(xué)年浙江省“9 1”高中聯(lián)盟高一年級(jí)下冊(cè)學(xué)期期中數(shù)學(xué)試題【含答案】
- 硬筆書法:幼小銜接識(shí)字寫字教學(xué)課件
- 盤扣支模架工程監(jiān)理細(xì)則
- 崇尚科學(xué)反邪教主題教育PPT反對(duì)邪教主題教育宣講課件
- smt首件檢驗(yàn)記錄表
- 大眾Formel-Q培訓(xùn)材料全解析含案例
- 莫扎特貝多芬肖邦英文簡(jiǎn)介課件
- 青島中瑞泰豐新材料有限公司2萬(wàn)噸無(wú)機(jī)環(huán)保新材料來料加工項(xiàng)目 環(huán)境影響報(bào)告書
- 《現(xiàn)代漢語(yǔ)詞匯》PPT課件(教學(xué))
- 編碼理論第3章
- 北京市46家種豬場(chǎng)地址及聯(lián)系方式
評(píng)論
0/150
提交評(píng)論