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

  stat函数

#include <sys/stat.h>

int stat(const char *restrict pathname,struct stat * restrict buf);

int fstat(int filedes,struct stat * buf);

int lstat(const char *restrict pathname,struct stat * restrict buf);

给出pathname,stat函数就返回与此命名文件有关的信息结构。fstat函数获取已在描述符filedes上打开文件的有关信息。lstat函数类似与stat,但是命名的文件不是个符号链接。

  实现指定url访问指定目录的web服务器

 int WebServer::ServerRequest(int cli_fd)
{
char buf[];
int size=;
int i,j;
char method[];//用于保存请求方式
char url[];
char path[];
struct stat st;
int cgi;
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++;
//截取第二个单词
i=;
while(!isspace(buf[j]) && (i<sizeof(url)-) && (j<sizeof(buf)))
{
url[i]=buf[j];
i++;j++;
}
url[i]='\0'; if(strcasecmp(method,"GET") && strcasecmp(method,"POST"))
{
Page_501(cli_fd);
return -;
} if(strcasecmp(method,"GET")==)
{
cout<<"此次请求的方式是GET方法"<<endl;
}
else if(strcasecmp(method,"POST")==)
{
cout<<"此次请求的方式是POST方法"<<endl;
}
cout<<"此次请求的地址为:"<<url<<endl; sprintf(path,"www%s",url);//这个是web服务器的主目录,这个以后可以处理成读取配置文件,这里就先写固定的www目录
if(path[strlen(path)-]=='/')
strcat(path,"index.html");//同上 //根据文件名,获取该文件的文件信息。如果为-1,表示获取该文件失败
if(stat(path,&st)==-)
{
while((size>) && strcmp("\n",buf))//去除掉多余的请求头信息
size=get_line(cli_fd,buf,sizeof(buf));
Page_404(cli_fd);
}
else
{
if((st.st_mode & S_IFMT)== S_IFDIR)//判断url地址,如果是个目录,那么就访问该目录的index.html
{
strcat(path,"/index.html");
}
if((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH))//判断该url地址所对应的文件是否是可执行,并且是否有权限
{
cgi=;//是一个cgi程序
}
if(cgi==)//如果cgi为0,那么就表示该url所对应的文件不是cgi程序,而是一个简单的静态页面
{
ServerCatHttpPage(cli_fd,path);
}
} if(fork()==)
{
//处理阶段
//execl("/bin/ls","ls","/home/myuser/",NULL);
//Page_200(cli_fd);
}
close(cli_fd);
return ;
}

  返回页的代码

 int WebServer::ServerCatHttpPage(int cli_fd,char *path)
{
FILE * resource=NULL;
int size=;
char buf[];
buf[]=;buf[]=;
while((size>) && strcmp("\n",buf))//去除掉多余的请求头信息
size=get_line(cli_fd,buf,sizeof(buf)); resource=fopen(path,"r");//根据GET后面的文件吗,将文件打开
if(resource==NULL)//打开文件失败
{
Page_404(cli_fd);
}
else
{
char type[]="text/html";
char * p =type;
Page_Headers(cli_fd,p);
Page_Cat(cli_fd,resource);
}
fclose(resource);
return ;
}
 int WebServer::Page_Headers(int cli_fd,char * type)
{
char buf[];
strcpy(buf,"HTTP/1.1 200 OK\r\n");
send(cli_fd, buf, strlen(buf), );
sprintf(buf, "Server:wunaozai.cnblogs.com\r\n");
send(cli_fd, buf, strlen(buf), );
sprintf(buf, "Content-Type: %s\r\n",type);
send(cli_fd, buf, strlen(buf), );
sprintf(buf, "\r\n");
send(cli_fd, buf, strlen(buf), );
return ;
}
int WebServer::Page_Cat(int cli_fd,FILE * fp)
{
char buf[]; fgets(buf,sizeof(buf),fp);
while(!feof(fp))
{
send(cli_fd,buf,strlen(buf),);
fgets(buf,sizeof(buf),fp);
}
return ;
}

  代码写好了,我在当前目录下创建一个www的目录在里面有个index.html和text.html的页面。然后我们通过浏览器进行返回。得到的结果如下:

  可以看出都显示了指定的网页信息,而最后一个是404页面,可是为什么会有乱码呢,应该是在应答信息哪里没有指点编码格式。所以我们在Page_404这个函数里的Content-Type这一行进行如下修改

 sprintf(buf, "Content-Type: text/html;charset=utf-8\r\n");

  当然还可以在html网页上进行指定。

  本小结篇幅比较少,接下来就实现传输一个ico图标吧。我们都知道一个html网页是通过一个url进行查找文件然后以http协议发送个浏览器。但是我们服务器怎么发送css或js或图片给浏览器呢?怎么知道那些是要的那些是不要的。一看是还以为很难,上网查了一下,原来很简单的。浏览器接收到根据url发送过来的html文件,然后浏览器会分析这个html文件中代码的图片文件,css文件等,然后在跟服务器建立一个http请求,请求一个新的文件。在发送的过程中,不是直接发送图片过去的,而是先编织成HTTP的格式发送给浏览器,其中还要指定这个图片的格式,大概就是这样了。说起来比较抽象,我用wireshark抓一个包看看。

  可以看出这个应答信息的格式跟以前讲的是一样的。也是一个应答头,然后在应答头里有个Content-Length属性,里面包含接下来要接收的文件大小。

  一开始使用下面代码进行文件的读取

 int WebServer::Page_Cat(int cli_fd,FILE * fp)
{
char buf[]; fgets(buf,sizeof(buf),fp);
while(!feof(fp))
{
send(cli_fd,buf,strlen(buf),);
fgets(buf,sizeof(buf),fp);
}
return ;
}

  然后在浏览器进行访问,然后就是一直访问不到图片资源,一直弄到凌晨几点。今天,想了个办法,对于图片一个字节一个字节的打印出来,弄了好久才知道,原来是因为图片资源里面有ascii码为0的字符,所以导致在发送的时候使用strlen时发送数据会不完整。哎......这个以后要注意啊。所以我准备使用fgetc来获取数据,要注意fgetc的返回值是int型,用char会出错,应该没有人跟我一样不小心吧。

 int WebServer::Page_Headers(int cli_fd,char * type,int filesize)
{
char buf[];
strcpy(buf,"HTTP/1.1 200 OK\r\n");
send(cli_fd, buf, strlen(buf), );
sprintf(buf, "Server:wunaozai.cnblogs.com\r\n");
send(cli_fd, buf, strlen(buf), );
sprintf(buf, "Content-Type: %s\r\n",type);
send(cli_fd, buf, strlen(buf), );
sprintf(buf, "Content-Length: %d\r\n",filesize);
send(cli_fd, buf, strlen(buf), );
sprintf(buf, "\r\n");
send(cli_fd, buf, strlen(buf), );
return ;
}
int WebServer::Page_Cat(int cli_fd,FILE * fp)
{
int c; while((c=fgetc(fp))!=EOF)
{
send(cli_fd,&c,,);
}
return ;
}

  ServerCatHttpPage函数的代码如下

 int WebServer::ServerCatHttpPage(int cli_fd,char *path,int filesize)
{
FILE * resource=NULL;
int size=;
char buf[];
char type[];
char * p =type;
buf[]=;buf[]=;
while((size>) && strcmp("\n",buf))//去除掉多余的请求头信息
size=get_line(cli_fd,buf,sizeof(buf)); //判断文件类型
int len=strlen(path);
cout<<path<<":"<<filesize<<endl;
if(path[len-]=='h'&&path[len-]=='t'&&path[len-]=='m'&&path[len-]=='l')
{
strcpy(type,"text/html");
}
else if(path[len-]=='.'&&path[len-]=='i'&&path[len-]=='c'&&path[len-]=='o')
{
strcpy(type,"image/x-icon");
}
else if(path[len-]=='.'&&path[len-]=='j'&&path[len-]=='p'&&path[len-]=='g')
{
strcpy(type,"image/jpeg");
}
else
{
strcpy(type,"text/html");
}
cout<<"请求资源的类型:"<<type<<endl; resource=fopen(path,"r");//根据GET后面的文件吗,将文件打开
if(resource==NULL)//打开文件失败
{
Page_404(cli_fd);
}
else
{
Page_Headers(cli_fd,p,filesize);
Page_Cat(cli_fd,resource);
}
fclose(resource);
return ;
}

  好了,感觉还不错的样子。

  参考资料: http://bbs.csdn.net/topics/100130327

        http://blog.csdn.net/xiaojianpitt/article/details/4389247

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

