1、简介

tinyhttpd是一个开源的超轻量型Http Server,阅读其源码,可以对http协议,微型服务器有进一步的了解。

源码链接

参考博客:tinyhttpd源码分析

2、笔记

------------------------------------------------------2016年5月7日21:31:35----------------------------------------------------

函数原型目录,列举出整个项目所要用到的所有函数原型

 void accept_request(void *);
void bad_request(int);
void cat(int, FILE *);
void cannot_execute(int);
void error_die(const char *);
void execute_cgi(int, const char *, const char *, const char *);
int get_line(int, char *, int);
void headers(int, const char *);
void not_found(int);
void serve_file(int, const char *);
int startup(u_short *);
void unimplemented(int);

每个函数的作用在项目的readme文件中有简单的描述:

函数 描述
accept_request 处理从套接字上监听到的一个 HTTP 请求,在这里可以很大一部分地体现服务器处理请求流程
bad_request 返回给客户端这是个错误请求,HTTP 状态吗 400 BAD REQUEST
cat 读取服务器上某个文件写到 socket 套接字
cannot_execute 主要处理发生在执行 cgi 程序时出现的错误
error_die 把错误信息写到 perror 并退出
execute_cgi 运行 cgi 程序的处理,也是个主要函数
get_line 读取套接字的一行,把回车换行等情况都统一为换行符结束
headers 把 HTTP 响应的头部写到套接字
not_found 主要处理找不到请求的文件时的情况
sever_file 调用 cat 把服务器文件返回给浏览器
startup 初始化 httpd 服务,包括建立套接字,绑定端口,进行监听等
unimplemented 返回给浏览器表明收到的 HTTP 请求所用的 method 不被支持

建议的源码阅读顺序:

main--->startup--->accept_request--->execute_cgi

按照这个顺序理清整个服务器工作流程。

main函数

 int main(void)
{
int server_sock = -;
u_short port = ;
int client_sock = -;
struct sockaddr_in client_name;
socklen_t client_name_len = sizeof(client_name);
pthread_t newthread; server_sock = startup(&port);
printf("httpd running on port %d\n", port); while ()
{
client_sock = accept(server_sock,
(struct sockaddr *)&client_name,
&client_name_len);
if (client_sock == -)
error_die("accept");
/* accept_request(client_sock); */
if (pthread_create(&newthread , NULL, (void *)accept_request, (void *)&client_sock) != )
perror("pthread_create");
} close(server_sock); return();
}

第3-7行是socket相关变量定义,有关socket的详细介绍可以上网查阅相关资料或者查看书籍《Unix网络编程》;

第10行调用startup函数,有关该函数的内容后续简要解释;

第13-23行,监听客户端向服务端发来的请求连接,当接收一个连接之后,创建一个线程执行相应的请求,线程入口函数是accept_request,另外有关Linux中pthread_create的使用需要注意一些细节问题。

第25行关闭服务端的套接字。

startup函数

函数参数是一个端口号,用于创建对应端口号的tcp套接字

 int startup(u_short *port)
{
int httpd = ;
struct sockaddr_in name; httpd = socket(PF_INET, SOCK_STREAM, );
if (httpd == -)
error_die("socket");
memset(&name, , sizeof(name));
name.sin_family = AF_INET;
name.sin_port = htons(*port);
name.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < )
error_die("bind");
if (*port == ) /* if dynamically allocating a port */
{
socklen_t namelen = sizeof(name);
if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -)
error_die("getsockname");
*port = ntohs(name.sin_port);
}
if (listen(httpd, ) < )
error_die("listen");
return(httpd);
}

第6行创建一个socket套接字,由于其参数是SOCK_STREAM,因此创建的是tcp套接字;

第7-8行,如果套接字创建不成功,则调用封装好的错误处理函数error_die,该函数比较简单,就是打印错误信息并结束整个程序。

第13-23则是创建socket服务端的基本流程,先是bind操作,接下来listen操作;

注意第15行,如果port为0,表示端口号是通过系统自动分配的,需要通过getsockname获取,有关这方面的知识查看《Unix网络编程》书籍

