源码阅读顺序
main —> startup —> accept_request —> excute_cgi
先看一个十分基本的服务端客户端模型代码
图片来源
https://baike.baidu.com/item/SOCKADDR_IN/3917215?fr=aladdin
工作流程
图片来自:
https://blog.csdn.net/sinat_19596835/article/
1.服务器启动,在指定端口或随机选取端口绑定httpd服务
2.收到一个HTTP请求时(listen端口accept的时候),派生一个线程运行
accept_request函数
3.取出HTTP请求中的method(GET或POST)和url。对于GET方法,如果有携带参数,则query_string指针指向url里 '?'后面的GET参数。
4.格式化url到path数组,表示浏览器请求的服务器文件路径,在tinyhttpd中服务器文件时在 htdocs文件夹下。当 url 以/结尾,或url是个目录,则默认在path中加上index.html.
5.如果文件路径合法,对于无参数的GET请求,直接输出服务器文件到浏览器,即用HTTP格式写到套接字上
7.建立两个管道,cgi_input和cgi_outputt,把STDIN重定向到cgi_input的读取端,关闭cgi_input的写入端的cgi_output的读取端,设置request_method的环境变量,GET的话设置query_string的环境变量,POST的换设置content_length的环境变量,这些环境变量都是为了给cgi脚本用,接着运行cgi程序。
9.在父进程中,关闭cgi_input的读取端和cgi_output的写入端,如果POST的话
首先来看main函数
int main(void)
{
//声明一个socket
int server_sock = -1;
//默认端口号
u_short port = 0;
//声明一个服务器
int client_sock = -1;
//此数据结构用做bind、connect、recvfrom、sendto等函数的参数,指明地址信息(摘自百度百科)
struct sockaddr_in client_name;
int client_name_len = sizeof(client_name);
pthread_t newthread;
server_sock = startup(&port);
printf("httpd running on port %d\n", port);
//持续监听,每收到一个请求就开启一个新线程
while (1)
{
client_sock = accept(server_sock,
(struct sockaddr *)&client_name,
&client_name_len);
//如果client_sock返回的是-1,则调用失败
if (client_sock == -1)
error_die("accept");
//成功则创建新的线程,调用accept_request来处理,accept_request通过client_sock与客户机通讯
/* accept_request(client_sock); */
if (pthread_create(&newthread , NULL, accept_request, client_sock) != 0)
perror("pthread_create");
}
//关闭与客户端的连接,因为TCP/IP协议是无连接的
close(server_sock);
return(0);
}
start_up
/*服务器端套接字初始化设置*/
int startup(u_short *port)
{
int httpd = 0;
struct sockaddr_in name;
httpd = socket(PF_INET, SOCK_STREAM, 0);//创建服务器端套接字,IPV4,TCP协议,返回server_socket的fd
if (httpd == -1)
error_die("socket");
memset(&name, 0, sizeof(name));//初始化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)) < 0)//绑定到指定地址和端口
error_die("bind");
if (*port == 0) /* if dynamically allocating a port *///动态分配一个端口
{
int namelen = sizeof(name);