自己动手写php web server
最近在做一個web緩存系統,作為一個web緩存系統,肯定得有一個web服務器的功能,即實現簡單的http協議。于是,干脆自己動手寫一個php的web server。(注明:本文的源碼,可以在本人的資源分享里面下載)
在此之前,先介紹一下簡單http協議。
協議包含兩個重要內容。
?? ? ? ?Request格式:
HTTP請求行?
(請求)頭?
空行?
可選的消息體
典型例子:
?? ? ? ?GET /index.php HTTP/1.1?/r/n
Host: 192.168.2.166?/r/n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8?/r/n
Accept-Language: en-us,en;q=0.5?/r/n
Accept-Encoding: gzip,deflate?/r/n
Keep-Alive: 300?/r/n
Connection: keep-alive?/r/n
?? ? ? ?HTTP響應格式:?
(應答)頭?
空行?
可選的消息體
HTTP/1.1 200 OK?/r/n
Cache-Control: private, max-age=30?/r/n
Content-Type: text/html; charset=utf-8?/r/n
Content-Encoding: gzip?/r/n
Expires: Mon, 25 May 2009 03:20:33 GMT?/r/n
Last-Modified: Mon, 25 May 2009 03:20:03 GMT?/r/n
Server: Microsoft-IIS/7.0 /r/n
Date: Mon, 25 May 2009 03:20:02 GMT /r/n
Content-Length: 5 /r/n
/r/n
hello ? ? //這里是消息的真正內容
本文只是簡單介紹一下http請求頭和響應頭,具體的協議格式,請另行google。
Request是瀏覽器給我們的服務器發送的,也就是說,我們的服務器解析reques包,然后給予響應,就完成了一個簡單的http協議通信過程。當然,在處理請求之前,得簡歷socket鏈接。(別說socket不會建立啊)。
下面直接從接收客戶端請求開始。當瀏覽器跟服務器的80端口建立連接后,會發送request包。包格式前面介紹過。
if((client_fd = accept(sockfd, (struct sockaddr *)&client, &sin_size )) == -1)//接受客戶端請求{perror("accept() error.");exit(1); }memset(buf,0,MAX_BUF_SIZE);int len = recv(client_fd,buf,MAX_BUF_SIZE,0); //接收請求頭 string type=getType(buf,len);//獲取請求類型string req = getReq(buf,len);//獲取請求文件對象string arg=getArg(buf,len);//獲取參數
接收到req包后,就是處理了。主要有三步,不管什么請求,先獲取請求的類型。比 如瀏覽器訪問這個鏈接:http://192.168.2.166/a.php?a=10&b=10,請求頭第一行就是GET /a.php?a=10&b=10 HTTP/1.1 /r/n
拿到這個請求報文后,先解析出請求的類型,比如php類型的請求,png類型的,gif類型的等等類型的請求。例子中是php類型的請求。
然后獲取請求對象文件:a.php。最后獲取請求的參數:a=10 b=10
接下來,開始著手填充http響應頭:
if(type=="png")//不同的請求類型,對應不同的應答類型 PS:這里應該用數據驅動的,為了方便,就沒有用數據驅動了。contentType="image/png";else if(type=="jpeg" || type == "jpg")contentType = "image/jpeg";else if(type=="json" )contentType = "application/json";else if(type=="gif")contentType = "image/gif";else if(type=="html" || type=="htm")contentType="text/html";else if(type=="php")contentType = "text/html";else if(type=="js")contentType = "application/x-javascript";else if(type=="css")contentType = "text/css";else contentType = "text/html";memset(buf,0,MAX_BUF_SIZE);//封裝應答頭 時間等字段先不考慮sprintf(buf,"HTTP/1.1 200 OK /r/nDate: Sat, 14 May 2011 14:10:15 GMT/r/nExpires: Sat, 14 May 2011 14:14:45 GMT/r/nCache-Control: private, max-age=31536000/r/nX-Content-Type-Options: nosniff/r/nServer: sffe/r/nContent-Type: %s/r/n",contentType.c_str());
然后根據不同的類型,處理流程不一樣。如果是php請求,則調用系統調用system("php-cgi a.php a=10 b=10")(如果沒有裝php-cgi,ubuntu下面用apt-get install php5-cgi安裝)。如果不是php請求,則可以把所有的請求當成普通文件請求,即把文件的內容直接放到消息體里面。
?
?
?
if(type=="php")//單獨處理php請求{char cmd[1024]={0};cout<<"req:"<<req<<endl;//方便起見,直接用系統調用實現php解析。實際當中用的是fastcgi。//系統調用的實例:php-cgi a.php a=10 b=20//php解析后的結果存到temp文件中sprintf(cmd,"php-cgi %s %s >> temp",req.c_str(),arg.c_str());system(cmd);char phpContext[1024*1024]={0};FILE* f=fopen("temp","r");//從temp文件中獲取結果len = fread(phpContext,1,MAX_BUF_SIZE,f);fclose(f);//處理請求結果char* content = dealPHPMes(phpContext,len);//向http頭追加內容長度int lena= sprintf(buf+bufLen,"Content-Length: %d/r/n",strlen(content));bufLen +=lena;?
其中,dealPHPMes函數就是處理php-cgi的響應。php-cgi的響應也是一個標準的http響應,有頭有內容。通過查找/r/n/r/n就可以找到頭和內容的分界。
?
//返回 PHP-cgi消息的頭和內容的分界點 //php-cgi處理php的返回消息格式如下 //head /r/n ///r/n //content /r/n char* dealPHPMes(char* data,int len) {char* p = data;char* end = p+len-3;for(;p<end;p++)//兩個/r/n的地方就是頭和內容的分界{if(*p=='/r' && *(p+1) =='/n' && *(p+2)=='/r' && *(p+3)=='/n')return p+4;}return data; }
如果瀏覽器請求的是普通的請求,即非php請求,比如png請求,css請求,對于這些請求,只要把這些文件發送給瀏覽器即可。
?
else //非php請求都當成普通文件處理{FILE* f=fopen(req.c_str(),"rb");//打開請求的文件if(f==NULL){string defPage="<html><b><center>404 not find!</center></b></html>";int templen = sprintf(buf+strlen(buf),"Content-Length: %d/r/n/r/n%s",defPage.size(),defPage.c_str());bufLen += templen;}else{int lena=strlen(buf);char temp[MAX_BUF_SIZE]={0};int fl = fread(temp,1,MAX_BUF_SIZE,f);//讀取文件數據fclose(f);//向http頭追加內容長度int lenb=sprintf(buf+lena,"Content-Length: %d/r/n/r/n",fl);bufLen +=lenb;//添加內容 注意:這里不能用sprintf 因為文件內容不能保證都是合法字符。比如png圖片。memcpy(buf+bufLen,temp,fl);bufLen +=fl;} }?
最后調用socket的send函數,把上面的buf里面的內容全部發送給瀏覽器,瀏覽器就可以正常顯示了。
到這里,一個簡單的php web server已經實現。
另外提供源碼下載地址:http://d.download.csdn.net/down/3288415/semillon
已經實現的功能有:
1.php支持
2.js css png jpg等靜態文件的支持。
3.GET POST兩種請求方式的支持。
4.目錄的支持
5.示例文件中a.php實現一個簡單的基于php的計算器。
?
總結
以上是生活随笔為你收集整理的自己动手写php web server的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ArrayList的去重、排序以及其他应
- 下一篇: qq登录界面php修改法,怎么修改qq登