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

 int WebServer::ServerGetFunction(int cli_fd,char *path,char *args)
{
ServerExecuteCGI(cli_fd,path,args);
return ;
}
int WebServer::ServerPostFunction(int cli_fd,char *path,char *args)
{
ServerExecuteCGI(cli_fd,path,args);
return ;
} int WebServer::ServerExecuteCGI(int cli_fd,char *path,char *args)
{
char query_env[];
char type[]="text/html";
pid_t pid;
int status;
int cgi_output[];
int cgi_input[]; if(pipe(cgi_output)<)
{
Page_500(cli_fd);
return ;
}
if(pipe(cgi_input)<)
{
Page_500(cli_fd);
return ;
} if((pid=fork())<)
{
Page_500(cli_fd);
return ;
}
if(pid==)//child
{
dup2(cgi_output[],);//cgi的输出端绑定文件描述符为1的输出端
dup2(cgi_input[],);
close(cgi_output[]);
close(cgi_input[]);
sprintf(query_env,"QUERY_STRING=%s",args);
putenv(query_env);
execl(path,path,args);
exit();
}
else //parent
{
char c;
close(cgi_output[]);//取消绑定
close(cgi_input[]);
Page_Headers(cli_fd,type,);
while(read(cgi_output[],&c,)>)
send(cli_fd,&c,,);
close(cgi_output[]);
close(cgi_input[]);
}
waitpid(pid,&status,);
return ;
}

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

 #include <stdio.h>
#include <stdlib.h> int main(int argc,char **args)
{
char *data;
printf("Hello\n");
printf("%s:%s\n",args[],args[]);
data=getenv("QUERY_STRING");
printf("query_string::%s\n",data);
return ;
}

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

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

 <html>
<head>
<title>Test</title>
<meta http-equiv="Content-Type" content="text/html ; charset=utf-8">
<link rel="stylesheet" href="style.css" type="text/css"/>
<script language="javascript" src="javascript.js"></script>
</head> <body>
<div class="ceshi">图片</div><img src="ab.jpg"></img>
<input name="button" type="button" value="Click!" onclick=hi();></input> <hr>
<br>使用post方式<br>
<form method="post" name="frm1" action="hello">
<label>用户名:</label>
<input type="text" name="username" />
<br>
<label>密码:</label>
<input type="password" name="password" />
<br>
<input type="submit" name="commit" value="登陆"/>
<br>
</form>
<hr>
<br>使用get方式<br>
<form method="get" name="frm1" action="hello">
<label>用户名:</label>
<input type="text" name="username" />
<br>
<label>密码:</label>
<input type="password" name="password" />
<br>
<input type="submit" name="commit" value="登陆"/>
<br>
</form>
</body>
</html>

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

 #include <stdio.h>
