程序流程:
- 服务器启动, 如果没有指定端口则随机选取端口建立套接字监听客户端连接
- accept()会一直阻塞等待客户端连接, 如果客户端连接上, 则创建一个新线程处理该客户端连接.
- 在accetp_request() 主要处理客户端连接, 首先解析HTTP请求报文. 只支持GET/POST请求, 否则返回HTTP501错误. 如果有请求参数的话, 记录在query_string中. 将请求的路径记录在path中, 如果请求的是目录, 则访问该目录下的index.html文件.
- 最后判断请求类型, 如果是静态请求, 直接读取文件发送给客户端; 如果是动态请求, 则fork()一个子进程, 在子进程中调用exec()函数簇执行cgi脚本. 然后父进程读取子进程执行结果 父子进程之间通过管道通信实现.(针对POST请求,需要fork出一个子进程来处理,然后建立两个管道来与浏览器或者客户端通信,这里需要使用管道而不是子进程直接处理socket的原因是我们的父进程已经读取了一部分内容.)
- 父进程等待子进程结束后, 关闭连接, 完成一次HTTP请求.
源码分析:
main:
程序入口, 这里建立套接字, 然后与sockaddr_in结构体进行绑定, 然后用listen监听该套接字上的连接请求, 这几步都在startup()中实现.
然后服务器在通过accept接受客户端请求, 如没有请求accept()会阻塞, 如果有请求就会创建一个新线程去处理客户端请求.
int main(void)
{
int server_sock = -1;
u_short port = 4000;
int client_sock = -1;
struct sockaddr_in client_name;
socklen_t client_name_len = sizeof(client_name);
pthread_t newthread;
//在对应端口建立httpd服务
server_sock = startup(&port);
printf("httpd runni