Startup函数执行完成后,服务端的准备工作完成,将进入监听状态,等待客户端的连接,通过accept函数判断是否有客户端请求连接,如果有,服务端创建一个新的线程执行客户请求,线程的入口函数是accept_request;

accept_request函数

该函数的参数是个void*,实际传递给函数的参数是一个套接字描述符。

 void accept_request(void *arg)
{
int client = *(int*)arg;
char buf[];
size_t numchars;
char method[];
char url[];
char path[];
size_t i, j;
struct stat st;
int cgi = ; /* becomes true if server decides this is a CGI
* program */
char *query_string = NULL; numchars = get_line(client, buf, sizeof(buf));
i = ; j = ;
while (!ISspace(buf[i]) && (i < sizeof(method) - ))
{
method[i] = buf[i];
i++;
}
j=i;
method[i] = '\0'; if (strcasecmp(method, "GET") && strcasecmp(method, "POST"))
{
unimplemented(client);
return;
} if (strcasecmp(method, "POST") == )
cgi = ; i = ;
while (ISspace(buf[j]) && (j < numchars))
j++;
while (!ISspace(buf[j]) && (i < sizeof(url) - ) && (j < numchars))
{
url[i] = buf[j];
i++; j++;
}
url[i] = '\0'; if (strcasecmp(method, "GET") == )
{
query_string = url;
while ((*query_string != '?') && (*query_string != '\0'))
query_string++;
if (*query_string == '?')
{
cgi = ;
*query_string = '\0';
query_string++;
}
} sprintf(path, "htdocs%s", url);
if (path[strlen(path) - ] == '/')
strcat(path, "index.html");
if (stat(path, &st) == -) {
while ((numchars > ) && strcmp("\n", buf)) /* read & discard headers */
numchars = get_line(client, buf, sizeof(buf));
not_found(client);
}
else
{
if ((st.st_mode & S_IFMT) == S_IFDIR)
strcat(path, "/index.html");
if ((st.st_mode & S_IXUSR) ||
(st.st_mode & S_IXGRP) ||
(st.st_mode & S_IXOTH) )
cgi = ;
if (!cgi)
serve_file(client, path);
else
execute_cgi(client, path, method, query_string);
} close(client);
}

第3-13行,变量说明部分;

第15行,通过get_line函数,从指定的套接字描述符的缓存中读取一行数据,数据长度为size,但是如果遇到'\n',函数直接返回,也就是这一行内容最多有size字节,函数返回实际读取到的字节数。

第17-23行,获取http请求头部;理解这一部分需要对http协议报文格式有所了解

Tinyhttpd阅读笔记的更多相关文章

  1. 阅读笔记 1 火球 UML大战需求分析

    伴随着七天国庆的结束,紧张的学习生活也开始了,首先声明,阅读笔记随着我不断地阅读进度会慢慢更新,而不是一次性的写完,所以会重复的编辑.对于我选的这本   <火球 UML大战需求分析>,首先 ...

  2. [阅读笔记]Software optimization resources

    http://www.agner.org/optimize/#manuals 阅读笔记Optimizing software in C++   7. The efficiency of differe ...

  3. 《uml大战需求分析》阅读笔记05

    <uml大战需求分析>阅读笔记05 这次我主要阅读了这本书的第九十章,通过看这章的知识了解了不少的知识开发某系统的重要前提是:这个系统有谁在用?这些人通过这个系统能做什么事? 一般搞清楚这 ...

  4. <<UML大战需求分析>>阅读笔记(2)

    <<UML大战需求分析>>阅读笔记(2)> 此次读了uml大战需求分析的第三四章,我发现这本书讲的特别的好,由于这学期正在学习设计模式这本书,这本书就讲究对uml图的利用 ...

  5. uml大战需求分析阅读笔记01

    <<UML大战需求分析>>阅读笔记(1) 刚读了uml大战需求分析的第一二章,读了这些内容之后,令我深有感触.以前学习uml这门课的时候,并没有好好学,那时我认为这门课并没有什 ...

  6. Hadoop阅读笔记(七)——代理模式

    关于Hadoop已经小记了六篇,<Hadoop实战>也已经翻完7章.仔细想想,这么好的一个框架,不能只是流于应用层面,跑跑数据排序.单表链接等,想得其精髓,还需深入内部. 按照<Ha ...

  7. Hadoop阅读笔记(六)——洞悉Hadoop序列化机制Writable

    酒,是个好东西,前提要适量.今天参加了公司的年会,主题就是吃.喝.吹,除了那些天生话唠外,大部分人需要加点酒来作催化剂,让一个平时沉默寡言的码农也能成为一个喷子!在大家推杯换盏之际,难免一些画面浮现脑 ...

  8. Hadoop阅读笔记(五)——重返Hadoop目录结构

    常言道:男人是视觉动物.我觉得不完全对,我的理解是范围再扩大点,不管男人女人都是视觉动物.某些场合(比如面试.初次见面等),别人没有那么多的闲暇时间听你诉说过往以塑立一个关于你的完整模型.所以,第一眼 ...

  9. Hadoop阅读笔记(四)——一幅图看透MapReduce机制

    时至今日,已然看到第十章,似乎越是焦躁什么时候能翻完这本圣经的时候也让自己变得更加浮躁,想想后面还有一半的行程没走,我觉得这样“有口无心”的学习方式是不奏效的,或者是收效甚微的.如果有幸能有大牛路过, ...

