网络 http服务器-v1-多线程版本
?????????????http服務器-v1-多線程版本
一、http協議分析
? ?? ? ?來源:http://www.cnblogs.com/li0803/archive/2008/11/03/1324746.html
???? ??引言? ?
????????HTTP是一個屬于應用層的面向對象的協議,由于其簡捷、快速的方式,適用于分布式超媒體信息系統。它于1990年提出,經過幾年(http/0.9 http/1.0)的使用與發展,得到不斷地完善和擴展。目前在WWW中HTTP/1.1已經廣泛使用,而且HTTP-NG(Next Generation of HTTP)的建議已經提出。
HTTP協議的主要特點可概括如下:
????????1.支持客戶/服務器模式。
????????2.簡單快速:客戶向服務器請求服務時,只需傳送請求方法和路徑。請求方法常用的有GET、HEAD、POST。每種方法規定了客戶與服務器聯系的類型不同。由于HTTP協議簡單,使得HTTP服務器的程序規模小,因而通信速度很快。
????????3.靈活:HTTP允許傳輸任意類型的數據對象。正在傳輸的類型由Content-Type加以標記。
????????4.無連接:無連接的含義是限制每次連接只處理一個請求。服務器處理完客戶的請求,并收到客戶的應答后,即斷開連接。采用這種方式可以節省傳輸時間。
????????5.無狀態:HTTP協議是無狀態協議。無狀態是指協議對于事務處理沒有記憶能力。缺少狀態意味著如果后續處理需要前面的信息,則它必須重傳,這樣可能導致每次連接傳送的數據量增大。另一方面,在服務器不需要先前信息時它的應答就較快。
? ? ? ?1.HTTP協議詳解之URL篇
??? ????????????http(超文本傳輸協議)是一個基于請求與響應模式的、無狀態的、應用層的協議,?;赥CP的連接方式,HTTP1.1版本中給出一種持續連接的機制,絕大多數的Web開發,都是構建在HTTP協議之上的Web應用。
HTTP URL (URL是一種特殊類型的URI,包含了用于查找某個資源的足夠的信息)的格式如下:
????????????????http://host[":"port][abs_path]
????????????????http表示要通過HTTP協議來定位網絡資源;host表示合法的Internet主機域名或者IP地址;port指定一個端口號,為空則使用缺省端口80;abs_path指定請求資源的URI;如果URL中沒有給出abs_path,那么當它作為請求URI時,必須以“/”的形式給出,通常這個工作瀏覽器自動幫我們完成。
eg:
????????????????1、輸入:www.guet.edu.cn?
????????????????瀏覽器自動轉換成:http://www.guet.edu.cn/
????????????????2、http:192.168.0.116:8080/index.jsp?(用到DNS域名解析協議)
????????2.HTTP協議詳解之請求篇?
????????????????http請求由三部分組成,分別是:請求行、消息報頭、請求正文
????????????????1、請求行以一個方法符號開頭,以空格分開,后面跟著請求的URI和協議的版本,格式如下:Method Request-URI HTTP-Version CRLF??
????????????????其中 Method表示請求方法;Request-URI是一個統一資源標識符;HTTP-Version表示請求的HTTP協議版本;CRLF表示回車和換行(除了作為結尾的CRLF外,不允許出現單獨的CR或LF字符)。
????????????????請求方法(所有方法全為大寫)有多種,各個方法的解釋如下:
????????????????GET???? 請求獲取Request-URI所標識的資源
????????????????POST??? 在Request-URI所標識的資源后附加新的數據
????????????????HEAD??? 請求獲取由Request-URI所標識的資源的響應消息報頭
????????????????PUT???? 請求服務器存儲一個資源,并用Request-URI作為其標識
????????????????DELETE? 請求服務器刪除Request-URI所標識的資源
????????????????TRACE?? 請求服務器回送收到的請求信息,主要用于測試或診斷
????????????????CONNECT 保留將來使用
????????????????OPTIONS 請求查詢服務器的性能,或者查詢與資源相關的選項和需求
????????????????應用舉例:
????????????????GET方法:在瀏覽器的地址欄中輸入網址的方式訪問網頁時,瀏覽器采用GET方法向服務器獲取資源,eg:GET ????????????????????/form.html HTTP/1.1 (CRLF)
????????????????POST方法要求被請求服務器接受附在請求后面的數據,常用于提交表單。
????????????????eg:POST /reg.jsp HTTP/ (CRLF)
????????????????Accept:p_w_picpath/gif,p_w_picpath/x-xbit,... (CRLF)
????????????????...
????????????????HOST:www.guet.edu.cn (CRLF)
????????????????Content-Length:22 (CRLF)
????????????????Connection:Keep-Alive (CRLF)
????????????????Cache-Control:no-cache (CRLF)
????????????????(CRLF)???????? //空行 ?該CRLF表示消息報頭已經結束,在此之前為消息報頭
????????????????user=jeffrey&pwd=1234? //此行以下為提交的數據
????????????????HEAD方法與GET方法幾乎是一樣的,對于HEAD請求的回應部分來說,它的HTTP頭部中包含的信息與通過GET請求所得到的信息是相同的。利用這個方法,不必傳輸整個資源內容,就可以得到Request-URI所標識的資源的信息。該方法常用于測試超鏈接的有效性,是否可以訪問,以及最近是否更新。
????????????????2、請求報頭后述
????????????????3、請求正文(略)?
????????3.HTTP協議詳解之響應篇
??????????????? 在接收和解釋請求消息后,服務器返回一個HTTP響應消息。
????????????????HTTP響應也是由三個部分組成,分別是:狀態行、消息報頭、響應正文
????????????????1、狀態行格式如下:
????????????????HTTP-Version Status-Code Reason-Phrase CRLF
????????????????其中,HTTP-Version表示服務器HTTP協議的版本;Status-Code表示服務器發回的響應狀態代碼;Reason-Phrase表示狀態代碼的文本描述。
????????????????狀態代碼有三位數字組成,第一個數字定義了響應的類別,且有五種可能取值:
????????????????1xx:指示信息--表示請求已接收,繼續處理
????????????????2xx:成功--表示請求已被成功接收、理解、接受
????????????????3xx:重定向--要完成請求必須進行更進一步的操作
????????????????4xx:客戶端錯誤--請求有語法錯誤或請求無法實現
????????????????5xx:服務器端錯誤--服務器未能實現合法的請求
????????????????常見狀態代碼、狀態描述、說明:
????????????????200 OK????? //客戶端請求成功
????????????????400 Bad Request? //客戶端請求有語法錯誤,不能被服務器所理解
????????????????401 Unauthorized //請求未經授權,這個狀態代碼必須和WWW-Authenticate報頭域一起使用?
????????????????403 Forbidden? //服務器收到請求,但是拒絕提供服務
????????????????404 Not Found? //請求資源不存在,eg:輸入了錯誤的URL
????????????????500 Internal Server Error //服務器發生不可預期的錯誤
????????????????503 Server Unavailable? //服務器當前不能處理客戶端的請求,一段時間后可能恢復正常
????????????????eg:HTTP/1.1 200 OK (CRLF)
????????????????2、響應報頭后述
????????????????3、響應正文就是服務器返回的資源的內容?
????????????4.HTTP協議詳解之消息報頭篇
??????????????? HTTP消息由客戶端到服務器的請求和服務器到客戶端的響應組成。請求消息和響應消息都是由開始行(對于請求消息,開始行就是請求行,對于響應消息,開始行就是狀態行),消息報頭(可選),空行(只有CRLF的行),消息正文(可選)組成。
????????????????HTTP消息報頭包括普通報頭、請求報頭、響應報頭、實體報頭。
????????????????每一個報頭域都是由名字+“:”+空格+值 組成,消息報頭域的名字是大小寫無關的。
???1、普通報頭
????在普通報頭中,有少數報頭域用于所有的請求和響應消息,但并不用于被傳輸的實體,只用于傳輸的消息。
????eg:
????Cache-Control?? 用于指定緩存指令,緩存指令是單向的(響應中出現的緩存指令在請求中未必會出現),且是獨立的(一個消息的緩存指令不會影響另一個消息處理的緩存機制),HTTP1.0使用的類似的報頭域為Pragma。
????請求時的緩存指令包括:no-cache(用于指示請求或響應消息不能緩存)、no-store、max-age、max-stale、min-fresh、only-if-cached;
????響應時的緩存指令包括:public、private、no-cache、no-store、no-transform、must-revalidate、proxy-revalidate、max-age、s-maxage.
????eg:為了指示IE瀏覽器(客戶端)不要緩存頁面,服務器端的JSP程序可以編寫下:????????response.sehHeader("Cache-Control","no-cache");
????//response.setHeader("Pragma","no-cache");作用相當于上述代碼,通常兩者//合用
這句代碼將在發送的響應消息中設置普通報頭域:Cache-Control:no-cache
????????????????????Date普通報頭域表示消息產生的日期和時間
????????????????????Connection普通報頭域允許發送指定連接的選項。例如指定連接是連續,或者指定“close”選項,通知服務器,在響應完成后,關閉連接
???2、請求報頭
????請求報頭允許客戶端向服務器端傳遞請求的附加信息以及客戶端自身的信息。
????常用的請求報頭
????Accept
????Accept請求報頭域用于指定客戶端接受哪些類型的信息。eg:Accept:p_w_picpath/gif,表明客戶端希望接受GIF圖象格式的資源;Accept:text/html,表明客戶端希望接受html文本。
????Accept-Charset
????Accept-Charset請求報頭域用于指定客戶端接受的字符集。eg:Accept-Charset:iso-8859-1,gb2312.如果在請求消息中沒有設置這個域,缺省是任何字符集都可以接受。
? ? ?Accept-Encoding
? ? ?Accept-Encoding請求報頭域類似于Accept,但是它是用于指定可接受的內容編碼。eg:Accept-Encoding:gzip.deflate.如果請求消息中沒有設置這個域服務器假定客戶端對各種內容編碼都可以接受。
????Accept-Language
????Accept-Language請求報頭域類似于Accept,但是它是用于指定一種自然語言。eg:Accept-Language:zh-cn.如果請求消息中沒有設置這個報頭域,服務器假定客戶端對各種語言都可以接受。
????Authorization
????Authorization請求報頭域主要用于證明客戶端有權查看某個資源。當瀏覽器訪問一個頁面時,如果收到服務器的響應代碼為401(未授權),可以發送一個包含Authorization請求報頭域的請求,要求服務器對其進行驗證。
????Host(發送請求時,該報頭域是必需的)
????Host請求報頭域主要用于指定被請求資源的Internet主機和端口號,它通常從HTTP URL中提取出來的,eg:
????我們在瀏覽器中輸入:http://www.guet.edu.cn/index.html
????瀏覽器發送的請求消息中,就會包含Host請求報頭域,如下:
????Host:www.guet.edu.cn
????此處使用缺省端口號80,若指定了端口號,則變成:Host:www.guet.edu.cn:指定端口號
????User-Agent
????我們上網登陸論壇的時候,往往會看到一些歡迎信息,其中列出了你的操作系統的名稱和版本,你所使用的瀏覽器的名稱和版本,這往往讓很多人感到很神奇,實際上,服務器應用程序就是從User-Agent這個請求報頭域中獲取到這些信息。User-Agent請求報頭域允許客戶端將它的操作系統、瀏覽器和其它屬性告訴服務器。不過,這個報頭域不是必需的,如果我們自己編寫一個瀏覽器,不使用User-Agent請求報頭域,那么服務器端就無法得知我們的信息了。
????請求報頭舉例:
????GET /form.html HTTP/1.1 (CRLF)
????Accept:p_w_picpath/gif,p_w_picpath/x-xbitmap,p_w_picpath/jpeg,application/x-shockwave-flash,application/vnd.ms-excel,application/vnd.ms-powerpoint,application/msword,*/* (CRLF)
????Accept-Language:zh-cn (CRLF)
????Accept-Encoding:gzip,deflate (CRLF)
????If-Modified-Since:Wed,05 Jan 2007 11:21:25 GMT (CRLF)
????If-None-Match:W/"80b1a4c018f3c41:8317" (CRLF)
????User-Agent:Mozilla/4.0(compatible;MSIE6.0;Windows NT 5.0) (CRLF)
????Host:www.guet.edu.cn (CRLF)
????Connection:Keep-Alive (CRLF)
????(CRLF)(空行)
????????????????3、響應報頭
????????????????????響應報頭允許服務器傳遞不能放在狀態行中的附加響應信息,以及關于服務器的信息和對Request-URI所標識的資源進行下一步訪問的信息。
????????????????????常用的響應報頭
????????????????????Location
????????????????????Location響應報頭域用于重定向接受者到一個新的位置。Location響應報頭域常用在更換域名的時候。
????????????????????Server
????????????????????Server響應報頭域包含了服務器用來處理請求的軟件信息。與User-Agent請求報頭域是相對應的。下面是
Server響應報頭域的一個例子:
????????????????????Server:Apache-Coyote/1.1
????????????????????WWW-Authenticate
????????????????????WWW-Authenticate響應報頭域必須被包含在401(未授權的)響應消息中,客戶端收到401響應消息時候,并發送Authorization報頭域請求服務器對其進行驗證時,服務端響應報頭就包含該報頭域。
????????????????????eg:WWW-Authenticate:Basic realm="Basic Auth Test!"? //可以看出服務器對請求資源采用的是基本驗證機制。
???4、實體報頭
????請求和響應消息都可以傳送一個實體。一個實體由實體報頭域和實體正文組成,但并不是說實體報頭域和實體正文要在一起發送,可以只發送實體報頭域。實體報頭定義了關于實體正文(eg:有無實體正文)和請求所標識的資源的元信息。
????常用的實體報頭
????Content-Encoding
????Content-Encoding實體報頭域被用作媒體類型的修飾符,它的值指示了已經被應用到實體正文的附加內容的編碼,因而要獲得Content-Type報頭域中所引用的媒體類型,必須采用相應的解碼機制。Content-Encoding這樣用于記錄文檔的壓縮方法,eg:Content-Encoding:gzip
????Content-Language
????Content-Language實體報頭域描述了資源所用的自然語言。沒有設置該域則認為實體內容將提供給所有的語言閱讀者。
????eg:Content-Language:da
????Content-Length
????Content-Length實體報頭域用于指明實體正文的長度,以字節方式存儲的十進制數字來表示。
????Content-Type
????Content-Type實體報頭域用語指明發送給接收者的實體正文的媒體類型。
????eg:Content-Type:text/html;charset=ISO-8859-1
????Content-Type:text/html;charset=GB2312
????Last-Modified
????Last-Modified實體報頭域用于指示資源的最后修改日期和時間。
????Expires
????Expires實體報頭域給出響應過期的日期和時間。為了讓代理服務器或瀏覽器在一段時間以后更新緩存中(再次訪問曾訪問過的頁面時,直接從緩存中加載,縮短響應時間和降低服務器負載)的頁面,我們可以使用Expires實體報頭域指定頁面過期的時間。
????eg:Expires:Thu,15 Sep 2006 16:23:12 GMT
????HTTP1.1的客戶端和緩存必須將其他非法的日期格式(包括0)看作已經過期。
????eg:為了讓瀏覽器不要緩存頁面,我們也可以利用Expires實體報頭域,設置為0,jsp中程序如下:????????????
????response.setDateHeader("Expires","0");
二、http服務器的實現邏輯
????????1.實現基于 tcp 協議的服務器端(創建監聽套接字 ?bind; listen )
????????注:http協議屬于應用層協議,其功能的實現依賴于下層結構提供的服務。它的無連接特性決定了它的下層必須保證 單次交互的可靠性(否則 ?本來就一次性,還不知道可靠不可靠(獲取的數據是否正確有序),誰用?)。而tcp協議剛好提供了這種可靠性。但是tcp協議是面向連接的,因此 ,在后續的邏輯中,在一次交互完成后,需要關閉連接。
?????????????????2.主線程循環 accept 建立連接,成功后創建子線程處理后續邏輯。主線程繼續accept
????????3.子線程 處理:
????????????1>獲取請求行 分別 截取 method ?url ?version ,在此要通過判斷是不是\r\n 表示一行結尾。用到recv函數中的MSG_PEEK方法
????????????
??????????????? 2>對正確的 請求行 進行處理
????????????????CGI:絕大多數的CGI程序被用來解釋處理來自表單的輸入信息,并在服務器產生相應的處理,或將相應的信息反饋給瀏覽器。CGI程序使網頁具有交互功能。
????????????????????(1)get方法無參數(非 CGI) 回復 請求資源。(在此用sendfile函數提高效率)
????????????????????(2)get方法有參數(url中含‘?’之后為參數(從含表單的網頁頁面獲取)),先將參數作為環境變量。fork子進程(子進程共享父進程的環境變量)。子進程 程序 替換 為 真實的 ?可執行程序 (‘?’之前的url) 對參數做處理得到執行結果,通過管道 傳給 父進程,父進程對數據做差錯檢測后,發送給客戶端。
????????????????????注:將參數作為環境變量 使用:
????????????????????????????putenv(char*);上傳環境變量
????????????????????????????getenv (char*);獲取環境變量
????????????????????(3)post方法 一定含有參數。參數的有效長度包含在消息報頭中
????????????????? ???例:Content-Length:22 (CRLF)
?????????????????????將其作為換境變量,fork子進程。子進程exec 為 請求的可執行程序,通過管道 在sock中的請求正文中讀取給定數量的字符,并處理。將處理結果通過管道發送給父進程,父進程 檢測后 ?發送給客戶端
????????????????CGI處理邏輯圖:
????????????? ? 3>? 清理sock的緩存。并清理子線程的資源,關閉sock。子線程退出。 ?
三、關鍵 程序實例
//主程序,包含tcp?服務端的bind?listen#include?<stdio.h> #include?<netinet/in.h> #include?<arpa/inet.h> #include?<sys/types.h> #include?<sys/socket.h> #include?<unistd.h> #include?<string.h> #include?<stdlib.h> #include?<errno.h> #include?<fcntl.h> #include?<pthread.h> #include?"httpd.h" #include?<netdb.h> #include?<sys/utsname.h> #include?<net/if.h> #include?<sys/ioctl.h> #define?ERRORIP?-1int?startup(char*?ip,int?port) { // printf("%s?%d\n",ip,port);int?sock=socket(AF_INET,SOCK_STREAM,0);struct?sockaddr_in?server;server.sin_family=AF_INET;server.sin_addr.s_addr=inet_addr(ip);server.sin_port=htons(port);int?flag=1;if(?setsockopt(sock,?SOL_SOCKET,?SO_REUSEADDR,?&flag,?sizeof(flag))?==?-1)??{??perror("setsockopt");??exit(1);?? ????}??if(bind(sock,(struct?sockaddr?*)&server,sizeof(server))<0){perror("bind");exit(-2);}if(listen(sock,5)<0){perror("listen");exit(-3);}//printf("%d\n",sock);return?sock; }void?usage(char*?arg) {printf("usage?%s?[ip]?[port]\n",arg);}void?http_start(int?listen_sock){int?done=0;int?client_sock=-1; // printf("http_start\n");struct?sockaddr_in?client;socklen_t?len=sizeof(client);while(!done){ // printf("循環\n");if((client_sock=accept(listen_sock,(struct?sockaddr?*)&client,&len))<0){printf("connect?error!\n");//做訪問記錄?存入數據庫continue;}pthread_t?tid; // printf("client:%d\n",client_sock);int?ret=pthread_create(&tid,NULL,http_action,(void*)client_sock);if(ret!=0){// printf("create?thread?error!\n");printf("%s\n",strerror(errno));continue;}else{pthread_detach(tid);}}}int?main(int?argc,char*?argv[]){if(argc!=3){usage(argv[0]);exit(-1);}int?listen_sock=0;char*?ip=NULL;int?port=atoi(argv[2]); if(strcmp(argv[1],"any")==0){int?sfd,?intr;struct?ifreq?buf[16];struct?ifconf?ifc;sfd?=?socket?(AF_INET,?SOCK_DGRAM,?0);?if?(sfd?<?0)return?ERRORIP;ifc.ifc_len?=?sizeof(buf);ifc.ifc_buf?=?(caddr_t)buf;if?(ioctl(sfd,?SIOCGIFCONF,?(char?*)&ifc))return?ERRORIP;intr?=?ifc.ifc_len?/?sizeof(struct?ifreq);while?(intr--?>?0?&&?ioctl(sfd,?SIOCGIFADDR,?(char?*)&buf[intr]));close(sfd);ip=?inet_ntoa(((struct?sockaddr_in*)(&buf[intr].ifr_addr))->?sin_addr);printf("%s\n",ip); listen_sock=startup(ip,port);}elselisten_sock=startup(argv[1],port); // printf("%d\n",listen_sock);http_start(listen_sock);close(listen_sock);return?0; }//httpd.h#include?<string.h> #include?<stdlib.h> #include?<errno.h> #include?<fcntl.h> #include?<pthread.h> #include?<sys/stat.h>#define?_SIZE_?1024char*?get_text(int?fd,char*?buf); void?error_all(int?fd,int?err,char*?reason); void?cgi_action(int?fd,char*?method,char*?url,char*?parameter);char*?get_length(int?fd,char?*?content_length);void*?http_action(void*?client_sock);void?echo_error(int?fd,int?_errno);void?echo_html(int?fd,const?char*?url,int?size?);#endif//httpd.c?包括后續非?exec?邏輯#include?"httpd.h"#define?DEFAULT?"src_html/default.html" #define?IMG?"src_html" #define?CGI?"src_cgi"int?times=0; void?echo_error(int?fd,int?_errno) {switch(_errno){case?400://Bad?Request??//客戶端請求有語法錯誤,不能被服務器所理解break;case?404:請求資源不存在,eg:輸入了錯誤的URL//printf("*******404\n");error_all(fd,404,"NOT_FIND!!");break;case?401://請求未經授權,這個狀態代碼必須和WWW-Authenticate報頭域一起使用?break;case?403://服務器收到請求,但是拒絕提供服務?break;case?500://?Internal?Server?Error?//服務器發生不可預期的錯誤break;case?503://Server?Unavailable??//服務器當前不能處理客戶端的請求,一段時間后可能恢復正常break;default:break;}close(fd); }void?error_all(int?fd,int?err,char*?reason) { char?buf[_SIZE_]="";char?error[_SIZE_]="";sprintf(buf,"HTTP/1.1?%d?%s\r\n\r\n",err,reason);sprintf(error,"?%d?%s\n",err,reason); // printf("err?buf:%s\n",buf);write(fd,buf,strlen(buf));write(fd,"<html>\n",strlen("<html>\n"));write(fd,"<head>\n",strlen("<head>\n"));write(fd,"<h1>?HELLO?PPH!!!</h1>\n",strlen("<h1>?HELLO?PPH!!!</h1>\n"));write(fd,"<h2>\n",strlen("<h2>\n"));write(fd,error,strlen(error));write(fd,"</h2>\n",strlen("</h2>\n"));write(fd,"</head>\n",strlen("</head>\n"));write(fd,"</html>\n",strlen("</html>\n")); // echo_html(fd,"src_html/1.html",102400000); }int?get_line(int?sock_fd,char?*?line){int?index=0;ssize_t?size=1;char?ch=0;while(ch!='\n'){if((size=read(sock_fd,&ch,1))<0){perror("read_get_line");return?-1;}if(ch=='\r'){char?tmp=0;if(recv(sock_fd,&tmp,1,MSG_PEEK)>0){if(tmp=='\n'){line[index++]=tmp;read(sock_fd,&ch,1);continue;}}}if(index==1024){line[1023]=0;return?-1;}line[index++]=ch;}line[index]=0;printf("line?:%d,%d,%d,%s",index,strlen(line),line[0],line);if(strcmp(line,"\n")==0){ // printf("*****************");return?0;}return?1; }void?echo_html(int?fd,const?char*?url,int?fd_size?){ char?buf[_SIZE_]="HTTP/1.1?200?OK\r\n\r\n";if(url==NULL){int?rfd=-1;off_t?set=0;ssize_t?size=0;if((rfd=open(DEFAULT,?O_RDONLY))<0){echo_error(fd,500);exit(0);} // printf("%d?%d\n",fd,rfd);write(fd,buf,strlen(buf));size=sendfile(fd,rfd,&set,fd_size);if(size<0){close(rfd);printf("senfile?error!\n");return;}printf("size:?%d\n",size);close(rfd);}else{int?rfd=-1;off_t?set=0;ssize_t?size=0;//printf("url:%s\n",url);if((rfd=open(url,?O_RDONLY))<0){echo_error(fd,500);// exit(0);}write(fd,buf,strlen(buf));size=sendfile(fd,rfd,NULL,fd_size);if(size<0){printf("senfile?error!\n");return;}// printf("size:?%d\n",size);close(rfd);return; } } //清除client_sockint??clear_buf(int?fd) {char?buf[_SIZE_]="";ssize_t?size=1;size=read(fd,buf,_SIZE_);if(size<0){perror("clear_buf_read");return?-1;}return?0; }//獲取post正文?參數 char*?get_length(int?fd,char*?content_length) {int?size=1;int?tag=0;int?index=0;while(size!=0){//通過持續讀取一行?直到讀到空行結束size=get_line(fd,content_length);if(strncasecmp(content_length,"content-length:?",16)==0){break;}if(size==-1){printf("get?line出錯\n");return?NULL;}}content_length[strlen(content_length)-1]=0;strcpy(content_length,content_length+16);printf("con?end:?%s\n",content_length);return?content_length; }void?cgi_action(int?fd,char*?method,char*?url?,char*?parameter) {char?env[20]="METHOD=";char?par[_SIZE_]="PARAMETER=";int?pwrite[2];if((pipe(pwrite)<0)){perror("pipe");return;}strcat(env,method);strcat(par,parameter);printf("?act?url:%s\n",url);printf("parameter:%s\n",par); if(putenv(env)<0){perror("putenv");return;}if(putenv(par)<0){perror("putenv?par");return;} // printf("fork?qian\n");pid_t?id=fork();if(id<0){perror("fork");return;}else?if(id==0){//子進程close(pwrite[0]);//printf("child\n");if(dup2(pwrite[1],1)<0){perror("dup2.1");return;}if(dup2(fd,0)<0){perror("dup2.2");return;}if(execl(url,NULL)<0){perror("execl");printf("exit?url:\n",url);exit(-2);}}else{//父進程printf("father?start\n");close(pwrite[1]);char?buf[_SIZE_]="";int?count=0;int?i=0;ssize_t?size=1;while(size>0){size=read(pwrite[0],buf,_SIZE_);if(size<0){echo_error(fd,500);break;}if(size==0)break;write(fd,buf,strlen(buf));printf("if?end?\n");}waitpid(0,NULL,0);close(pwrite[0]);} }void*?http_action(void?*?client_sock){ int?fd=(int)client_sock;struct?stat?stat_buf;?char?line[_SIZE_];char*?method=NULL;char*?version=NULL;char*?url=NULL;char*?parameter=NULL;//get方法?參數char??text[_SIZE_]="";int?cgi=0;if(get_line(fd,line)==-1){printf("this?is?a?cache?requset?,?so?can't?process!\n");return?NULL;}int?index=strlen(line)-1;//printf("%d??\n",index);//GET?/?HTTP/1.1printf("%d\n?index?",index);while(index>0){//提取method?urlif(line[index]=='?'&&version==NULL){version=((char*)line)+index+1;line[index]=0;}if(line[index]=='?'&&url==NULL){url=line+index+1;line[index]=0;}--index;}method=line;char?real_url[128]=CGI;//判斷請求方法if(strcasecmp("POST",method)==0){printf("real_url?:%s\n",real_url);strcat(real_url,url);printf("start:%s?%s?%s",method,url?,version); get_length(fd,text);cgi_action(fd,method,real_url,text);printf("fork?...end!\n");}else?if(strcasecmp("GET",method)==0){index=0;while(url[index]){if(url[index]=='?'){cgi=1;parameter=url+index+1;url[index]=0;printf("?get?url:%s\n",url);break;}++index;}if(cgi==1){//cgi;strcat(real_url,url);printf("start:%s?%s?%s",method,url?,version); cgi_action(fd,method,real_url,parameter);printf("ret?cgi\n");}else{if(strcmp("/",url)==0){if(stat(DEFAULT,?&stat_buf)<0){echo_error(fd,500);close(fd);return?;}echo_html(fd,?NULL,stat_buf.st_size);}else{char?real_url[128]="src_html";strcat(real_url,url);printf("real_url?:%s\n",real_url);if(stat(real_url,?&stat_buf)<0){ echo_error(fd,404);//printf("times:%d?end!\n",times++);//close(fd);//return;}else{printf("fd?size:%d",stat_buf.st_size);echo_html(fd,real_url,stat_buf.st_size);}}}}else{echo_error(fd,403);}if(strcasecmp(method,"POST")==0){printf("fork?...end!\n");close(fd);printf("times:%d?end!\n",times++);return;}if(clear_buf(fd)<0){printf("clear?error\n");}elseprintf("clear?success!\n");printf("times:%d?end!\n",times++);close(fd);return; }四、運行結果截圖
轉載于:https://blog.51cto.com/shaungqiran/1792495
總結
以上是生活随笔為你收集整理的网络 http服务器-v1-多线程版本的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: iOS 限制输入字数完美解决方案
- 下一篇: maven编译不通过:软件包com.su