#include <stdlib.h>
#include <string.h> int split(char **arr,char *str,const char*del)
{
char *s=NULL;
int i=;
s=strtok(str,del);
while(s!=NULL)
{
*arr++=s;
s=strtok(NULL,del);
i++;
}
return i;
} void split_key(char *ch,char *key,char *value)
{
int len;
int i;
int j;
len=strlen(ch);
j=;
for(i=;i<len;i++)
{
if(ch[i]=='=')
{
i++;
break;
}
key[j]=ch[i];
j++;
}
key[j]=;
j=;
for(;i<len;i++)
{
value[j]=ch[i];
j++;
}
value[j]=;
return ;
} int main(int argc,char **args)
{
char *data;
char *myargs[];
int cnt=;
int i;
char key[],value[];
char username[],password[];
memset(myargs,,sizeof(myargs));
cnt=split(myargs,args[],"&"); for(i=;i<cnt;i++)
{
split_key(myargs[i],key,value);
if(strcmp(key,"username")==)
strcpy(username,value);
if(strcmp(key,"password")==)
strcpy(password,value);
} //这里可以写上完整的网页
if(strcmp(username,"admin")== && strcmp(password,"")==)
{
printf("<p>登陆成功</p>");
}
else
{
printf("<p>登陆失败</p>");
}
return ;
}

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

 int WebServer::ServerRequest(int cli_fd)
{
char buf[];
int size=;
int i,j;
char method[];//用于保存请求方式
char url[];
char path[];
char args[];
struct stat st;
int cgi;//cgi 为0 表示get普通方法 1表示get带参方法 2表示post方法
pid_t pid;
memset(buf,,sizeof(buf));
cgi=;
//获取第一行请求信息 一般格式为: GET / HTTP/1.1
// POST / HTTP/1.1
size=get_line(cli_fd,buf,sizeof(buf));
//cout<<"\t\t"<<buf<<endl;
i=,j=;
//截取第一个单词
while(!isspace(buf[j]) && (i<sizeof(method)-))
{
method[i]=buf[j];
i++;j++;
}
method[i]='\0';
//取第一个与第二个单词之间的空格
while(isspace(buf[j]) && (j<sizeof(buf)))
j++; if(strcasecmp(method,"GET") && strcasecmp(method,"POST"))
{
Page_501(cli_fd);
return -;
} if(strcasecmp(method,"GET")==)
{
// cout<<"此次请求的方式是GET方法"<<endl;
cgi=;
}
else if(strcasecmp(method,"POST")==)
{
// cout<<"此次请求的方式是POST方法"<<endl;
cgi=;
} //截取第二个单词
i=;
int flag=;
while(!isspace(buf[j]) && (i<sizeof(url)-) && (j<sizeof(buf)))
{
if(buf[j]=='?')
{
flag=;
j++;
url[i]='\0';
i=;
cgi=(cgi==?:);
continue;
}
if(flag==)
{
url[i]=buf[j];
i++;j++;
}
else if(flag==)
{
args[i]=buf[j];
i++;j++;
}
}
if(flag==)
url[i]='\0';
else
args[i]='\0'; sprintf(path,"www%s",url);//这个是web服务器的主目录,这个以后可以处理成读取配置文件,这里就先写固定的www目录
if(path[strlen(path)-]=='/')
strcat(path,"index.html");//同上 //cout<<"============>此次请求的地址为:"<<path<<":"<<args<<endl; //根据文件名,获取该文件的文件信息。如果为-1,表示获取该文件失败
if(stat(path,&st)==-)
{
while((size>) && strcmp("\n",buf))//去除掉多余的请求头信息
size=get_line(cli_fd,buf,sizeof(buf));
Page_404(cli_fd);
}
else
{
if(S_ISDIR(st.st_mode))//判断url地址,如果是个目录,那么就访问该目录的index.html
{
strcat(path,"/index.html");
if(stat(path,&st)==-)
{
Page_404(cli_fd);
}
}
if(!S_ISDIR(st.st_mode)&&((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH)))//判断该url地址所对应的文件是否是可执行,并且是否有权限
{
//是一个cgi程序
if(strcasecmp(method,"GET")==)
cgi=;
else if(strcasecmp(method,"POST")==)
cgi=;
else
cgi=;
}
cout<<"访问:"<<path<<endl;
if(cgi==)//如果cgi为0,那么就表示该url所对应的文件不是cgi程序,而是一个简单的静态页面
{
pid = fork();
if(pid==)
{
ServerCatHttpPage(cli_fd,path,st.st_size);
}
}
else if(cgi==)//get方法带参数
{
pid=fork();
if(pid==)
{
while((size>) && strcmp("\n",buf))//去除掉多余的请求头信息
size=get_line(cli_fd,buf,sizeof(buf));
ServerGetFunction(cli_fd,path,urldecode(args));
}
}
else if(cgi==)//post方法
{
pid=fork();
if(pid==)
{
int content_length=;
while((size>) && strcmp("\n",buf))//去除掉多余的请求头信息
{
size=get_line(cli_fd,buf,sizeof(buf));
buf[]='\0';
if(strcasecmp(buf,"Content-Length:")==)
{
content_length=atoi(&(buf[]));
}
}
if(content_length==)
{
Page_400(cli_fd);
return ;
}
char c;
j=;
for(int i=;i<content_length;i++)
{
recv(cli_fd,&c,,);
args[j]=c;
j++;
}
args[j]=;
ServerPostFunction(cli_fd,path,urldecode(args));
}
}
}
close(cli_fd);
return ;
}

  运行时的界面

  用户名密码正确时

  用户名密码错误时

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

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

 #!/usr/bin/perl -Tw

 use strict;
