从零开始一个http服务器(四)

代码地址 : https://github.com/flamedancer/cserver

git checkout step4

运行:

make clean && make && ./myserver.out

测试:

浏览器打开 http://127.0.0.1:9734/

response 返回文件 根据request 动态返回 response

  • Makefile
  • 读取文件内容并作为body返回
  • 列出目录下文件

Makefile

看看我们现在的编译运行命令gcc request.h request.c response.h response.c main.c tools/utils.c tools/utils.h && ./a.out

太长了!而且如果以后添加新的文件编译命令还要改。我们用Makefile来优化编译。

objects = main.o request.o response.o \
utils.o
VPATH = tools myserver.out : $(objects)
cc -o myserver.out $(objects) main.o :
request.o :
response.o :
utils.o : .PHONY : clean
clean :
-rm myserver.out $(objects)

第1,2行定义了一个常量objects,因为要经常用到main.o request.o response.o utils.o,定义这个常量可以节省打字量。

VPATH是指除了当前路径外的额外路径,例如当前路径是没有utils.c的,需要告诉它还可以在tools目录找。

第5,6行是一组,第5行:冒号是前置声明,也就是声明如果要生成冒号前的文件,需要冒号后面那些文件存在。注意这只是声明。第6行开头需要有tag键(vim 可以在v模式下打出tab),然后是要执行的命令,注意这个命令不一定非要生成冒号前的目标文件。

第8~11行其实和第5行一样,只是利用了make的自动推导功能:根据 .o文件推导需要同名的.c文件,同时推导命令 cc -o main.o main.c

第14行也是一样的,只不过手动用.PHONY说明了下这里并不是要真正生成clean这个目标文件,只是为了执行后面的命令而已, rm 前的 - 号 是指遇到错误继续执行。

这样我们以后编译只要执行make就好了,会生成目标文件myserver.out,可以执行make clean 来清理中间文件。或者直接执行make clean && make && ./myserver.out来运行我们的程序。

读取文件内容并作为body返回

现在我们发送给浏览器是固定的内容,当需要改变内容时,需要重新编译。这样很不灵活,也不实用。

我们修改逻辑,让服务器收到请求时,都去读取文件,再返回文件内容,这样的话当我们要改变发送内容时,只需要修改文件就好了。

void responeFileContent(char * filePath, struct http_response * response) {
char * error_file = "static/404.html";
FILE * fileptr;
fileptr = fopen(filePath, "rb");
if (NULL == fileptr)
{
fileptr = fopen(error_file, "rb");
}
fseek(fileptr, 0, SEEK_END);
response->body_size = ftell(fileptr);
rewind(fileptr);
response->body = (char *)malloc((response->body_size));
fread(response->body, response->body_size, 1, fileptr);
fclose(fileptr);
return;
}

打开文件和关闭文件的函数 fopen fclose 很简单。

这里主要注意获取文件大小的方法:

​ fseek 移动文件指针,从文件末尾(SEEK_END代表文件末尾)移动0位置,跳到文件末尾。

​ ftell 获取文件首到当前文件指针的距离(偏移字节数)。这样就获取了文件大小。

​ rewind 再把文件指针移动回首。

Content-Length的真实意义是字节数,ftell返回的也是字节数,所以body_size不需要 * sizeof(char)

列出目录下文件

我们想在首页列出可以跳转的链接。为此可以扫描static目录下的所有文件,然后动态构造带标签的html返回。

void show_dir_content(struct http_response * response) {
char * path = "static";
char *html = "<html> <ul> %s </ul> </html>";
char *ui = "<li><a href='/static/%s'>static/%s</a></li>"; char liStr[500];
char * liStrP = liStr;
DIR *d = opendir(path); // open the path
// if (d == NULL)
// return; // if was not able return
struct dirent *dir; // for the directory entries
while ((dir = readdir(d)) != NULL) // if we were able to read somehting from the directory
{
if (dir->d_type != DT_DIR) { // if the type is not directory just print it with blue
printf("%s\n", dir->d_name);
sprintf(liStrP, ui, dir->d_name, dir->d_name);
liStrP = liStr + strlen(liStr);
}
}
closedir(d); // finally close the directory response->body = (char *)malloc((strlen(liStr) + strlen(html)) * sizeof(char));
sprintf(response->body, html, liStr);
response->body_size = strlen(response->body);
return;
}

这里主要是打开目录,遍历目录和关闭目录 opendir, readdir, closedir 这几个函数。

最后修改doResponse方法:

void doResponse(struct http_request * request, FILE * stream) {
struct http_response responseInstance;
struct http_response * response = &responseInstance;
initHttpResponse(response);
response->version = "HTTP/1.1";
response->code = "200";
response->desc = "OK"; char content_len[25]; char * home_url = "/";
char * static_url = "/static/";
char * action_url = "/action/"; if (strncmp(static_url, request->url, strlen(static_url)) == 0) {
responeFileContent(request->url + 1, response);
}
else if (strncmp(home_url, request->url, strlen(home_url)) == 0) {
show_dir_content(response);
} else {
char *content = "<html><meta charset='utf-8'><a src='/'> >_< 看来你迷路了 </a></html>";
response->body_size = (int)strlen(content);
response->body = (char *)malloc((response->body_size));
strcpy(response->body, content);
}
sprintf(content_len, "%d", response->body_size);
printf("body size is %d\n", response->body_size);
struct Item * item = newItem(
"Content-Length",
content_len
);
struct Map map_instance;
initMap(&map_instance);
response->headers = &map_instance; mapPush(response->headers, item); outputToFile(response, stream); // clean
free(response->body); // If ptr is NULL, no operation is performed.
response->body = NULL;
}

