首页 > 代码库 > Socket网络编程--简单Web服务器(5)

Socket网络编程--简单Web服务器(5)

  这一小节我们将实现服务器对get和post的请求进行对cgi程序的调用。对于web服务器以前的章节已经实现了对get和post请求的调用接口,接下来给出对应接口的实现。

 1 int WebServer::ServerGetFunction(int cli_fd,char *path,char *args) 2 { 3     ServerExecuteCGI(cli_fd,path,args); 4     return 0; 5 } 6 int WebServer::ServerPostFunction(int cli_fd,char *path,char *args) 7 { 8     ServerExecuteCGI(cli_fd,path,args); 9     return 0;10 }11 12 int WebServer::ServerExecuteCGI(int cli_fd,char *path,char *args)13 {14     char query_env[1024];15     char type[16]="text/html";16     pid_t pid;17     int status;18     int cgi_output[2];19     int cgi_input[2];20 21     if(pipe(cgi_output)<0)22     {23         Page_500(cli_fd);24         return 0;25     }26     if(pipe(cgi_input)<0)27     {28         Page_500(cli_fd);29         return 0;30     }31 32     if((pid=fork())<0)33     {34         Page_500(cli_fd);35         return 0;36     }37     if(pid==0)//child38     {39         dup2(cgi_output[1],1);//cgi的输出端绑定文件描述符为1的输出端40         dup2(cgi_input[0],0);41         close(cgi_output[0]);42         close(cgi_input[1]);43         sprintf(query_env,"QUERY_STRING=%s",args);44         putenv(query_env);45         execl(path,path,args);46         exit(0);47     }48     else //parent49     {50         char c;51         close(cgi_output[1]);//取消绑定52         close(cgi_input[0]);53         Page_Headers(cli_fd,type,0);54         while(read(cgi_output[0],&c,1)>0)55             send(cli_fd,&c,1,0);56         close(cgi_output[0]);57         close(cgi_input[1]);58     }59     waitpid(pid,&status,0);60     return 0;61 }

  然后我们写一个hello.c的文件然后编译成hello可执行文件(要保证权限是可执行的)

 1 #include <stdio.h> 2 #include <stdlib.h> 3  4 int main(int argc,char **args) 5 { 6     char *data; 7     printf("Hello\n"); 8     printf("%s:%s\n",args[0],args[1]); 9     data=http://www.mamicode.com/getenv("QUERY_STRING");10     printf("query_string::%s\n",data);11     return 0;12 }

  然后在浏览器输入以下网址,然后查看执行结果

  成功的执行c程序了。下面就做一个完整的例子,做一个用于登录的例子。

 1 <html> 2     <head> 3         <title>Test</title> 4         <meta http-equiv="Content-Type" content="text/html ; charset=utf-8"> 5         <link rel="stylesheet" href="style.css" type="text/css"/> 6         <script language="javascript" src="javascript.js"></script> 7     </head> 8  9     <body>10         <div class="ceshi">图片</div><img src="ab.jpg"></img>11         <input name="button" type="button" value="Click!" onclick=hi();></input>12 13         <hr>14         <br>使用post方式<br>15         <form method="post" name="frm1" action="hello">16             <label>用户名:</label>17             <input type="text" name="username" />18             <br>19             <label>密码:</label>20             <input type="password" name="password" />21             <br>22             <input type="submit" name="commit" value="登陆"/>23             <br>24         </form>25         <hr>26         <br>使用get方式<br>27         <form method="get" name="frm1" action="hello">28             <label>用户名:</label>29             <input type="text" name="username" />30             <br>31             <label>密码:</label>32             <input type="password" name="password" />33             <br>34             <input type="submit" name="commit" value="登陆"/>35             <br>36         </form>37     </body>38 </html>

  然后在当前目录下有个hello.c程序

 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4  5 int split(char **arr,char *str,const char*del) 6 { 7     char *s=NULL; 8     int i=0; 9     s=strtok(str,del);10     while(s!=NULL)11     {12         *arr++=s;13         s=strtok(NULL,del);14         i++;15     }16     return i;17 }18 19 void split_key(char *ch,char *key,char *value)20 {21     int len;22     int i;23     int j;24     len=strlen(ch);25     j=0;26     for(i=0;i<len;i++)27     {28         if(ch[i]===)29         {30             i++;31             break;32         }33         key[j]=ch[i];34         j++;35     }36     key[j]=0;37     j=0;38     for(;i<len;i++)39     {40         value[j]=ch[i];41         j++;42     }43     value[j]=0;44     return ;45 }46 47 int main(int argc,char **args)48 {49     char *data;50     char *myargs[32];51     int cnt=0;52     int i;53     char key[32],value[32];54     char username[32],password[32];55     memset(myargs,0,sizeof(myargs));56     cnt=split(myargs,args[1],"&");57 58     for(i=0;i<cnt;i++)59     {60         split_key(myargs[i],key,value);61         if(strcmp(key,"username")==0)62             strcpy(username,value);63         if(strcmp(key,"password")==0)64             strcpy(password,value);65     }66 67     //这里可以写上完整的网页68     if(strcmp(username,"admin")==0 && strcmp(password,"123456")==0)69     {70         printf("<p>登陆成功</p>");71     }72     else73     {74         printf("<p>登陆失败</p>");75     }76     return 0;77 }

  ServerRequest函数修改了一些BUG后的代码

  1 int WebServer::ServerRequest(int cli_fd)  2 {  3     char buf[1024];  4     int size=1024;  5     int i,j;  6     char method[255];//用于保存请求方式  7     char url[512];  8     char path[1024];  9     char args[1024]; 10     struct stat st; 11     int cgi;//cgi 为0 表示get普通方法 1表示get带参方法  2表示post方法 12     pid_t pid; 13     memset(buf,0,sizeof(buf)); 14     cgi=0; 15     //获取第一行请求信息 一般格式为: GET / HTTP/1.1 16     //                               POST / HTTP/1.1 17     size=get_line(cli_fd,buf,sizeof(buf)); 18     //cout<<"\t\t"<<buf<<endl; 19     i=0,j=0; 20     //截取第一个单词 21     while(!isspace(buf[j]) && (i<sizeof(method)-1)) 22     { 23         method[i]=buf[j]; 24         i++;j++; 25     } 26     method[i]=\0; 27     //取第一个与第二个单词之间的空格 28     while(isspace(buf[j]) && (j<sizeof(buf))) 29         j++; 30  31     if(strcasecmp(method,"GET") && strcasecmp(method,"POST")) 32     { 33         Page_501(cli_fd); 34         return -1; 35     } 36  37     if(strcasecmp(method,"GET")==0) 38     { 39     //    cout<<"此次请求的方式是GET方法"<<endl; 40         cgi=0; 41     } 42     else if(strcasecmp(method,"POST")==0) 43     { 44     //    cout<<"此次请求的方式是POST方法"<<endl; 45         cgi=2; 46     } 47  48     //截取第二个单词 49     i=0; 50     int flag=0; 51     while(!isspace(buf[j]) && (i<sizeof(url)-1) && (j<sizeof(buf))) 52     { 53         if(buf[j]==?) 54         { 55             flag=1; 56             j++; 57             url[i]=\0; 58             i=0; 59             cgi=(cgi==0?1:2); 60             continue; 61         } 62         if(flag==0) 63         { 64             url[i]=buf[j]; 65             i++;j++; 66         } 67         else if(flag==1) 68         { 69             args[i]=buf[j]; 70             i++;j++; 71         } 72     } 73     if(flag==0) 74         url[i]=\0; 75     else 76         args[i]=\0; 77  78     sprintf(path,"www%s",url);//这个是web服务器的主目录,这个以后可以处理成读取配置文件,这里就先写固定的www目录 79     if(path[strlen(path)-1]==/) 80         strcat(path,"index.html");//同上 81  82     //cout<<"============>此次请求的地址为:"<<path<<":"<<args<<endl; 83  84     //根据文件名,获取该文件的文件信息。如果为-1,表示获取该文件失败 85     if(stat(path,&st)==-1) 86     { 87         while((size>0) && strcmp("\n",buf))//去除掉多余的请求头信息 88             size=get_line(cli_fd,buf,sizeof(buf)); 89         Page_404(cli_fd); 90     } 91     else 92     { 93         if(S_ISDIR(st.st_mode))//判断url地址,如果是个目录,那么就访问该目录的index.html 94         { 95             strcat(path,"/index.html"); 96             if(stat(path,&st)==-1) 97             { 98                 Page_404(cli_fd); 99             }100         }101         if(!S_ISDIR(st.st_mode)&&((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH)))//判断该url地址所对应的文件是否是可执行,并且是否有权限102         {103             //是一个cgi程序104             if(strcasecmp(method,"GET")==0)105                 cgi=1;106             else if(strcasecmp(method,"POST")==0)107                 cgi=2;108             else109                 cgi=0;110         }111         cout<<"访问:"<<path<<endl;112         if(cgi==0)//如果cgi为0,那么就表示该url所对应的文件不是cgi程序,而是一个简单的静态页面113         {114             pid = fork();115             if(pid==0)116             {117                 ServerCatHttpPage(cli_fd,path,st.st_size);118             }119         }120         else if(cgi==1)//get方法带参数121         {122             pid=fork();123             if(pid==0)124             {125                 while((size>0) && strcmp("\n",buf))//去除掉多余的请求头信息126                     size=get_line(cli_fd,buf,sizeof(buf));127                 ServerGetFunction(cli_fd,path,urldecode(args));128             }129         }130         else if(cgi==2)//post方法131         {132             pid=fork();133             if(pid==0)134             {135                 int content_length=0;136                 while((size>0) && strcmp("\n",buf))//去除掉多余的请求头信息137                 {138                     size=get_line(cli_fd,buf,sizeof(buf));139                     buf[15]=\0;140                     if(strcasecmp(buf,"Content-Length:")==0)141                     {142                         content_length=atoi(&(buf[16]));143                     }144                 }145                 if(content_length==0)146                 {147                     Page_400(cli_fd);148                     return 0;149                 }150                 char c;151                 j=0;152                 for(int i=0;i<content_length;i++)153                 {154                     recv(cli_fd,&c,1,0);155                     args[j]=c;156                     j++;157                 }158                 args[j]=0;159                 ServerPostFunction(cli_fd,path,urldecode(args));160             }161         }162     }163     close(cli_fd);164     return 0;165 }
View Code

  运行时的界面

   用户名密码正确时

   用户名密码错误时

   同理GET方法的请求也是可以了。

  上面实现的程序是使用c原来来写的实现cgi,听说perl的cgi很出名,那么接下来就实现对perl-cgi的支持。首先要安装perl,一般的Linux都有自带,接下来就需要一个perl的cgi库,安装的方式为 yum install perl-CGI 进行安装。 安装成功与否运行下面脚本就知道了。

 1 #!/usr/bin/perl -Tw 2  3 use strict; 4 use CGI; 5  6 my($cgi) = new CGI; 7  8 #print $cgi->header(‘text/html‘); 9 print $cgi->start_html(-title => "Example CGI script",10         -BGCOLOR => red);11 print $cgi->h1("CGI Example");12 print $cgi->p, "This is an example of CGI\n";13 print $cgi->p, "Parameters given to this script:\n";14 print "<UL>\n";15 foreach my $param ($cgi->param)16 {17     print "<LI>", "$param ", $cgi->param($param), "\n";18 }19 print "</UL>";20 print $cgi->end_html, "\n";

  注意如果要保证我们的Webserver可以通过execl进行调用的话,还要修改cgi脚本程序的执行权限(chomd)

  下面这个是带参数的结果图

  如果想要c语言的语法,但是对于网页格式有太多的没有必要的HTML标签,这里可是使用一个cgi的库,可以加快cgi程序的开发,Fastcgi模块(http://www.fastcgi.com/devkit/doc/fastcgi-prog-guide/ap_guida.htm)

  到这里我们的web服务器易筋经实现最基本的功能了,可以做很多事了。我们的webserver编译后大小才22K而已。

 

  本文地址: http://www.cnblogs.com/wunaozai/p/3946486.html

Socket网络编程--简单Web服务器(5)