main函数分析;
一,参数设置: 读取命令行参数
配置文件参数
读取参数,设置对应的全局变量。主要参数有:配置文件;资源目录;进程ID文件;日志文件;字符集;主机名及端口号...
二,参数处理:重点分析,主机名及端口号,damon
1,主机名及端口号
解析主机名,获取对应IP地址,初始化监听套接字(包括:socket,bind,listen)
以IPv4为例:
listen_fd=socket(AF_INET,SOCK_STREAM,0);创建套接字
套接字选项设置:设置方法有fcntl,setsockopt
1,fcntl(listen_fd,F_SETFD,FD_CLOEXEC); 设置close-on-exec,exec进程时,关闭此文件描述符
2, int i=1;
setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,(void*)&i,sizeof(i));可重用本地地址
3,bind(listen_fd,struct sockaddr* -,sizeof(-));绑定套接字和IP:端口
4,listen(listen_fd,1024);设置监听套接字最大队列长度
2,若不是调试模式,则应该设置为damon模式,
switch(fork()){
case 0: break; 子进程继续执行
case -1: 出错,退出程序
default:exit(-1); 父进程退出
}
3,信号处理设置:
SIGTERM: kill发送系统中断进程信号
SIGINT: 用户终端Ctrl+C中断进程
SIGHUP:一般用来通知守护进程再次读取配置文件等作用,因为守护进程没有终端,不会收到此信号。此处用于再次读取日志文件
SIGCHLD:子进程退出时,会发送该信号给父进程,父进程应该捕捉,否则将产生僵尸进程
SIGPIPE:管道读关闭时写,套接字不再连接时写,都会产生此信号。网络编程中出现的很多
4,服务器主循环:
若收到SIGHUP信号,则再次打开日志文件(覆盖),若需要可备份
select模式,多路复用:阻塞模式
FD_ZERO(&lfdset);清空描述符集
FD_SET(listen_fd,&lfdset);条件套接字描述符到描述符集中
设置select阻塞读,select(listen_fd+1,&lfdset,NULL,NULL,NULL);永远阻塞,直到listen_fd可读;
若listen_fd可读,调用accept,获得已连接套接字描述符,若出错(EAGAIN,资源暂不可用;EINTR,中断函数调用;ECONNABORT,连接中断。continue,try again),否则异常退出。
若连接成功,fork(),子进程处理请求,父进程继续循环,监听。
子进程关闭监听套接字,调用 handle_request 函数处理请求,退出;父进程关闭已连接套接字,继续循环监听。
5,handle_request函数解析
超时设置:利用信号SIGALRM,alarm(60);
[设置TCP_NOPUSH套接字选项]
读取请求:循环read,把读取的数据存入全局变量request中,每次读取后,重新设置超时alarm(60)
请求解析: get_request_line()
解析请求的第一行:分三部分:方法,路径,协议
解析请求其余部分:name:value...初始化对应的全局变量
方法字符串分解:方法(GET,HEAD,POST),查询字符串query,?之后
路径解析:获取文件名file
超时设置,SIGALRM(写超时设置)
响应:构造请求响应(报头及实体),写套接字(循环write),发送响应。