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

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

 int WebServer::get_filetype(char *type,char *path)//用于判断该url指向文件的后缀
{
if(strstr(path,".html"))
strcpy(type,"text/html");
else if(strstr(path,".gif"))
strcpy(type,"image/gif");
else if(strstr(path,".jpg"))
strcpy(type,"image/jpeg");
else if(strstr(path,".png"))
strcpy(type,"image/png");
else if(strstr(path,".ico"))
strcpy(type,"image/x-icon");
else if(strstr(path,".js"))
strcpy(type,"text/javascript");
else if(strstr(path,".json"))
strcpy(type,"application/json");
else if(strstr(path,".css"))
strcpy(type,"text/css");
else
strcpy(type,"text/plain");
return ;
}

  然后修改一下主页index.html的文件

 <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>
</body>
</html>

  两个外部文件如下

 [myuser@bogon www]$ cat style.css
.ceshi {font-size:20px; color:#ff0000;}
[myuser@bogon www]$ cat javascript.js
function hi()
{
alert("js 调用,你单机了按钮");
}
[myuser@bogon www]$

  下面这个是运行的结果图

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

  用chrome浏览器的F12调试工具获取到的状态,可以看到,该网页可以获取到index.html,然后浏览器会根据这个页面再发送index.html文件所需要的外部文件的请求,从上面可以看出请求了style.css,javascript.js和ab.jpg图片。然后显示网页,我单击了那个button也是可以调用js脚本的。

  接下来就是判断一个提交是get还是post,网页index.html的源代码

     <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">
<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="index.html">
<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>

  修改后的ServerRequest函数

 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++;
i=;
url[i]='\0';
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");
//cout<<"此次请求的地址为:"<<path<<endl;
}
if(!S_ISDIR(st.st_mode)&&((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH)))//判断该url地址所对应的文件是否是可执行,并且是否有权限
{
cgi=;//是一个cgi程序
}
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,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,args);
}
}
}
close(cli_fd);
return ;
}

  增加的两个处理cgi程序的函数(下小节实现)

 int WebServer::ServerGetFunction(int cli_fd,char *path,char *args)
{
cout<<"cli_fd:"<<cli_fd<<" path:"<<path<<" args:"<<args<<endl;
Page_200(cli_fd);
return ;
}
int WebServer::ServerPostFunction(int cli_fd,char *path,char *args)
{
cout<<"cli_fd:"<<cli_fd<<" path:"<<path<<" args:"<<args<<endl;
Page_200(cli_fd);
return ;
}

  下面这个是运行的结果

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

  从上图可以看出通过GET方法和POST方法都可以正常的获取到从表单那里传过去的参数。我们知道中文在url地址中的显示是显示成16进制的。这个是URL编码。接下来要实现对这个URL编码进行转换,这里使用网上别人提供的代码。

 #include <ctype.h>
#include <string>
#include <string.h>
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
using namespace std;
static int php_htoi(char *s)
{
int value;
int c; c = ((unsigned char *)s)[];
if (isupper(c))
c = tolower(c);
value = (c >= '' && c <= '' ? c - '' : c - 'a' + ) * ; c = ((unsigned char *)s)[];
if (isupper(c))
c = tolower(c);
value += c >= '' && c <= '' ? c - '' : c - 'a' + ; return (value);
}
/*
*URL解码,提取自PHP 5.2.17
*用法:string urldecode(string str_source)
*时间:2012-8-14 By Dewei
* */
string urldecode(string &str_source)
{
char const *in_str = str_source.c_str();
int in_str_len = strlen(in_str);
int out_str_len = ;
string out_str;
char *str; str = strdup(in_str);
char *dest = str;
char *data = str; while (in_str_len--) {
if (*data == '+') {
*dest = ' ';
}
else if (*data == '%' && in_str_len >= && isxdigit((int) *(data + ))
&& isxdigit((int) *(data + ))) {
*dest = (char) php_htoi(data + );
data += ;
in_str_len -= ;
} else {
*dest = *data;
}
data++;
dest++;
}
*dest = '\0';
out_str_len = dest - str;
out_str = str;
free(str);
return out_str;
} int main()
{
string str="username=%E7%99%BB%E5%BD%95";
string out=urldecode(str);
cout<<out<<endl;
return ;
}

  我们通过在传参的时候调用urldecode进行解码就可以实现中文显示了。

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

  这一节就到这里,下一小节将实现对cgi的支持。

  参考资料: http://blog.csdn.net/xiaojianpitt/article/details/4389247

      : http://dewei.iteye.com/blog/1630969 (关于URL编码解码代码)

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

上一篇:Django配置https协议


下一篇:python socket编程 实现简单p2p聊天程序