TKeed Webserver
main.cpp
本篇从main.c中逐一解析函数、结构体(边记边学,欢迎指正)
#include <stdio.h>
#include "threadpool.h"
#include "http.h"
#define DEFAULT_CONFIG "tkeed.conf"
extern struct epoll_event *events;
char *conf_file = DEFAULT_CONFIG;
tk_conf_t conf;
// 读取配置文件
read_conf(conf_file, &conf);
https://blog.csdn.net/jojozym/article/details/104825583 // read_conf函数详解
// 处理SIGPIPE
handle_for_sigpipe();
https://blog.csdn.net/jojozym/article/details/104825787 // handle_for_sigpipe详解
//初始化套接字开始监听
int listen_fd = socket_bind_listen(conf.port);
https://blog.csdn.net/jojozym/article/details/104826186 //socket_bind_listen函数详解
// 设置为socket非阻塞
int rc = make_socket_non_blocking(listen_fd);
https://blog.csdn.net/jojozym/article/details/104826653 //make_socket_non_blocking详解
//创建epoll并注册监听描述符
int epoll_fd = tk_epoll_create(0);
tk_http_request_t* request = (tk_http_request_t*)malloc(sizeof(tk_http_request_t));
tk_init_request_t(request, listen_fd, epoll_fd, conf.root);
tk_epoll_add(epoll_fd, listen_fd, request, (EPOLLIN | EPOLLET));
https://blog.csdn.net/jojozym/article/details/104834862 //tk_epoll_create详解
https://blog.csdn.net/jojozym/article/details/104835199 //tk_init_request_t详解
https://blog.csdn.net/jojozym/article/details/104835627 //tk_epoll_add详解
// 初始化线程池
tk_threadpool_t *tp = threadpool_init(conf.thread_num);
https://blog.csdn.net/jojozym/article/details/104861812 //threadpool_init详解
// 初始化计时器
tk_timer_init();
之后进入循环
while(1){
// 得到最近且未删除时间和当前时间差值(等待时间)
int time = tk_find_timer();
// 调用epoll_wait函数,返回接收到事件的数量
int events_num = tk_epoll_wait(epoll_fd, events, MAXEVENTS, -1);
// 处理已经超时的请求
tk_handle_expire_timers();
// 遍历events数组,根据监听种类及描述符类型分发操作
tk_handle_events(epoll_fd, listen_fd, events, events_num, conf.root, tp);
}
// 回收线程资源
// threadpool_destroy(tp, graceful_shutdown);
}
read_conf
read_conf函数在util.cpp中
#include "util.h"
#include "http_request.h"
#include "epoll.h"
函数定义如下:
int read_conf(char* filename, tk_conf_t* conf) {
// 以只读方式打开文件
FILE* fp = fopen(filename, "r");
if (!fp)
return TK_CONF_ERROR;
char buff[BUFLEN];
int buff_len = BUFLEN;
char* curr_pos = buff;
char* delim_pos = NULL;
int i = 0;
int pos = 0;
int line_len = 0;
while (fgets(curr_pos, buff_len - pos, fp)) {
// 定位每行第一个界定符位置
delim_pos = strstr(curr_pos, DELIM);
if (!delim_pos)
return TK_CONF_ERROR;
if (curr_pos[strlen(curr_pos) - 1] == '\n') {
curr_pos[strlen(curr_pos) - 1] = '\0';
}
// 得到root信息
if (strncmp("root", curr_pos, 4) == 0) {
delim_pos = delim_pos + 1;
while (*delim_pos != '#') {
conf->root[i++] = *delim_pos;
++delim_pos;
}
}
// 得到port值
if (strncmp("port", curr_pos, 4) == 0)
conf->port = atoi(delim_pos + 1);
// 得到thread数量
if (strncmp("thread_num", curr_pos, 9) == 0)
conf->thread_num = atoi(delim_pos + 1);
// line_len得到当前行行长
line_len = strlen(curr_pos);
// 当前位置跳转至下一行首部
curr_pos += line_len;
}
fclose(fp);
return TK_CONF_OK;
}
read_conf函数功能:
1、main.c传tkeed.conf给util.cpp
2、util.cpp负责从tkeed.conf中读取root、port、thread_num等信息,赋值给tk_conf_t* conf
tk_conf_t在util.h头文件中有定义。
typedef struct tk_conf {
char root[PATHLEN];
int port;
int thread_num;
}tk_conf_t;
端口的取值范围是:0-65535。
在这个取值范围中1023以下的端口已经分配给了常用的一些应用程序,这个数字以后的端口部分被使用,所以网络编程可用的端口一般在1024之后选取。
在网络技术中,端口(Port)大致有两种意思:
1、物理意义上的端口,比如,ADSL Modem、集线器、交换机、路由器用于连接其他网络设备的接口,如RJ-45端口、SC端口等等;
2、逻辑意义上的端口,一般是指TCP/IP协议中的端口,端口号的范围从0到65535,比如用于浏览网页服务的80端口,用于FTP服务的21端口等等。
handle_for_sigpipe
handle_for_sigpipe函数在util.c中
函数定义如下:
void handle_for_sigpipe() {
struct sigaction sa;
memset(&sa, '\0', sizeof(sa));
sa.sa_handler = SIG_IGN;//忽略信号,也可以选择另一个参数SIG_DFL,它俩都在signal.h里
sa.sa_flags = 0;//只接收信号,不需要其他额外信息,设置为0
if (sigaction(SIGPIPE, &sa, NULL))
}
SIG_DFL 默认信号处理
SIG_IGN 忽略信号
定义函数 int sigaction(int signum,const struct sigaction *act ,struct sigaction *oldact);
函数说明 sigaction()会依参数signum指定的信号编号来设置该信号的处理函数。参数signum可以指定SIGKILL和SIGSTOP以外的所有信号。
结构体 sigaction定义如下:
struct sigaction {
union {
__sighandler_t _sa_handler;
void (*_sa_sigaction)(int, struct siginfo *, void *);
} _u;
sigset_t sa_mask;
unsigned long sa_flags;
void (*sa_restorer)(void);
};
#define sa_handler _u._sa_handler
#define sa_sigaction _u._sa_sigaction
#endif /* __KERNEL__ */
sa_handler的原型是一个参数为int,返回类型为void的函数指针。参数即为信号值,所以信号不能传递除信号值之外的任何信息;
sa_sigaction的原型是一个带三个参数,类型分别为int,struct siginfo *,void *,返回类型为void的函数指针。第一个参数为信号值;第二个参数是一个指向struct siginfo结构的指针,此结构中包含信号携带的数据值;第三个参数没有使用。
sa_mask指定在信号处理程序执行过程中,哪些信号应当被阻塞。默认当前信号本身被阻塞。
sa_flags包含了许多标志位,比较重要的一个是SA_SIGINFO,当设定了该标志位时,表示信号附带的参数可以传递到信号处理函数中。即使sa_sigaction指定信号处理函数,如果不设置SA_SIGINFO,信号处理函数同样不能得到信号传递过来的数据,在信号处理函数中对这些信息的访问都将导致段错误。
sa_restorer已过时,POSIX不支持它,不应再使用。
因此,当你的信号需要接收附加信息的时候,你必须给sa_sigaction赋信号处理函数指针,同时还要给sa_flags赋SA_SIGINFO,类似下面的代码:
#include <signal.h>
……
void sig_handler_with_arg(int sig,siginfo_t *sig_info,void *unused){……}
int main(int argc,char **argv)
{
struct sigaction sig_act;
……
sigemptyset(&sig_act.sa_mask);
sig_act.sa_sigaction=sig_handler_with_arg;
sig_act.sa_flags=SA_SIGINFO;
……
}
如果你的应用程序只需要接收信号,而不需要接收额外信息,那你需要的设置的是sa_handler,而不是sa_sigaction,你的程序可能类似下面的代码:
#include <signal.h>
……
void sig_handler(int sig){……}
int main(int argc,char **argv)
{
struct sigaction sig_act;
……
sigemptyset(&sig_act.sa_mask);
sig_act.sa_handler=sig_handler;