微型Web服务器

CSAPP这本书好厚,曾经一度以为可能买了就是吃灰,很难读完。每次拿起觉得太厚了,简单的翻翻又放下。后来我就不整体看,每周花个1-2个小时的时间,只看一章,看完一章在目录打个勾,而看书的顺序,完全看自己的喜好,没想到,还比较轻松,这么厚的书,可能还有2-3章就读完了,感觉这个办法对于读厚书来说还是不错的。以后就用这种方法读书吧。

这篇文章就是按照第11章的代码来实践和测试一个简单的web服务器,麻雀虽小,五脏俱全。通过简单的实现代码,可以帮助我们了解HTTP协议的基本内容,了解一般的web工作原理。

一 有什么用

除了了解web流程和http协议之外,这个web服务器有什么应用场景那,我想还是有的。比如:

  • 嵌入在你的程序里面,可以做程序的内部状态的监控,前台需要了解程序的运行状态,调用你的http服务就可以了,还不用担心为此单独部署一个web服务器。

  • 在家用路由器等小型的设备上,如果部署个常见的tomcat或jetty,nginx等服务器比较重,占内存和容量都是不可接受的,而且路由器管理的需要的功能有限,可以嵌入这种小型的服务器。

  • 可以做个简单的后门程序

二 Web服务器工作原理

简化版本的http交互流程图如下:从上图可以看出,其实web服务器的功能从整体看还是比较简单,就是接受web请求,解析http协议请求内容,然后处理请求,返回。这里面处理分为两类:

  1. 静态内容处理,比如读取本地的html文件,css文件,图片文件直接返回。

  2. 动态内容处理,比如执行一个可执行文件,将结果返回给客户端。静态文件内容处理,没啥好说的,就是读取文件,把内容write发送给客户端;动态内容涉及到的问题比较多,如何把参数传递给服务器,服务器如何调用可执行文件执行,可执行文件的执行结果又以怎么样的形式返回给客户端。一旦有可能产生混乱的地方也就有了标准,CGI就是这个标准。

  • 参数传递:CGI定义了很多环境变量,http服务器可以设置环境变量,调用程序时候,程序通过环境变量:QUERY_STRING 来获取参数。

  • 执行结果返回:可以将CGI程序的输出重定向到客户端的描述符上,从而达到返回的目的。

三 实践

代码引用了书里面的封装的代码,本想把整个代码都拿出来的,发现可能也挺无聊的,找些重点的有意思的代码分析下,就够了:

// 处理静态文件
void serve_static(int fd, char* filename, int filesize)
{
    int srcfd;
    char *srcp, filetype[MAXLINE], buf[MAXBUF];
    get_filetype(filename, filetype);
    sprintf(buf, "HTTP/1.0 200 OK\r\n");
    sprintf(buf, "%sServer: Tiny Web Server\r\n", buf);
    sprintf(buf, "%sContent-length: %d\r\n", buf, filesize);
    sprintf(buf, "%sContent-type: %s\r\n\r\n", buf, filetype);
    // 将返回头信息发送给客户端
    Rio_writen(fd, buf, strlen(buf));


    srcfd = Open(filename, O_RDONLY, 0);
    srcp = Mmap(0, filesize, PROT_READ, MAP_PRIVATE, srcfd, 0);
    Close(srcfd);
    Rio_writen(fd, srcp, filesize);
    Munmap(srcp, filesize);
}

其中:

srcfd = Open(filename, O_RDONLY, 0);
srcp = Mmap(0, filesize, PROT_READ, MAP_PRIVATE, srcfd, 0);
Close(srcfd);

Mmap函数创建一个新的只读的私有的虚拟存储器区域,将文件描述符srcfd指向的文件,映射到这个内存区域,返回的是指向新虚拟内存区域的地址,这样可以减少一次内存的拷贝。

看下调用CGI程序提供动态服务的函数:

void serve_dynamic(int fd, char* filename, char* cgiargs)
{
    char buf[MAXLINE], *emptylist[] = { NULL };

    // 返回结果头部设置
    sprintf(buf, "HTTP/1.0 200 OK\r\n");
    Rio_writen(fd, buf, strlen(buf));
    sprintf(buf, "Server: Tiny Web Server\r\n");
    Rio_writen(fd, buf, strlen(buf));
    // 子线程执行
    if (Fork() == 0) {
        // 设置CGI 应用使用的参数
        setenv("QUERY_STRING", cgiargs, 1);
        // 将CGI应用的输出重定向到客户端的socket上去
        Dup2(fd, STDOUT_FILENO);
       // 执行CGI应用
        Execve(filename, emptylist, environ);
    }
    // 回收
    Wait(NULL);
}

关键函数就是两步:

        // 设置CGI 应用使用的参数
        setenv("QUERY_STRING", cgiargs, 1);
        // 将CGI应用的输出重定向到客户端的socket上去
        Dup2(fd, STDOUT_FILENO);

像上面我们说的一样,CGI程序通过获取QUERY_STRING环境变量来获取它的执行参数。获取的时候:

// 获取环境变量
char * buf = getenv("QUERY_STRING");

五 测试

5.1  基本html测试:

image.png

5.2  CGI测试

CGI测试

如果改成了系统的一些命令,再增加交互,就是一个后门了。

如果需要代码,可以联系我。

六诗词欣赏

雨霖铃·寒蝉凄切

[宋]  [柳永]

寒蝉凄切,对长亭晚,骤雨初歇。
都门帐饮无绪,留恋处,兰舟催发。
执手相看泪眼,竟无语凝噎。
念去去,千里烟波,暮霭沉沉楚天阔。

多情自古伤离别,更那堪冷落清秋节!
今宵酒醒何处?杨柳岸,晓风残月。
此去经年,应是良辰好景虚设。
便纵有千种风情,更与何人说?


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值