上一小节已经实现了浏览器发送请求,然后服务器给出应答信息,然后浏览器显示出服务器发送过来的网页。一切看起来都是那么的美好。这一小节就准备实现可以根据地址栏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. Spring bean加载多个配置文件

    除了写很简单的加载一个xml,加载多个的情况一直没用到,在公司里也不会由自己处理这个问题,现在需要用到了,就研究验证一下. 使用的案例还是上面的例子. 只有,将原来的beans.xml分成两个部分. ...

  2. poj 1160 Post Office 【区间dp】

    <题目链接> 转载于:>>> 题目大意: 一条高速公路,有N个村庄,每个村庄均有一个唯一的坐标,选择P个村庄建邮局,问怎么选择,才能使每个村庄到其最近邮局的距离和最小?最 ...

  3. HDU 1159 Common Subsequence 【最长公共子序列】模板题

    题目链接:https://vjudge.net/contest/124428#problem/A 题目大意:给出两个字符串,求其最长公共子序列的长度. 最长公共子序列算法详解:https://blog ...

  4. 机器学习 Logistic 回归

    Logistic regression 适用于二分分类的算法,用于估计某事物的可能性. logistic分布表达式 $ F(x) = P(X<=x)=\frac{1}{1+e^{\frac{-( ...

  5. 潭州课堂25班:Ph201805201 第二课:数据类型和序列类型 (课堂笔记)

    workon py3env pip install ipython   安装虚拟环境, 安装完成之后,键入:ipython   进入环境, 数字类型:(整型)   int    --------> ...

  6. Android Binder学习的网站

    1. Binder系列 http://gityuan.com/2015/10/31/binder-prepare/ 2. Binder机制 http://jcodecraeer.com/a/anzhu ...

  7. 喵哈哈村的魔法考试 Round #10 (Div.2) 题解

    喵哈哈村与哗啦啦村的大战(一) 最大值就是全部+3,最小值就是全部-3,注意不能降为负数. #include<bits/stdc++.h> using namespace std; con ...

  8. linux上 安装软件

    一.rpm包安装方式步骤:  1.找到相应的软件包,比如soft.version.rpm,下载到本机某个目录: 2.打开一个终端,su -成root用户: 3.cd soft.version.rpm所 ...

  9. 动态创建的 CEdit 被限制长度,增加 ES_AUTOHSCROLL 属性;被无法Tab激活焦点,增加 WS_TABSTOP 属性(转)

    动态创建的 CEdit 被限制长度,增加 ES_AUTOHSCROLL 属性:被无法Tab激活焦点,增加 WS_TABSTOP 属性. CEdit m_editUrl; // ES_AUTOHSCRO ...

  10. 亲测GO环境搭建,理解go build、go install、go get

    GO下载: GO语言中文网下载:https://studygolang.com/dl Mac下直接通过brew instatll go指令即可完成下载安装 GO环境变量配置: $GOROOT=/usr ...