源码分析-简洁的HTTPServer-tinyhttpd
源码分析-简洁的HTTPServer
second60 20180610
1. tinyhttpd 简介
tinyhttpd 可以说是最⼩最精简的HTTP服务器,C语⾔编写,全部代码只有五百多⾏。通过阅读tinyhttpd源码可以了解HTTP服务器搭建的本质和HTTP基础。
2. tinyhttpd 安装和使⽤
httpd编译: make
在编译时,如果报错,在makefile中注释-lsocket.(源码开头注释)
httpd运⾏: ./httpd
simpleclient编译: gcc simpleclient.c -o simpleclient
simpleclient运⾏: ./simpleclient
此程序中的端⼝是随机⽣成的,可以在练习时,固定好⼀个端⼝,或修改成命令⾏传⼊。
3. tinyhttpd 流程
1) 创建服务器套接字,如果端⼝为0,随机⽣成⼀个端⼝,startup
2) 套接字绑定并监听startup
3) 如果有客户端连接,单独开启⼀个线程来处理客户端请求accept_request
a) 获取客户端数据
b) 分析客户端请求数据:GET/POST/query_string/path等
c) ⾮cgi,执⾏serve_file,读取⽂件并返回给客户端(⾮脚本⽂件)
d) cgi存在执⾏execute_cgi(脚本⽂件)
4. http 原理
4.1 HTTP协议流程
1.⽤户在浏览器中键⼊需要访问⽹页的URL或者点击某个⽹页中链接;
2.浏览器根据URL中的域名,通过DNS解析出⽬标⽹页的IP地址;
3.浏览器与⽹页所在服务器建⽴TCP连接;
4.浏览器发送HTTP请求报⽂,获取⽬标⽹页的⽂件;
5.服务器发送HTTP响应报⽂,将⽬标⽹页⽂件发送给浏览器;
6.释放TCP连接;
7.浏览器将⽹页的内容包括⽂本、图像、声⾳等显⽰呈现在⽤户计算机屏幕。
4.2 HTTP请求报⽂格式
HTTP请求报⽂的由请求⾏、请求头部⾏、空⾏和请求数据四部分构成,具体格式如下所⽰:
(请求⾏)⽅法名+空格+URL+空格+版本+回车换⾏(\r\n)
(请求头部⾏1)关键字+“:”+空格+值+回车换⾏(\r\n)
.
..
(请求头部⾏N)关键字+“:”+空格+值+回车换⾏(\r\n)
(空⾏)回车换⾏(\r\n)
(请求数据)……
4.2.1 请求⾏
请求⾏由请求⽅法字段、URL字段和HTTP协议版本字段3个字段组成,它们⽤空格分隔。最后由回车和换⾏表⽰请求⾏结束。例如: GET HTTP/1.1 回车换⾏(\r\n)
HTTP请求报⽂的主要⽅法包括:
GET
POST
HEAD
PUT
DELETE
OPTIONS
TRACE
CONNECT
4.2.2 请求头部⾏(header)
请求头部⾏包括若⼲⾏,每⾏由关键字及其值构成的,关键字和值⽤英⽂冒号“:”分隔,每⼀⾏都由回车换⾏表⽰结束。
请求头部通知服务器有关于客户端请求的信息,典型的请求头部关键字有:
User-Agent产⽣请求的浏览器类型
Accept客户端可识别的内容类型列表
Accept-Language客户端可识别的语⾔类型
Host请求的主机名
Connection告知服务器发送完⽂档后释放连接还是保持连接
4.2.3 空⾏
最后⼀个请求头部之后是⼀个空⾏,发送回车符和换⾏符,通知服务器以下不再有请求头部了。
4.2.4 请求数据
GET⽅法中没有请求数据的内容
POST⽅法使⽤请求数据,⽤于客户端向服务器端填写表单等操作。
4.2.5 HTTP请求例⼦
GET /index.html HTTP/1.1 \r\n
webserver接口开发Host:www.baidu\r\n
User-Agent:Mozilla/5.0
Accept-Language:cn*/*\r\n
4.3 HTTP响应报⽂格式
HTTP响应也由四个部分组成,分别是:状态⾏、消息头部、空⾏和响应正⽂。
其具体格式如下:
(状态⾏)版本+空格+状态码+空格+短语+回车换⾏
(消息头部1)关键字+“:”+空格+值+回车换⾏
……
(消息头部N)关键字+“:”+空格+值+回车换⾏
(空⾏)回车换⾏(\r\n)
(响应正⽂)……
4.3.1 响应状态⾏
在响应报⽂的状态⾏中,版本字的表⽰服务器HTTP协议的版本,状态码字的表⽰服务器发回的响应状态代码;短语字段表⽰状态代码的⽂本描述。
状态码由三位⼗进制数字组成,第⼀个数字定义了响应的类别,有五种可能取值(1-5)
每种状态码的含义如下:
1xx指⽰信息表⽰请求已接收,继续处理
2xx成功表⽰请求已被成功接收、理解、接受
3xx重定向要完成请求必须进⾏更进⼀步的操作
4xx客户端错误请求有语法错误或请求⽆法实现
5xx服务器端错误服务器未能实现合法的请求
常见状态码及状态描述的说明如下:
200OK客户端请求成功
400Bad Request客户端请求有语法错误,不能被服务器所理解
401Unauthorized请求未经授权
403Forbidden服务器收到请求,但是拒绝提供服务
404Not Found请求资源不存在,⽐如输⼊了错误的URL
500Internal ServerError服务器发⽣不可预期的错误
503ServerUnavailable服务器当前不能处理客户端的请求,⼀段时间后可能恢复正常
4.3.2 响应消息头部
消息头部与请求头部的格式相似,也是包含若⼲⾏,每⾏由关键字及其值构成
常⽤的关键字包括:
Date表⽰返回消息的时间
Content-Type表⽰返回消息的内容类型
Content-Length返回内容的长度(字节数)
Server使⽤的服务器软件及其版本号
4.3.3 空⾏
同样,最后⼀个消息头部之后是⼀个空⾏,发送回车符和换⾏符,通知客户端以下不再有消息头部了。(注:空⾏必须有)
4.3.4 响应正⽂
响应正⽂部分是服务器端根据客户端的请求发回的具体⽂档内容,以HTML语⾔表⽰。
4.4 CGI 简介
CGI(Common Gateway Interface) 是WWW技术中最重要的技术之⼀,有着不可替代的重要地位。CGI是外部应⽤程序(CGI程序)与之间的接⼝标准,是在CGI程序和Web服务器之间传递信息的过程。CGI规范允许Web服务器执⾏外部程序,并将它们的输出发送给Web浏览器,CGI将Web的⼀组简单的静态超媒体⽂档变成⼀个完整的新的交互式媒体。
5 源码分析
/* J. David's webserver */
/* This is a simple webserver.
* Created November 1999 by J. David Blackstone.
* CSE 4344 (Network concepts), Prof. Zeigler
* University of Texas at Arlington
*/
/* This program compiles for Sparc Solaris 2.6.
* To compile for Linux:
* 1) Comment out the #include <pthread.h> line.
* 2) Comment out the line that defines the variable newthread.
* 3) Comment out the two lines that run pthread_create().
* 4) Uncomment the line that runs accept_request().
* 5) Remove -lsocket from the Makefile.
*/
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <ctype.h>
#include <strings.h>
#include <string.h>
#include <sys/stat.h>
#include <pthread.h>
#include <sys/wait.h>
#include <stdlib.h>
#define ISspace(x) isspace((int)(x))
#define SERVER_STRING "Server: jdbhttpd/0.1.0\r\n"
void accept_request(int);
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);
/**********************************************************************/
/* A request has caused a call to accept() on the server port to
* return. Process the request appropriately.
* Parameters: the socket connected to the client */
/
**********************************************************************/
// 有客户端连接进来时,开启⼀个线程单独处理⼀个客户端连接void accept_request(int client)
{
char buf[1024];
int numchars;
char method[255];
char url[255];
char path[512];
size_t i, j;
struct stat st;
int cgi = 0; /* becomes true if server decides this is a CGI
* program */
char *query_string = NULL;
// 获取客户端数据
numchars = get_line(client, buf, sizeof(buf));
i = 0; j = 0;
while (!ISspace(buf[j]) && (i < sizeof(method) - 1))
{
method[i] = buf[j];
i++; j++;
}
method[i] = '\0';
if (strcasecmp(method, "GET") && strcasecmp(method, "POST")) {
unimplemented(client);
return;
}
if (strcasecmp(method, "POST") == 0)
cgi = 1;
i = 0;
while (ISspace(buf[j]) && (j < sizeof(buf)))
j++;
while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf)))
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论