执行 make clean && make && ./myserver.out 看看效果!

从零开始一个http服务器(四)-动态返回的更多相关文章

  1. 从零开始一个http服务器(三)-返回response 构造

    从零开始一个http服务器(三) 代码地址 : https://github.com/flamedancer/cserver git checkout step3 运行: gcc request.h ...

  2. 从零开始一个http服务器(五)-模拟cgi

    从零开始一个http服务器-模拟cgi(五) 代码地址 : https://github.com/flamedancer/cserver git checkout step5 运行: make cle ...

  3. 从零开始一个http服务器(六)-多路复用和压力测试

    从零开始一个http服务器(六)-多路复用和压力测试 代码地址 : https://github.com/flamedancer/cserver git checkout step6 运行: make ...

  4. 从零开始一个http服务器(二)-请求request解析

    从零开始一个http服务器 (二) 代码地址 : https://github.com/flamedancer/cserver git checkout step2 解析http request 观察 ...

  5. 从零开始一个http服务器(一)-开始

    从零开始一个http服务器 (一) 代码地址 : https://github.com/flamedancer/cserver git checkout step1 一个简单的socket serve ...

  6. 从零开始用 Flask 搭建一个网站(四)

    前言 从零开始用 Flask 搭建一个网站(三) 介绍了网页前端与后端.前端与前端之间数据的交流.本节主要介绍一下如何应用 Flask-OAuthlib, 使用 Flask-OAuthlib 就可以轻 ...

  7. 【重点突破】——使用Express创建一个web服务器

    一.引言 在自学node.js的过程中有一个非常重要的框架,那就是Express.它是一个基于NodeJs http模块而编写的高层模块,弥补http模块的繁琐和不方便,能够快速开发http服务器.这 ...

  8. 用java写一个web服务器

    一.超文本传输协议 Web服务器和浏览器通过HTTP协议在Internet上发送和接收消息.HTTP协议是一种请求-应答式的协议——客户端发送一个请求,服务器返回该请求的应答.HTTP协议使用可靠的T ...

  9. Web服务器和动态语言如何交互--CGI&FastCGI&FPM浅谈

    一个用户的Request是如何经过Web服务器(Apache,Nginx,IIS,Light)与后端的动态语言(如PHP等)进行交互并将结果返回给用户的呢? 本文浅谈个人观点,可能有误,欢迎拍砖,共同 ...

随机推荐

  1. .net 面向对象程序设计深入](3)UML

    1.活动图简介 定义:是阐明了业务用例实现的工作流程. 业务工作流程说明了业务为向所服务的业务主角提供其所需的价值而必须完成的工作. 业务用例由一系列活动组成,它们共同为业务主角生成某些工件. 工作流 ...

  2. case选择语句

    #!/bin/bash   PS3="please select menu:"   select  i  in "Apache" "Mysql&quo ...

  3. Linux:301重定向 —— 将不带www的重定向到带www的

    仓鼠又要手把手教啦 1.先看看自己有没有解析域名(带www和不带www的是两种域名形式!!) 位置:阿里云->域名->解析 2.进入解析,带www的说明你已经解析了www.ljjpm.co ...

  4. lua之m进制转换为n进制-任意进制转换算法

    够无聊的写这个,为防止需要的人也无聊一遍,写个吧 算法有n种,但是,咱们一种就够用了 --数组倒序排列 local function orderByDesc( input ) local output ...

  5. Ios国际化翻译工具

    IOS Translation Tool(IOS国际化翻译工具) 介绍 当IOS项目国际化的时候,手工去翻译每一个字符串是一件非常痛苦的事情.尤其是当项目中存在N多种语言.而且又很难保证,手工翻译的准 ...

  6. 关于token,session,cookie的概念和区别

    记录几篇讲的比较好的文章 https://www.cnblogs.com/moyand/p/9047978.html https://blog.csdn.net/wabiaozia/article/d ...

  7. Java使用iText生成word文件的完美解决方案(亲测可行)

    JAVA生成WORD文件的方法目前有以下种: 一种是jacob 但是局限于windows平台 往往许多JAVA程序运行于其他操作系统 在此不讨论该方案 一种是pio但是他的excel处理很程序 wor ...

  8. websphere 配置库中已存在应用程序,异常处理

    from:http://mengdboy.iteye.com/blog/1677379 出现此问题的原因之一:操作界面上没有卸载完成. 进行一下操作: 1.删除 $WAS_HOME/profiles/ ...

  9. 手把手带你理解style

    在写代码的时候,经常遇到自定义的style,有的用来设置属性,有的用来设置主题,搞的自己云里雾里,因此在心底暗暗发誓,等到空闲的时候,一定好好学学android中的style的究竟是个什么东西,到底有 ...

  10. 使用 Open Live Writer 创建我的第一个博文

    希望能在此记录我的技术开发过程. 请记住我的博客首页为:https://www.cnblogs.com/unrulife/ 期待在博客园遇到志同道合的朋友! 希望在博客园开启技术生涯的新篇章!