我是靠谱客的博主 听话火车,最近开发中收集的这篇文章主要介绍基于c开发的一个超轻量型 HTTP Server附源码Tiny HTTPd源码下载链接,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

Tiny HTTPd

Tiny HTTPd 是一个超轻量型 HTTP Server

https://sourceforge.net/projects/tinyhttpd/

源码下载链接

https://download.csdn.net/download/liufang_imei/87453964

工作流程

  • 服务器启动,在指定端口或随机选取端口绑定 httpd 服务。

  • 收到一个 HTTP 请求时(其实就是 listen 的端口 accpet 的时候),派生一个线程运行 accept_request 函数。

  • 取出 HTTP 请求中的 method (GET 或 POST) 和 url。对于 GET 方法,如果有携带参数,则 query_string 指针指向 url 中 ? 后面的 GET 参数。

  • 格式化 url 到 path 数组,表示浏览器请求的服务器文件路径,在 tiny_httpd 中服务器文件是在 www 文件夹下。当 url 以 / 结尾,或 url 是个目录,则默认在 path 中加上 index.html,表示访问主页。

  • 如果文件路径合法,对于无参数的 GET 请求,直接输出服务器文件到浏览器,即用 HTTP 格式写到套接字上,跳到(10)。其他情况(带参数 GET,POST 方式,url 为可执行文件),则调用 excute_cgi 函数执行 cgi 脚本。

  • 读取整个 HTTP 请求并丢弃headers,如果是 POST 则找出 Content-Length. 把 HTTP 200 状态码写到套接字。

  • 建立两个管道,cgi_input 和 cgi_output, 并 fork 一个进程。

  • 在子进程中,把 STDOUT 重定向到 cgi_output 的写入端,把 STDIN 重定向到 cgi_input 的读取端,关闭 cgi_input 的写入端 和 cgi_output 的读取端,设置 request_method 的环境变量,GET 的话设置 query_string 的环境变量,POST 的话设置 content_length 的环境变量,这些环境变量都是为了给 cgi 脚本调用,接着用 execl 运行 cgi 程序。

  • 在父进程中,关闭 cgi_input 的读取端 和 cgi_output 的写入端,如果 POST 的话,把 POST 数据写入 cgi_input,已被重定向到 STDIN,读取 cgi_output 的管道输出到客户端,该管道输入是 STDOUT。接着关闭所有管道,等待子进程结束。

  • 关闭与浏览器的连接,完成了一次 HTTP 请求与回应。

每个函数的作用

// 处理从套接字上监听到的一个HTTP请求
void accept_request(void *);
// 返回客户端这是个错误的请求,状态码400
void bad_request(int);
// 读取某个文件写到socket套接字
void cat(int, FILE *);
// 处理执行cgi程序时的错误
void cannot_execute(int);
// 把错误信息写到perror并退出
void error_die(const char *);
// 执行cgi程序
void execute_cgi(int, const char *, const char *, const char *);
// 读取套接字的一行,把回车换行等情况都统一为换行符结束
int get_line(int, char *, int);
// 把HTTP响应的头部写到套接字
void headers(int, const char *);
// 请求的资源不存在
void not_found(int);
// 调用cat把文件返回给浏览器
void serve_file(int, const char *);
// 初始化httpd
int startup(u_short *);
// method不被支持
void unimplemented(int);

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

管道相关知识

int pipe(int pipefd[2]);

pipe函数的功能是创建一个用于进程间通信的通道。数组pipefd返回所创建管道的两个文件描述符,其中pipefd[0]表示管道的读端,而pipefd[1]表示管道的写端。从写端写入到管道中的数据由内核进行缓存,直到有进程从读端将数据读出。

pipe函数是基于文件描述符工作的,所以在使用pipe创建的管道要使用read和write调用来读取和发送数据。

另外,管道的读、写行为和一般文件也不相同。当一个进程使用read读取一个空管道时,read会阻塞,直到管道中有数据被写入;当一个进程试图向一个满的管道写入数据时,write将会被阻塞,直到足够多的数据被从管道中读取,write可以将数据全部写入管道中。

// 与pipe函数经常一起使用的还有dup、dup2函数
int dup(int oldfd);
int dup2(int oldfd, int newfd);

两个函数的主要功能都是实现对oldfd文件描述符的复制。其中dup函数使用所有文件描述符中未使用的最小的编号作为新的描述符,而dup2则使用传入的newfd作为oldfd的副本。

套接字相关知识

一个套接字就类似于一扇门。当门打开,外面的人可以通过门进到房子里来,里面的人也可以通过门出去。当门关闭时,就拒绝了和外界的交往。如果两个人互相朝对方打开了一扇门,那么他们之间就建立了联系(连接),彼此就可以相互交流(通信).

int socket(int domain, int type, int protocol);

domain参数是套接字的域(协议簇)

type指定套接字的类型,决定了套接字所采用的通信机制

protocol指定通信所用的协议,一般由套接字域和类型来决定,一般将其设置为0,表示使用默认协议

int bind(int socket, const struct sockaddr *address, size_t address_len);

socket 是所要命名的套接字的标识符,address为要绑定的地址,address_len为地址结构的长度。

注意:在这里,需要将一个特定的地址结构指针转换为通用地址类型(struct sockaddr *)

int listen(int socket, int backlog);

listen函数会创建一个队列来缓存未处理的连接,其中,socket是服务套接字的标识符。backlog为连接队列的最大长度(因为Linux系统通常会对队列中的最大连接数有所限制),当队列中的连接数超过这个值时,后续的连接将被拒绝。backlog参数通常设为5

int accept(int socket, struct sockaddr *address, size_t *address_len);

只有当监听队列中第一个未处理的连接试图连接到由socket参数指定的服务套接字时,函数才返回。accept函数会创建一个新的套接字来与所接受的客户进行通信,并返回新套接字的描述符。新套接字类型与服务套接字类型是一样的。

address参数所指向的sockaddr结构用于存放连接客户的地址,如果程序不需要客户地址,也可以将该参数设为空指针。address_len参数用以指定客户地址结构的长度。如果客户地址长度超过这个值,它将被截断,因此在调用accept之前必须将address_len设置为足够的长度。当函数返回时,address_len将被设置为客户地址结构的实际长度。

如果监听队列中没有未处理的连接,accept函数将阻塞,程序暂停执行,直到有客户连接上为止。当有未处理的客户连接时,accept函数返回一个新套接字的描述符。发生错误时,返回-1。

int close(int socket);

通过调用close函数关闭客户套接字sock,与服务器套接字的连接也自然关闭。其中socket是要关闭的套接字标识符。函数成功时返回0,失败时返回-1。

最后

以上就是听话火车为你收集整理的基于c开发的一个超轻量型 HTTP Server附源码Tiny HTTPd源码下载链接的全部内容,希望文章能够帮你解决基于c开发的一个超轻量型 HTTP Server附源码Tiny HTTPd源码下载链接所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(56)

评论列表共有 0 条评论

立即
投稿
返回
顶部