Socket网络编程--简单Web服务器(3)的更多相关文章

  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服务器(4)

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

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

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

  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. 【Java】 剑指offer(27) 二叉树的镜像

    本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 请完成一个函数,输入一个二叉树,该函数输出它的镜像. 思路 画图可 ...

  2. this关键字的用法(包括this语句)

    一:概述 this代表它所在函数所属对象的引用. 简单说:哪个对象在调用this所在的函数,this就代表哪个对象. 二:关于运用的程序 同龄人的比较. 三:关于this语句的使用 用于构造函数之间进 ...

  3. C++语言实现-邻接表

    图的邻接表实现 邻接表是图的一种链式存储结构.主要是应对于邻接矩阵在顶点多边少的时候,浪费空间的问题.它的方法就是声明两个结构.如下图所示: 先来看看伪代码: typedef char Vertext ...

  4. 洛谷P2347 砝码称重 【多重背包】(方案数)(经典)

    题目链接:https://www.luogu.org/problemnew/show/P2347 题目描述 设有1g.2g.3g.5g.10g.20g的砝码各若干枚(其总重<=1000), 输入 ...

  5. Spring Boot 项目实战(一)Maven 多模块项目搭建

    一.前言 最近公司项目准备开始重构,框架选定为 Spring Boot ,本篇主要记录了在 IDEA 中搭建 Spring Boot Maven 多模块项目的过程. 二.软件及硬件环境 macOS S ...

  6. MySQL数据库crash的问题分析

    [问题] 生产环境有多台slave服务器,不定期的会crash,下面是error log中的堆栈信息 Thread pointer: 0x7f1e54b26410 Attempting backtra ...

  7. js算法初窥03(搜索及去重算法)

    前面我们了解了一些常用的排序算法,那么这篇文章我们来看看搜索算法的一些简单实现,我们先来介绍一个我们在实际工作中一定用到过的搜索算法——顺序搜索. 1.顺序搜索 其实顺序搜索十分简单,我们还是以第一篇 ...

  8. CF868 F. Yet Another Minimization Problem 决策单调优化 分治

    目录 题目链接 题解 代码 题目链接 CF868F. Yet Another Minimization Problem 题解 \(f_{i,j}=\min\limits_{k=1}^{i}\{f_{k ...

  9. Python中的pass的作用

    1.pass语句什么也不做,一般作为占位符或者创建占位程序,pass语句不会执行任何操作2.保证格式完整 3.保证语义完整 以if语句为例,在c或c++/java中: ? if(true) ;//do ...

  10. Codeforces Round #405 (rated, Div. 2, based on VK Cup 2017 Round 1) C. Bear and Different Names 贪心

    C. Bear and Different Names 题目连接: http://codeforces.com/contest/791/problem/C Description In the arm ...