随机推荐

  1. 懒人小工具:T4自动生成Model,Insert,Select,Delete以及导出Excel的方法

    之前写了篇文章,懒人小工具:[自动生成Model,Insert,Select,Delete以及导出Excel的方法](http://www.jianshu.com/p/d5b11589174a),但是 ...

  2. PHP垃圾回收机制理解

    使用的是"引用计数"方式进行回收.简单地理解的话,就是每个分配的内存区域都有一个计数器,记录有多少个变量指针指向这片内存.当指向该片内存的指针数量为0,那么该片内存区域就可以被回收 ...

  3. [js高手之路] html5 canvas系列教程 - 文本样式(strokeText,fillText,measureText,textAlign,textBaseline)

    接着上文线条样式[js高手之路] html5 canvas系列教程 - 线条样式(lineWidth,lineCap,lineJoin,setLineDash)继续. canvas提供两种输出文本的方 ...

  4. JAVA多线程---volatile关键字

    加锁机制既可以确保可见性又可以保证原子性,而volatile变量只能确保可见性. 当把变量声明为volatile时候 编译器与运行时都会注意到这个变量是共享的,不会将该变量上的操作与其他内存操作一起重 ...

  5. Android Framework 初探

    最近工作任务不忙,学习一下Android Framework方面的知识. 一.介绍,是什么 Android的Framework是直接应用之下的一层,叫做应用程序框架层.这一层是核心应用程序所使用的AP ...

  6. 记录各种IE兼容问题,IE6,IE7,IE8,IE9,IE10

     记录遇到的IE BUG:  1.IE8开发者工具打不开 解决办法:IE8新增了开发人员工具,非常不错,比早期的DevToolbar好用多了.不过在我的Win7下 使用的时候偶尔会出现一个莫名其妙的问 ...

  7. Java比较器

    导语 本节内容,比较器Comparable是核心内容. 主要内容 重新认识Arrays类 两种比较器的使用 具体内容 Arrays类 在之前一直使用的"java.util.Arrays.so ...

  8. ubuntu环境配置eclipse+opencv

    blockquote { direction: ltr; color: rgb(0, 0, 0) } blockquote.western { font-family: "Liberatio ...

  9. Python自学笔记-Django分页器小实例

    from django.core.paginator import Paginator iter = 'abcdefhijklmnopqw' paginator = Paginator(iter,4) ...

  10. javaWeb学习笔记——关于交叉连接-内连接-左外连接-右外连接的区别

    废话不说:直接上图1 图1-1 table1表 图1-2 table2 图1-3 cross join 交叉连接 图1-4 显示内连接 图1-5 左外链接 图1-6 右外链接