use CGI; my($cgi) = new CGI; #print $cgi->header('text/html');
print $cgi->start_html(-title => "Example CGI script",
-BGCOLOR => 'red');
print $cgi->h1("CGI Example");
print $cgi->p, "This is an example of CGI\n";
print $cgi->p, "Parameters given to this script:\n";
print "<UL>\n";
foreach my $param ($cgi->param)
{
print "<LI>", "$param ", $cgi->param($param), "\n";
}
print "</UL>";
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)的更多相关文章

  1. Socket网络编程--简单Web服务器(6)

    本来是想实现ssl连接的,但是弄了好久都不成功,就索性不做了,等以后有能力再做了.所以这一小节就是本次的最后一节了.就简单的说几个注意点. 1.加个配置文件 使用单例模式,使用一个类,该类保存一些信息 ...

  2. Socket网络编程--简单Web服务器(1)

    这一次的Socket系列准备讲Web服务器.就是编写一个简单的Web服务器,具体怎么做呢?我也不是很清楚流程,所以我找来了一个开源的小的Web服务器--tinyhttpd.这个服务器才500多行的代码 ...

  3. Socket网络编程--简单Web服务器(2)

    上一小节通过阅读开源的Web服务器--tinyhttpd.大概知道了一次交互的请求信息和应答信息的具体过程.接下来我就自己简单的实现一个Web服务器. 下面这个程序只是实现一个简单的框架出来.这次先实 ...

  4. Socket网络编程--简单Web服务器(3)

    上一小节已经实现了浏览器发送请求,然后服务器给出应答信息,然后浏览器显示出服务器发送过来的网页.一切看起来都是那么的美好.这一小节就准备实现可以根据地址栏url的不同来返回指定的网页.目前还不考虑带参 ...

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

    上一小节已经实现了对图片的传输,接下来就是判断文件是否为js,css,png等格式.我们增加一个函数用于判断格式 int WebServer::get_filetype(char *type,char ...

  6. C++ socket 网络编程 简单聊天室

    操作系统里的进程通讯方式有6种:(有名/匿名)管道.信号.消息队列.信号量.内存(最快).套接字(最常用),这里我们来介绍用socket来实现进程通讯. 1.简单实现一个单向发送与接收 这是套接字的工 ...

  7. C#中使用Socket实现简单Web服务器

    上一篇博客中介绍了怎样使用socket访问web服务器.关键有两个: 熟悉Socket编程: 熟悉HTTP协议. 上一篇主要是通过socket来模拟浏览器向(任何)Web服务器发送(HTTP)请求,重 ...

  8. Java Web 基础(一) 基于TCP的Socket网络编程

    一.Socket简单介绍 Socket通信作为Java网络通讯的基础内容,集中了异常.I/O流模式等众多知识点.学习Socket通信,既能够了解真正的网络通讯原理,也能够增强对I/O流模式的理解. 1 ...

  9. python之Socket网络编程

    什么是网络? 网络是由节点和连线构成,表示诸多对象及其相互联系.在数学上,网络是一种图,一般认为专指加权图.网络除了数学定义外,还有具体的物理含义,即网络是从某种相同类型的实际问题中抽象出来的模型.在 ...

随机推荐

  1. 6-10 下落的树叶 uva699

    类似第九题  都是属于比较巧妙的题目  ! 用一个p数组来保存水平值   然后开始built 自然就会按照自左而右的顺序来读取!!!!!!这很重要 #include<bits/stdc++.h& ...

  2. 003 将spark源码导入到IDEA中

    1.解压源代码 2.进入IDEA的首界面 3.使用open将解压的工程加载 4.将文件的形式改成maven项目 5.使用

  3. C++ 的static 与 const

    1.static成员变量(非const)必须在类外定义,在类中只是作为声明(声明其scope为该类),不能使用类初始化成员列表来初始化,只能在定义的时候初始化. 2.static const的成员变量 ...

  4. PopupWindow下拉列表

    效果图 步骤: 1.画出编辑框的布局.popupWindow的布局.popupWindow中listview每行的布局 2.new一个PopupWindow对象,设置其属性 3.定义一个BaseAda ...

  5. Angular 个人深究(五)【外部包引用 Leaflet 简单实用】

    Leaflet 使用 最近在Angular项目中,用到了地图,由于种种原因放弃了百度地图api使用,最后选择了leaflet,简单介绍一下. 介绍: Leaflet 是一个为移动设备设计的交互式地图的 ...

  6. 潭州课堂25班:Ph201805201 爬虫基础 第三课 urllib (课堂笔记)

    Python网络请求urllib和urllib3详解   urllib是Python中请求url连接的官方标准库,在Python2中主要为urllib和urllib2,在Python3中整合成了url ...

  7. android TextView如何换行?

    具体代码是: rb_zuotian.setText("第一行"+"\n"+"第二行"); rb_jintian.setText(DateUt ...

  8. C++.Linux下redis编程:error while loading shared libraries: libhiredis.so.0.13

    编译 sudo gcc -o sltest01 sltest01.c -L/usr/local/lib/ -lhiredis 运行 sudo ./sltest01 编译成功后运行报错信息: ./slt ...

  9. EasyUI学习总结(四)——parser源码分析

    parser模块是easyloader第一个加载的模块,它的主要作用,就是扫描页面上easyui开头的class标签,然后初始化成easyui控件. /** * parser模块主要是解析页面中eas ...

  10. 移动端web禁止长按选择文字以及弹出菜单

    /*如果是禁用长按选择文字功能,用css*/ * { -webkit-touch-callout:none; -webkit-user-select:none; -khtml-user-select: ...