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)//child
38     {
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 //parent
49     {
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=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     else
73     {
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             else
109                 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

posted @ 2014-08-31 09:32  无脑仔的小明  阅读(1745)  评论(0编辑  收藏  举报