zoukankan      html  css  js  c++  java
  • 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
    ");
     8     printf("%s:%s
    ",args[0],args[1]);
     9     data=getenv("QUERY_STRING");
    10     printf("query_string::%s
    ",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<<"		"<<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]='';
     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]='';
     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]='';
     75     else
     76         args[i]='';
     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("
    ",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("
    ",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("
    ",buf))//去除掉多余的请求头信息
    137                 {
    138                     size=get_line(cli_fd,buf,sizeof(buf));
    139                     buf[15]='';
    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
    ";
    13 print $cgi->p, "Parameters given to this script:
    ";
    14 print "<UL>
    ";
    15 foreach my $param ($cgi->param)
    16 {
    17     print "<LI>", "$param ", $cgi->param($param), "
    ";
    18 }
    19 print "</UL>";
    20 print $cgi->end_html, "
    ";

      注意如果要保证我们的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

  • 相关阅读:
    CommonJs、AMD、CMD模块化规范
    在 repeater控件中有button控件,如何点击button按钮在后头产生方法
    DropdownList动态绑定数据库中的字段
    电脑操作的快捷键
    多条件查询
    asp.Net_图片上传的一个类库的源码
    日期转换_中文To数字
    VS2010断点设置技巧
    附加进程调试
    电脑常用的几个快捷键
  • 原文地址:https://www.cnblogs.com/wunaozai/p/3946486.html
Copyright © 2011-2022 走看看