Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >《Linux高性能服务器编程》学习小结(2)

《Linux高性能服务器编程》学习小结(2)

原创
作者头像
wanyicheng
修改于 2021-02-01 03:13:22
修改于 2021-02-01 03:13:22
2.7K00
代码可运行
举报
运行总次数:0
代码可运行

TCP CGI服务器(进程池)

原文是C++写的,我改写成了C版本。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <stdbool.h>

// 连接socket读缓冲区大小
#define BUFFER_SIZE 1024

// 进程池容量
const int MAX_PROCESS_NUMBER = 16;
// 处理连接任务的任务对象的最大数量
const int USER_PER_PROCESS = 65536;
// 最大epoll监听事件数量
const int MAX_EVENT_NUMBER = 10000;

// 进程数组中的进程容器
struct process {
	pid_t m_pid;
	int m_pipefd[2];
};

// 进程池对象
struct processpoll {
	int m_process_number;
	int m_idx;
	int m_epollfd;
	int m_listenfd;
	int m_stop;
	struct process* m_sub_process;
};

// 连接任务处理对象
struct conn_task {
	int m_epollfd;
	int m_sockfd;
	struct sockaddr_in m_address;
	char m_buf[BUFFER_SIZE];
	int m_read_idx;
};

// 进程池唯一实例
static struct processpoll* m_instance = NULL;

// 信号管道
static int sig_pipefd[2];

// 非阻塞
static int setnonblocking(int fd) {
	int old_option = fcntl(fd, F_GETFL);
	int new_option = old_option | O_NONBLOCK;
	fcntl(fd, F_SETFL, new_option);
	return old_option;
}

// 注册epoll事件
static void addfd(int epollfd, int fd) {
	struct epoll_event event;
	event.data.fd = fd;
	event.events = EPOLLIN | EPOLLET;
	epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
	setnonblocking(fd);
}

// 移除epoll事件
static void removefd(int epollfd, int fd) {
	epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, 0);
	close(fd);
}

// 信号处理函数
static void sig_handler(int sig) {
	int save_errno = errno;
	int msg = sig;
	send(sig_pipefd[1], (char*) &msg, 1, 0);
	errno = save_errno;
}

// 添加信息函数
static void addsig(int sig, void(handler)(int), bool restart) {
	struct sigaction sa;
	memset(&sa, '\0', sizeof(sa));
	sa.sa_handler = handler;
	if (restart) {
		sa.sa_flags |= SA_RESTART;
	}
	sigfillset(&sa.sa_mask);
	assert(sigaction(sig, &sa, NULL) != -1);
}

// 创建进程自己的epoll并且监听信号管道 以及添加各类信号处理函数
void setup_sig_pipe() {
	m_instance->m_epollfd = epoll_create(5);
	assert(m_instance->m_epollfd != -1);

	int ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sig_pipefd);
	assert(ret != -1);

	setnonblocking(sig_pipefd[1]);
	addfd(m_instance->m_epollfd, sig_pipefd[0]);

	// 注册信号事件函数
	addsig(SIGCHLD, sig_handler, true);
	addsig(SIGTERM, sig_handler, true);
	addsig(SIGINT, sig_handler, true);
	// 忽略SIGPIPE
	addsig(SIGPIPE, SIG_IGN, true);
}

// 进程池的构造函数
void processpoll_constructor(int listenfd, int process_number) {
	m_instance->m_idx = -1;
	m_instance->m_listenfd = listenfd;
	m_instance->m_process_number = process_number;

	assert((process_number > 0) && (process_number <= MAX_PROCESS_NUMBER));

	m_instance->m_sub_process = (struct process*)malloc(sizeof(struct process)*process_number);
	assert(m_instance->m_sub_process);

	for(int i=0;i<process_number;++i) {
		// 创建一对父子进程的双向通信管道
		int ret = socketpair(PF_UNIX, SOCK_STREAM, 0, m_instance->m_sub_process[i].m_pipefd);
		assert(ret == 0);

		// 在这里进行fork()
		m_instance->m_sub_process[i].m_pid = fork();
		assert(m_instance->m_sub_process[i].m_pid >= 0);
		if (m_instance->m_sub_process[i].m_pid > 0) {
			// 父进程
			close(m_instance->m_sub_process[i].m_pipefd[1]);
			continue;
		} else {
			// 子进程
			close(m_instance->m_sub_process[i].m_pipefd[0]);
			// 每个子进程都有一个m_instance 它的m_idx就是此时的i 代表当前子进程在父进程池中的索引
			m_instance->m_idx = i;
			break;
		}
	}
}

// 进程池销毁
void processpoll_free() {
	free(m_instance->m_sub_process);
	free(m_instance);
}

// 创建进程池
struct processpoll* processpool_create(int listenfd, int process_number) {
	if (!m_instance) {
		m_instance = (struct processpoll*)malloc(sizeof(struct processpoll));
		processpoll_constructor( listenfd, process_number);
	}
	return m_instance;
}


// 父进程的主体逻辑
void run_parent() {
	setup_sig_pipe();

	// 父进程负责监听listen socket
	addfd(m_instance->m_epollfd, m_instance->m_listenfd);

	struct epoll_event events[MAX_EVENT_NUMBER];
	int sub_process_counter = 0;
	int new_conn = 1;
	int number = 0;
	int ret = -1;

	while(!m_instance->m_stop) {
		number = epoll_wait(m_instance->m_epollfd, events, MAX_EVENT_NUMBER, -1);
		if (number < 0 && errno != EINTR) {
			printf("epoll failture\n");
			break;
		}

		for(int i = 0;i < number; ++i) {
			int sockfd = events[i].data.fd;

			if (sockfd == m_instance->m_listenfd) {
				// 所有进程中选取一个空闲的进程 进程池初始化的时候每个子进程的m_pid都是0 代表是空闲状态
				// 从上一轮选择的sub_process_counter开始计数 一个一个轮流选取
				int i = sub_process_counter;
				do {
					if (m_instance->m_sub_process[i].m_pid != -1) {
						break;
					}
					i = (i+1) % m_instance->m_process_number;
				} while(i != sub_process_counter);

				// 如果最后所有的子进程都退出了 则父进程也退出
				if (m_instance->m_sub_process[i].m_pid == -1) {
					m_instance->m_stop = true;
					break;
				}
				// 所有已经使用了的子进程的数量更新
				sub_process_counter = (i+1)%m_instance->m_process_number;
				// 给当前选中的进程 i 发送信号 通过通信管道 告知子进程处理这个新到来的连接
				send(m_instance->m_sub_process[i].m_pipefd[0], (char*)&new_conn, sizeof(new_conn), 0);
				printf("send request to child %d\n", i);
			}
			else if (sockfd == sig_pipefd[0] && (events[i].events & EPOLLIN)) {
				// 信号捕获逻辑
				int sig;
				char signals[1024];
				ret = recv(sig_pipefd[0], signals, sizeof(signals), 0);
				if (ret <= 0) {
					continue;
				}
				else {
					for(int i=0;i<ret;++i) {
						switch(signals[i]) {
							case SIGCHLD:
							{
								// 有个子进程退出了
								pid_t pid;
								int stat;
								while((pid = waitpid(-1, &stat, WNOHANG)) > 0) {
									for(int i=0;i<m_instance->m_process_number;++i) {
										if (m_instance->m_sub_process[i].m_pid == pid) {
											// 捕获了这个子进程的退出事件
											printf("child %d join\n", i);
											close(m_instance->m_sub_process[i].m_pipefd[0]);
											// 把它的m_pid标记为-1
											m_instance->m_sub_process[i].m_pid = -1;
										}
									}
								}
								// 如果所有子进程都退出了 那父进程就结束
								m_instance->m_stop = true;
								for(int i=0;i<m_instance->m_process_number;++i) {
									if (m_instance->m_sub_process[i].m_pid != -1) {
										m_instance->m_stop = false;
									}
								}
								break;
							}
							case SIGTERM:
							case SIGINT:
							{
								// 父进程收到了终止信号 杀死所有子进程
								printf("kill all the child now\n");
								for(int i=0;i<m_instance->m_process_number;++i) {
									int pid = m_instance->m_sub_process[i].m_pid;
									if (pid != -1) {
										kill(pid, SIGTERM);
									}
								}
								break;
							}
							default:
							{
								break;
							}
						}
					}
				}
			}
			else {
				continue;
			}
		}
	}

	close(m_instance->m_epollfd);
}

// 处理连接任务的task

void conn_task_init(struct conn_task* task, int epollfd, int connfd, struct sockaddr_in* client_address) {
	task->m_epollfd = epollfd;
	task->m_sockfd = connfd;
	task->m_address = *client_address;
	memset(task->m_buf, '\0', BUFFER_SIZE);
	task->m_read_idx = 0;
}

void conn_task_process(struct conn_task* task) {
	int idx = 0;
	int ret = -1;
	while(1) {
		idx = task->m_read_idx;
		ret = recv(task->m_sockfd, task->m_buf + idx, BUFFER_SIZE - 1 -idx, 0);
		if (ret < 0) {
			if (errno != EAGAIN) {
				removefd(task->m_epollfd, task->m_sockfd);
			}
			break;
		}
		else if (ret == 0) {
			removefd(task->m_epollfd, task->m_sockfd);
			break;
		}
		else {
			// 上一轮已经读取的字符个数
			task->m_read_idx += ret;
			printf("user content is: %s\n", task->m_buf);
			for(;idx < task->m_read_idx;++idx) {
				// if (idx >= 1 && task->m_buf[idx - 1] == '\r' && task->m_buf[idx] == '\n') {
				// 	break;
				// }
				// 这里我用换行符在表示用户输入完毕了
				if (idx >= 1 && task->m_buf[idx] == '\n') {
					break;
				}
			}
			// 这一行代表还没读取到换行符 需要继续读取
			if (idx == task->m_read_idx) {
				continue;
			}
			// task->m_buf[idx - 1] = '\0';
			// 替换换行符\n为字符串结束符\0
			task->m_buf[idx] = '\0';

			// CGI服务器 接受的数据 是一个GCI脚本文件的完整路径
			char* file_name = task->m_buf;
			// 检查文件状态
			if (access(file_name, F_OK) == -1) {
				removefd(task->m_epollfd, task->m_sockfd);
				break;
			}
			// 子进程自己fork出一个新的孙子进程来执行CGI脚本
			ret = fork();
			if (ret == -1) {
				removefd(task->m_epollfd, task->m_sockfd);
				break;
			}
			else if (ret > 0) {
				// 子进程关闭sockfd即可
				removefd(task->m_epollfd, task->m_sockfd);
				break;
			} else {
				// 孙子进程关闭标准输出 也就是1
				close(STDOUT_FILENO);
				// dup函数选取一个最小的没有被使用的文件描述符来指向 参数中的文件描述符所对应的内核中的文件表
				dup(task->m_sockfd);
				// 执行对应的CGI脚本 由于孙子进程继承了子进程的文件描述符 这个脚本的标准输出将会被写入到sockfd中 执行完后 孙子进程自己终止 子进程负责回收它
				execl(task->m_buf, task->m_buf, 0, NULL);
				exit(0);
			}
		}
	}
}

// 子进程的主体逻辑
void run_child() {
	// 每个子进程自己要独立处理信号 因为fork的时候信号集被清空了 需要重新设置
	setup_sig_pipe();

	// 子进程先注册通信管道
	int pipefd = m_instance->m_sub_process[m_instance->m_idx].m_pipefd[1];
	addfd(m_instance->m_epollfd, pipefd);

	struct epoll_event events[MAX_EVENT_NUMBER];
	// 初始化每个子进程的任务处理对象数组
	struct conn_task* users = (struct conn_task*)malloc(sizeof(struct conn_task)*USER_PER_PROCESS);
	assert(users);
	int number = 0;
	int ret = -1;

	while(!m_instance->m_stop) {
		number = epoll_wait(m_instance->m_epollfd, events, MAX_EVENT_NUMBER, -1);
		if (number < 0 && errno != EINTR) {
			printf("epoll failture\n");
			break;
		}

		for(int i = 0;i < number; ++i) {
			int sockfd = events[i].data.fd;
			if (sockfd == pipefd && (events[i].events & EPOLLIN)) {
				// 通信管道有可读数据 意味着有新连接到了 
				int client = 0;
				ret = recv(sockfd, (char*) &client, sizeof(client), 0);
				if ((ret < 0 && errno != EAGAIN) || ret == 0) {
					continue;
				}
				else {
					struct sockaddr_in client_address;
					socklen_t client_addrlength = sizeof(client_address);
					// 子进程负责accept这个连接
					int connfd = accept(m_instance->m_listenfd, (struct sockaddr*) &client_address, &client_addrlength);
					if (connfd < 0) {
						printf("errno is: %d\n", errno);
						continue;
					}
					// 并且子进程自己监听这个connfd的事件
					addfd(m_instance->m_epollfd, connfd);
					// 初始化一个任务处理对象 建立起任务对象和这个连接socket 以及 子进程epollfd 之间的关联
					conn_task_init(&users[connfd], m_instance->m_epollfd, connfd, &client_address);
				}
			}
			else if (sockfd == sig_pipefd[0] && (events[i].events & EPOLLIN)) {
				// 子进程信号捕获逻辑
				int sig;
				char signals[1024];
				ret = recv(sig_pipefd[0], signals, sizeof(signals), 0);
				if (ret <= 0) {
					continue;
				}
				else {
					for(int i=0;i<ret;++i) {
						switch(signals[i]) {
							case SIGCHLD:
							{
								// 由于这是一个CGI服务器 子进程也会自己fork 需要处理回收自己的子进程
								pid_t pid;
								int stat;
								while((pid = waitpid(-1, &stat, WNOHANG)) > 0) {
									continue;
								}
								break;
							}
							case SIGTERM:
							case SIGINT:
							{
								// 终止当前子进程
								m_instance->m_stop = true;
								break;
							}
							default:
							{
								break;
							}
						}
					}
				}
			}
			else if (events[i].events & EPOLLIN) {
				// 连接socket数据可读了 处理一下它 
				conn_task_process(&users[sockfd]);
			}
			else {
				continue;
			}
		}
	}

	// 释放当前子进程申请的资源
	free(users);
	users = NULL;
	close(pipefd);
	close(m_instance->m_epollfd);
}

void processpoll_run() {
	if (m_instance->m_idx != -1) {
		// 运行子进程的逻辑
		run_child();
		return;
	}
	// 运行父进程的逻辑
	run_parent();
}

int main(int argc, char *argv[])
{
	if (argc < 2) {
        printf("usage: %s  ip_address port_number\n", basename(argv[0]));
        return 1;
    }
    const char* ip = argv[1];
    int port = atoi(argv[2]);

	int listenfd = socket(PF_INET, SOCK_STREAM, 0);
	assert(listenfd >= 0);

	int ret = 0;
	struct sockaddr_in address;
	bzero(&address, sizeof(address));
	address.sin_family = AF_INET;
	inet_pton(AF_INET, ip, &address.sin_addr);
	address.sin_port = htons(port);

	int reuse = 1;
    ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,(const void *)&reuse , sizeof(int));
	assert(ret != -1);

	ret = bind(listenfd, (struct sockaddr*) &address, sizeof(address));
	assert(ret != -1);
	
	ret = listen(listenfd, 5);
	assert(ret != -1);

	// 创建进程池
	struct processpoll* pool = processpool_create(listenfd, 8);
	if (pool) {
		// 由于在上一步的processpool_create中存在fork()调用 所有每个进程都会执行下面的逻辑 阻塞在自己的内部循环中 除非它被终止了
		processpoll_run();
		processpoll_free();
	}
	// 哪个函数打开了文件描述符就哪个函数关闭它
	close(listenfd);

	return 0;
}

TCP HTTP服务器 (线程池)

原文是C++写的,我改写成了C版本。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/epoll.h>
#include <stdbool.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <stdarg.h>

// 最大任务处理对象的数量
#define MAX_FD 65536
// epoll可注册最大事件数量
#define MAX_EVENT_NUMBER 10000
// 请求的html文件资源名字的最大长度
#define FILENAME_LEN 200
// socket可读缓冲区最大值
#define READ_BUFFER_SIZE 2048
// socket可写缓冲区最大值
#define WRITE_BUFFER_SIZE 1024

// 全局唯一 epollfd
static int m_epollfd;
// 用户连接数量
static int m_user_count = 0;

// http请求行中的方法
enum METHOD {GET = 0, POST, HEAD, PUT, DELETE, TRACE, OPTIONS, CONNECT, PATCH};
// 状态机解析当前读取的字符所处的状态枚举
enum CHECK_STATE {CHECK_STATE_REQUESTLINE = 0, CHECK_STATE_HEADER, CHECK_STATE_CONTENT };
// 读取完一段字符后得到的状态码
enum HTTP_CODE {NO_REQUEST, GET_REQUEST, BAD_REQUEST, NO_RESOURCE, FORBIDDEN_REQUEST, FILE_REQUEST, INTERNAL_ERROR, CLOSED_CONNECTION};
// 读到当前字符串结尾或者完整的一行时候返回的行状态枚举
enum LINE_STATUS {LINE_OK = 0, LINE_BAD, LINE_OPEN};

// 一些固定的http响应报文字符串 状态 提示等
const char* ok_200_title = "OK";
const char* error_400_title = "Bad Request";
const char* error_400_form = "Your request has bad syntax or is inherently impossible to satisfy.\n";
const char* error_403_title = "Forbidden";
const char* error_403_form = "You do not have permission to get file from this server.\n";
const char* error_404_title = "Not Found";
const char* error_404_form = "The requested file was not found on this server.\n";
const char* error_500_title = "Internal Error";
const char* error_500_form = "There was an unusual problem serving the requested file.\n";
// 系统目录中存放静态资源的前面一截路径 最终解析完请求数据后得到 例如 /var/www/html/请求行中的path部分/xxxx.html 的地址
const char* doc_root = "/var/www/html";

// 前向声明
struct listnode;
struct http_conn;
struct threadpool;

bool sem();
void free_sem();
bool wait();
bool post();
bool locker();
void free_locker();
bool lock();
bool unlock();
bool cond();
bool free_cond();
bool cond_wait();
bool cond_signal();

bool list_empty(struct threadpool*);
void list_append(struct threadpool*, struct http_conn*);
void list_free(struct threadpool*);
struct http_conn* list_front(struct threadpool*);
struct threadpool* threadpool_contstruct( int, int );
void threadpool_free(struct threadpool*);
// bool pool_append_work(struct threadpool*, struct http_coon*);
void* worker(void*);
void pool_run(struct threadpool*);

int setnonblocking( int );
void addfd( int , int , bool );
void modfd( int , int , int  );
void removefd( int , int  );

void http_conn_init(struct http_conn* , int , const struct sockaddr_in* );
void init(struct http_conn*);
void http_conn_close(struct http_conn* , bool );
void http_conn_process(struct http_conn*);
bool http_conn_read(struct http_conn*);
bool http_conn_write(struct http_conn*);
bool process_write(struct http_conn* , enum  HTTP_CODE );
enum HTTP_CODE process_read(struct http_conn*);
enum HTTP_CODE parse_request_line(struct http_conn* , char*  );
enum HTTP_CODE parse_headers(struct http_conn* , char*  );
enum HTTP_CODE parse_content(struct http_conn* , char*  );
enum HTTP_CODE do_request(struct http_conn* );
char* get_line(struct http_conn*);
enum LINE_STATUS parse_line(struct http_conn*);
void unmap(struct http_conn* );
bool add_response(struct http_conn* , const char* , ... );
bool add_content(struct http_conn* , const char* );
bool add_status_line(struct http_conn* , int , const char*  );
bool add_headers(struct http_conn* , int  );
bool add_content_length(struct http_conn* , int  );
bool add_linger(struct http_conn* );
// bool add_blank_line(struct http_conn* ,);
// void m_addsig(int, void()(int), bool);
void show_error(int , const char* );

// 线程之间由于会相互修改共享的变量 导致引入竞争态 需要需要一些方法来同步彼此之间的执行顺序
// locker 锁相关代码
sem_t m_queuestat;

// 初始化信号量
bool sem() {
    return sem_init(&m_queuestat, 0, 0) != 0;
}

// 释放信号量
void free_sem() {
    sem_destroy(&m_queuestat);
}

// 等待信号量 如果之前信号量的值<=0就阻塞调用者
bool wait() {
    return sem_wait(&m_queuestat) == 0;
}

// 信号量+1 如果此时有线程阻塞在wait中 系统负责唤醒其中一个
bool post() {
    return sem_post(&m_queuestat) == 0;
}

pthread_mutex_t m_queuelocker;

// 互斥锁
bool locker() {
    return pthread_mutex_init(&m_queuelocker, NULL) != 0;
}

// 释放互斥锁
void free_locker() {
    pthread_mutex_destroy(&m_queuelocker);
}

// 锁住互斥锁 如果它已经被其他线程锁住 就阻塞调用者
bool lock() {
    return pthread_mutex_lock(&m_queuelocker) == 0;
}

// 释放互斥锁 唤醒一个在等待互斥锁的线程(如果有)
bool unlock() {
    return pthread_mutex_unlock(&m_queuelocker) == 0;
}

pthread_mutex_t m_cond_mutex;
pthread_cond_t m_cond;

// 条件变量和配合它使用的一个互斥锁
bool cond() {
    return pthread_mutex_init( &m_cond_mutex, NULL ) != 0 &&  pthread_cond_init( &m_cond, NULL ) != 0;
}

// 释放条件变量
bool free_cond() {
    pthread_mutex_destroy( &m_cond_mutex );
    pthread_cond_destroy( &m_cond );
}

// 等待
bool cond_wait() {
    int ret = 0;
    pthread_mutex_lock(&m_cond_mutex);
    ret = pthread_cond_wait(&m_cond, &m_cond_mutex);
    pthread_mutex_unlock(&m_cond_mutex);
    return ret == 0;
}

// 通知信号量可用了
bool cond_signal() {
    return pthread_cond_signal(&m_cond) == 0;
}

// end

// threadpool 线程池代码

// 一个简单的双向链表节点
struct listnode {
    struct http_conn* task;
    struct listnode* prev;
    struct listnode* next;
};

// 线程池对象
struct threadpool { 
    int m_thread_number;
    int m_max_requests;

    pthread_t* m_threads;

    struct listnode* head;
    struct listnode* tail;

    int size;

    bool m_stop;
};

// 链表判空
bool list_empty(struct threadpool* pool) {
    return pool->head->next == pool->tail;
}

// 链表尾部插入一个任务
void list_append(struct threadpool* pool, struct http_conn* request) {
    struct listnode* p = pool->tail->prev;
    struct listnode* node = (struct listnode*)malloc(sizeof(struct listnode));
    node->task = request;
    node->next = pool->tail;
    node->prev = p;
    p->next = node;
    pool->tail->prev = node;
    // 线程池size+1
    pool->size += 1;
}

// 释放链表
void list_free(struct threadpool* pool) {
    if (!list_empty(pool)) {
        struct listnode* node = pool->head->next;
        while(node) {
            struct listnode* next = node->next;
            free(node);
            node = next;
        }
        pool->size = 0;
    }
}

// 从链表中取出第一个来交给任务处理对象去处理
struct http_conn* list_front(struct threadpool* pool) {
    struct listnode* node = pool->head->next;
    struct http_conn* task = node->task;
    pool->head->next = node->next;
    node->next->prev = pool->head;
    free(node);
    pool->size -= 1;
    return task;
}

// 线程池构造函数
struct threadpool* threadpool_contstruct( int thread_number, int max_requests ) {
    struct threadpool* pool = (struct threadpool*)malloc(sizeof(struct threadpool));
    if (!pool) {
        return NULL;
    }
    assert(thread_number> 0);
    assert(max_requests > 0);
    pool->m_thread_number = thread_number;
    pool->m_max_requests = max_requests;

    // 初始化任务队列(双向链表实现)
    pool->head = (struct listnode*)malloc(sizeof(struct listnode));
    pool->tail = (struct listnode*)malloc(sizeof(struct listnode));
    pool->head->next = pool->tail;
    pool->tail->prev = pool->head;
    pool->size = 0;

    pool->m_stop = false;
    pool->m_threads = (pthread_t*)malloc(sizeof(pthread_t));
    assert(pool->m_threads);

    // 创建目标数量的线程 以worker函数作为起点 pool作为参数 将子线程设置为脱离属性 让他们自己在终止的时候回收资源
    for(int i=0;i<thread_number;++i) {
        printf("create the %dth thread\n", i);
        if (pthread_create((pool->m_threads)+i, NULL, worker, pool) != 0) {
            free(pool->m_threads);
        }
        if (pthread_detach(pool->m_threads[i])) {
            free(pool->m_threads);
        }
    }

    return pool;
}

// 释放线程池
void threadpool_free(struct threadpool* pool) {
    free(pool->m_threads);
    list_free(pool);
    free(pool->head);
    free(pool->tail);
    pool->m_stop = true;
}

// 工作队列中插入一个任务
bool pool_append_work(struct threadpool* pool, struct http_coon* request) {
    // 先锁住它 
    lock();
    if (pool->size > pool->m_max_requests) {
        unlock();
        return false;
    }
    list_append(pool, request);
    unlock();
    // 通知一个阻塞在wait状态的线程 可以处理task了现在
    post();
    return true;
}

void* worker(void* arg) {
    struct threadpool* pool = (struct threadpool*)arg;
    pool_run(pool);
    return pool;
}

// 子线程就简单的阻塞在wait 等待被唤醒去处理task 处理完后 如果没有被终止 就继续wait等待
void pool_run(struct threadpool* pool) {
    while(!pool->m_stop) {
        wait();
        lock();
        if (list_empty(pool)) {
            unlock();
            continue;
        }
        struct http_conn* request = list_front(pool);
        unlock();
        if (!request) {
            continue;
        }
        http_conn_process(request);
    }
}

// end

// http请求的处理任务

// 非阻塞
int setnonblocking( int fd )
{
    int old_option = fcntl( fd, F_GETFL );
    int new_option = old_option | O_NONBLOCK;
    fcntl( fd, F_SETFL, new_option );
    return old_option;
}

// 注册事件 EPOLLONESHOT 类型保证:如果有一个线程正在处理某个socket上的数据 在这个线程未处理完之前不会触发这个socket的可读事件从而导致其他线程来处理这个socket, 避免出现2个线程操作一个socket的数据的情况
void addfd( int epollfd, int fd, bool one_shot )
{
    struct epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN | EPOLLET | EPOLLRDHUP;
    if( one_shot )
    {
        event.events |= EPOLLONESHOT;
    }
    epoll_ctl( epollfd, EPOLL_CTL_ADD, fd, &event );
    setnonblocking( fd );
}

// 修改事件 因为需要重置ev事件 让socket重新可读或者可写
void modfd( int epollfd, int fd, int ev )
{
    struct epoll_event event;
    event.data.fd = fd;
    event.events = ev | EPOLLET | EPOLLONESHOT | EPOLLRDHUP;
    epoll_ctl( epollfd, EPOLL_CTL_MOD, fd, &event );
}

// 移除fd
void removefd( int epollfd, int fd )
{
    epoll_ctl( epollfd, EPOLL_CTL_DEL, fd, 0 );
    close( fd );
}

// http处理对象
struct http_conn {
    int m_sockfd;
    struct sockaddr_in m_address;

    char m_read_buf[ READ_BUFFER_SIZE ];
    int m_read_idx;
    int m_checked_idx;
    int m_start_line;
    char m_write_buf[ WRITE_BUFFER_SIZE ];
    int m_write_idx;

    enum CHECK_STATE m_check_state;
    enum METHOD m_method;

    char m_real_file[ FILENAME_LEN ];
    char* m_url;
    char* m_version;
    char* m_host;
    int m_content_length;
    bool m_linger;

    char* m_file_address;
    struct stat m_file_stat;
    struct iovec m_iv[2];
    int m_iv_count;
};

// 初始化一个task对象 建立和目标socket的联系
void http_conn_init(struct http_conn* task, int sockfd, const struct sockaddr_in* addr) {
    task->m_sockfd = sockfd;
    task->m_address = *addr;
    int error = 0;
    socklen_t len = sizeof(error);
    getsockopt( task->m_sockfd, SOL_SOCKET, SO_ERROR, &error, &len );
    int reuse = 1;
    setsockopt( task->m_sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof( reuse ) );
    addfd( m_epollfd, sockfd, true );
    m_user_count++;

    init(task);
}

// 重置读写辅助字段的值
void init(struct http_conn* task) {
    task->m_check_state = CHECK_STATE_REQUESTLINE;
    task->m_linger = false;

    task->m_method = GET;
    task->m_url = 0;
    task->m_version = 0;
    task->m_content_length = 0;
    task->m_host = 0;
    task->m_start_line = 0;
    task->m_checked_idx = 0;
    task->m_read_idx = 0;
    task->m_write_idx = 0;
    memset( task->m_read_buf, '\0', READ_BUFFER_SIZE );
    memset( task->m_write_buf, '\0', WRITE_BUFFER_SIZE );
    memset( task->m_real_file, '\0', FILENAME_LEN );
}

// 终止一个sockfd连接
void http_conn_close(struct http_conn* task, bool real_close) {
    if (real_close && (task->m_sockfd != -1)) {
        removefd(m_epollfd, task->m_sockfd);
        task->m_sockfd = -1;
        m_user_count--;
    }
}

// task处理函数 
void http_conn_process(struct http_conn* task) {
    // 读取当前缓冲区的数据后得到一个当前结果
    enum HTTP_CODE read_ret = process_read(task);
    // 如果还没得到一个确定的结果 则继续等待读取
    if ( read_ret == NO_REQUEST )
    {
        modfd( m_epollfd, task->m_sockfd, EPOLLIN );
        return;
    }

    // 根据当前确定的结果 写响应报文
    bool write_ret = process_write(task, read_ret);
    if (!write_ret) {
        http_conn_close(task ,true);
    }

    // 写准备工作完成后 注册可写事件 让另外个线程来执行任务
    modfd( m_epollfd, task->m_sockfd, EPOLLOUT );
}

// 把当前socket缓冲区的数据都读到task的读缓冲区中
bool http_conn_read(struct http_conn* task) {
    if (task->m_read_idx >= READ_BUFFER_SIZE) {
        return false;
    }

    int bytes_read = 0;
    while(true) {
        bytes_read = recv( task->m_sockfd, task->m_read_buf + task->m_read_idx, READ_BUFFER_SIZE - task->m_read_idx, 0 );
        if (bytes_read == -1) {
            if( errno == EAGAIN || errno == EWOULDBLOCK )
            {
                break;
            }
            return false;
        }
        else if (bytes_read == 0) {
            return false;
        }

        task->m_read_idx += bytes_read;
    }

    return true;
}

// 把准备好的响应报文数据写入到socket的内核写缓冲区中
bool http_conn_write(struct http_conn* task) {
    int temp = 0;
    int bytes_have_send = 0;
    int bytes_to_send = task->m_write_idx;
    if (bytes_to_send == 0) {
        // 写完了 重置辅助参数 重新注册可读事件 等待socket的新数据到来
        modfd(m_epollfd, task->m_sockfd, EPOLLIN);
        init(task);
        return true;
    }

    while(1) {
        temp = writev(task->m_sockfd, task->m_iv, task->m_iv_count);
        if (temp <= -1) {
            if( errno == EAGAIN )
            {
                modfd( m_epollfd, task->m_sockfd, EPOLLOUT );
                return true;
            }
            unmap(task);
            return false;
        }

        bytes_to_send -= temp;
        bytes_have_send += temp;

        // task的写缓冲区分2块 第一个块放头部信息 第二个块放文档数据 由于writev返回的是写入成功的字节数 所以需要我们自己维持下一次的写入开始位置和剩余写入数量
        if (bytes_have_send >= task->m_iv[0].iov_len)
        {
            task->m_iv[0].iov_len = 0;
            task->m_iv[1].iov_base = task->m_file_address + (bytes_have_send - task->m_write_idx);
            task->m_iv[1].iov_len = bytes_to_send;
        }
        else
        {
            task->m_iv[0].iov_base = task->m_write_buf + bytes_have_send;
            task->m_iv[0].iov_len = task->m_iv[0].iov_len - bytes_have_send;
        }

        // 写入完毕
        if (bytes_to_send <= 0) {
            unmap(task);
            // 根据字段是否终止当前连接
            if (task->m_linger) {
                init(task);
                modfd( m_epollfd, task->m_sockfd, EPOLLIN );
                return true;
            }
            else {
                modfd( m_epollfd, task->m_sockfd, EPOLLIN );
                return false;
            }
        }
    }
}

// 处理task的读缓冲区的状态机函数
enum HTTP_CODE process_read(struct http_conn* task) {
    enum LINE_STATUS line_status = LINE_OK;
    enum HTTP_CODE ret = NO_REQUEST;
    char* text = 0;

    while ( ( ( task->m_check_state == CHECK_STATE_CONTENT ) && ( line_status == LINE_OK  ) )
                || ( (line_status = parse_line(task)) == LINE_OK ) )
    {
        // 进入到这里意味着已经读取到一个完整的一行字符了 可以分析目前这一行数据的字段含义了
        // text 指向这一行的起始地址
        text = get_line(task);
        // 更新一下当前行的位置 也就是上一次检查到的字符位置
        task->m_start_line = task->m_checked_idx;
        printf( "got 1 http line: %s\n", text );

        // 状态机内部转换 开始时候为 CHECK_STATE_REQUESTLINE
        switch ( task->m_check_state )
        {
            case CHECK_STATE_REQUESTLINE:
            {
                // 处理请求行
                ret = parse_request_line(task, text );
                if ( ret == BAD_REQUEST )
                {
                    return BAD_REQUEST;
                }
                break;
            }
            case CHECK_STATE_HEADER:
            {
                // 处理头部字段行
                ret = parse_headers(task,  text );
                if ( ret == BAD_REQUEST )
                {
                    return BAD_REQUEST;
                }
                else if ( ret == GET_REQUEST )
                {
                    return do_request(task);
                }
                break;
            }
            case CHECK_STATE_CONTENT:
            {
                // 处理内容数据
                ret = parse_content(task,  text );
                if ( ret == GET_REQUEST )
                {
                    return do_request(task);
                }
                line_status = LINE_OPEN;
                break;
            }
            default:
            {
                return INTERNAL_ERROR;
            }
        }
    }

    // 如果在上面的状态转换中没有返回确定的状态 则返回 还没有收到一个完整请求的状态
    return NO_REQUEST;
}

// 把响应报文根据返回状态码写入到task的写缓冲区中 
bool process_write(struct http_conn* task, enum  HTTP_CODE ret ) {
    switch ( ret )
    {
        case INTERNAL_ERROR:
        {
            add_status_line(task, 500, error_500_title );
            add_headers(task, strlen( error_500_form ) );
            if ( ! add_content(task, error_500_form ) )
            {
                return false;
            }
            break;
        }
        case BAD_REQUEST:
        {
            add_status_line(task, 400, error_400_title );
            add_headers(task, strlen( error_400_form ) );
            if ( ! add_content(task, error_400_form ) )
            {
                return false;
            }
            break;
        }
        case NO_RESOURCE:
        {
            add_status_line(task, 404, error_404_title );
            add_headers(task, strlen( error_404_form ) );
            if ( ! add_content(task, error_404_form ) )
            {
                return false;
            }
            break;
        }
        case FORBIDDEN_REQUEST:
        {
            add_status_line(task, 403, error_403_title );
            add_headers(task, strlen( error_403_form ) );
            if ( ! add_content(task, error_403_form ) )
            {
                return false;
            }
            break;
        }
        case FILE_REQUEST:
        {
            // 请求成功
            add_status_line(task, 200, ok_200_title );
            if ( task->m_file_stat.st_size != 0 )
            {
                add_headers(task, task->m_file_stat.st_size );
                // 对应上文的写缓冲区分2块
                task->m_iv[ 0 ].iov_base = task->m_write_buf;
                task->m_iv[ 0 ].iov_len = task->m_write_idx;
                task->m_iv[ 1 ].iov_base = task->m_file_address;
                task->m_iv[ 1 ].iov_len = task->m_file_stat.st_size;
                task->m_iv_count = 2;
                return true;
            }
            else
            {
                const char* ok_string = "<html><body></body></html>";
                add_headers(task, strlen( ok_string ) );
                if ( ! add_content(task, ok_string ) )
                {
                    return false;
                }
            }
            // 这里原文代码中并没有加 导致进入default 然后逻辑进入关闭连接代码了 那为何要加内容呢放里面? 所以我觉得本意是发送空html过去 所以要加break,然后进入538行的代码段,发送这些信息过去
            break;
        }
        default:
        {
            return false;
        }
    }

    // 没有通过正常成功的处理逻辑后返回 就会进入到下面的处理逻辑 只需要写入一个缓冲块就可以了
    task->m_iv[ 0 ].iov_base = task->m_write_buf;
    task->m_iv[ 0 ].iov_len = task->m_write_idx;
    task->m_iv_count = 1;
    return true;
}

// 解析请求行信息
enum HTTP_CODE parse_request_line(struct http_conn* task, char* text ) {
    // 以 GET http://lcoalhost/index.html HTTP/1.1\r\n 为例
    // 把m_url指针移动到 GET后面第一个空格
    task->m_url = strpbrk( text, " \t" );
    if ( !task->m_url )
    {
        return BAD_REQUEST;
    }
    // 空格替换\0
    *task->m_url++ = '\0';

    // METHOD只支持GET
    char* method = text;
    if ( strcasecmp( method, "GET" ) == 0 )
    {
        task->m_method = GET;
    }
    else
    {
        return BAD_REQUEST;
    }

    // 把m_url移动到第一个不是空格 也就是h这里
    task->m_url += strspn( task->m_url, " \t" );
    // 把m_version移动到m_url后面第一个空格 也就是 html后面第一个空格
    task->m_version = strpbrk( task->m_url, " \t" );
    if ( !task->m_version )
    {
        return BAD_REQUEST;
    }
    // 空格替换\0
    *task->m_version++ = '\0';
    // m_version移动到第一个不是空格的地方 也就是 HTTP/1.1的H
    task->m_version += strspn( task->m_version, " \t" );
    if ( strcasecmp( task->m_version, "HTTP/1.1" ) != 0 )
    {
        return BAD_REQUEST;
    }

    // 解析资源url了
    if ( strncasecmp( task->m_url, "http://", 7 ) == 0 )
    {
        task->m_url += 7;
        // 把m_url移动到第一次出现 / 的位置也就是 lcoalhost/index.html 的 /index.html 第一个字符这里 这个操作去掉了host信息 保留了path信息
        task->m_url = strchr( task->m_url, '/' );
    }

    if ( !task->m_url || task->m_url[ 0 ] != '/' )
    {
        return BAD_REQUEST;
    }

    // 解析状态变成解析头部
    task->m_check_state = CHECK_STATE_HEADER;
    return NO_REQUEST;
}

// 解析头部字段
enum HTTP_CODE parse_headers(struct http_conn* task, char* text ) {
    // 由于头部字段可能有多行 所以起始位置可能是个\0
    if( text[ 0 ] == '\0' )
    {
        if ( task->m_method == HEAD )
        {
            return GET_REQUEST;
        }

        if ( task->m_content_length != 0 )
        {
            task->m_check_state = CHECK_STATE_CONTENT;
            return NO_REQUEST;
        }

        return GET_REQUEST;
    }
    // 下面就是常规的头部字段解析了 分析思路跟上面一样 移动指针
    else if ( strncasecmp( text, "Connection:", 11 ) == 0 )
    {
        text += 11;
        text += strspn( text, " \t" );
        if ( strcasecmp( text, "keep-alive" ) == 0 )
        {
            task->m_linger = true;
        }
    }
    else if ( strncasecmp( text, "Content-Length:", 15 ) == 0 )
    {
        text += 15;
        text += strspn( text, " \t" );
        task->m_content_length = atol( text );
    }
    else if ( strncasecmp( text, "Host:", 5 ) == 0 )
    {
        text += 5;
        text += strspn( text, " \t" );
        task->m_host = text;
    }
    else
    {
        printf( "oop! unknow header %s\n", text );
    }

    return NO_REQUEST;
}

// 解析请求报文的内容字段
enum HTTP_CODE parse_content(struct http_conn* task, char* text ) {
    // 不做内容具体分析 只看是否已读取的字节数大于文件长度 代表读取完成了
    if ( task->m_read_idx >= ( task->m_content_length + task->m_checked_idx ) )
    {
        text[ task->m_content_length ] = '\0';
        // 得到了一个完整的http请求报文了
        return GET_REQUEST;
    }

    return NO_REQUEST;
}

// 咋解析请求报文的头部和数据部分时候可能得到一个完整的请求 我们要获取请求中对应的资源
enum HTTP_CODE do_request(struct http_conn* task) {
    strcpy( task->m_real_file, doc_root );
    int len = strlen( doc_root );
    // 得到完整的路径 如 /var/www/html/index.html
    strncpy( task->m_real_file + len, task->m_url, FILENAME_LEN - len - 1 );
    if ( stat( task->m_real_file, &task->m_file_stat ) < 0 )
    {
        return NO_RESOURCE;
    }

    if ( !( task->m_file_stat.st_mode & S_IROTH ) )
    {
        return FORBIDDEN_REQUEST;
    }

    // 不支持目录
    if ( S_ISDIR( task->m_file_stat.st_mode ) )
    {
        return BAD_REQUEST;
    }

    int fd = open( task->m_real_file, O_RDONLY );
    // m_file_address 指向文件存储的内存区间映射 为发送操作建立好数据来源
    task->m_file_address = ( char* )mmap( NULL, task->m_file_stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0 );
    close( fd );
    return FILE_REQUEST;
}

// 下一个可以分析的完整行的起始地址
char* get_line(struct http_conn* task) { return task->m_read_buf + task->m_start_line; }

// 从task读缓冲区中尝试读出一个完整的一行
enum LINE_STATUS parse_line(struct http_conn* task) {
    // 一行以\r\n作为结尾
    char temp;
    for(;task->m_checked_idx < task->m_read_idx;++task->m_checked_idx) {
        temp = task->m_read_buf[task->m_checked_idx];
        if (temp == '\r') {
            if ( ( task->m_checked_idx + 1 ) == task->m_read_idx )
            {
                return LINE_OPEN;
            }
            else if (task->m_read_buf[ task->m_checked_idx + 1 ] == '\n') {
                task->m_read_buf[ task->m_checked_idx++ ] = '\0';
                task->m_read_buf[ task->m_checked_idx++ ] = '\0';
                return LINE_OK;
            }
            return LINE_BAD;
        }
        else if (temp == '\n') {
            if( ( task->m_checked_idx > 1 ) && ( task->m_read_buf[ task->m_checked_idx - 1 ] == '\r' ) )
            {
                task->m_read_buf[ task->m_checked_idx-1 ] = '\0';
                task->m_read_buf[ task->m_checked_idx++ ] = '\0';
                return LINE_OK;
            }
            return LINE_BAD;
        }
    }

    return LINE_OPEN;
}

// 释放共享映射
void unmap(struct http_conn* task) {
    if (task->m_file_address) {
        munmap( task->m_file_address, task->m_file_stat.st_size );
        task->m_file_address = 0;
    }
}

// 把目标字符串按照指定格式写入task的写缓冲区中
bool add_response(struct http_conn* task, const char* format, ... ) {
    if( task->m_write_idx >= WRITE_BUFFER_SIZE )
    {
        return false;
    }
    va_list arg_list;
    va_start( arg_list, format );
    int len = vsnprintf( task->m_write_buf + task->m_write_idx, WRITE_BUFFER_SIZE - 1 - task->m_write_idx, format, arg_list );
    if( len >= ( WRITE_BUFFER_SIZE - 1 - task->m_write_idx ) )
    {
        return false;
    }
    task->m_write_idx += len;
    va_end( arg_list );
    return true;
}

// 以下几个函数分别对应响应报文中的各个字段
bool add_content(struct http_conn* task, const char* content ) {
    return add_response(task,  "%s", content );
}

bool add_status_line(struct http_conn* task, int status, const char* title ) {
    return add_response(task, "%s %d %s\r\n", "HTTP/1.1", status, title );
}

bool add_blank_line(struct http_conn* task) {
    return add_response(task,  "%s", "\r\n" );
}

bool add_headers(struct http_conn* task, int content_length ) {
    add_content_length(task, content_length );
    add_linger(task);
    add_blank_line(task);
}

bool add_content_length(struct http_conn* task, int content_length ) {
    return add_response(task,  "Content-Length: %d\r\n", content_length );
}

bool add_linger(struct http_conn* task) {
    return add_response(task,  "Connection: %s\r\n", ( task->m_linger == true ) ? "keep-alive" : "close" );
}

// end

// 添加信号处理函数
void m_addsig(int sig, void(handler)(int), bool restart) {
    struct sigaction sa;
    memset( &sa, '\0', sizeof( sa ) );
    sa.sa_handler = handler;
    if( restart )
    {
        sa.sa_flags |= SA_RESTART;
    }
    sigfillset( &sa.sa_mask );
    assert( sigaction( sig, &sa, NULL ) != -1 );
}

// 发送错误提示
void show_error(int connfd, const char* info) {
    printf("%s", info);
    send(connfd, info, strlen(info), 0);
    close(connfd);
}

int main(int argc, char* argv[]) {
    if (argc <= 2) {
        printf("usage: %s ip_address port_number\n", basename(argv[0]));
        return 1;
    }
    const char* ip = argv[1];
    int port = atoi(argv[2]);

    m_addsig(SIGPIPE, SIG_IGN, false);

    // 初始化信号量和互斥锁
    sem();
    locker();

    struct threadpool* pool = NULL;
    pool = threadpool_contstruct(8, 10000);
    assert(pool);

    struct http_conn* users = (struct http_conn*)malloc(sizeof(struct http_conn)*MAX_FD);
    assert(users);
    

    int listenfd = socket(PF_INET, SOCK_STREAM, 0);
    assert(listenfd >= 0);

    // 设置 {1, 0} 将导致close系统调用立即返回并且丢弃socket缓冲区的待发送数据 服务器的对连接socket按照这种处理逻辑处理即可 连接socket会从listen socket继承这个属性
    struct linger tmp = {1, 0};
    setsockopt(listenfd, SOL_SOCKET, SO_LINGER, &tmp, sizeof(tmp));

    int ret = 0;
    int reuse = 1;
    ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,(const void *)&reuse , sizeof(int));
	assert(ret != -1);

    struct sockaddr_in address;
    bzero( &address, sizeof( address ) );
    address.sin_family = AF_INET;
    inet_pton( AF_INET, ip, &address.sin_addr );
    address.sin_port = htons( port );

    ret = bind( listenfd, ( struct sockaddr* )&address, sizeof( address ) );
    assert( ret >= 0 );

    ret = listen( listenfd, 5 );
    assert( ret >= 0 );

    struct epoll_event events[ MAX_EVENT_NUMBER ];
    int epollfd = epoll_create( 5 );
    assert( epollfd != -1 );
    addfd( epollfd, listenfd, false );
    // 全局只有epollfd
    m_epollfd = epollfd;

    while(true) {
        // 主线程负责监听listen socket和所有connect socket的事件 然后让子线程根据事件类型来竞争处理任务 这种进程池不太一样
        int number = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);
        if ((number < 0) && (errno != EINTR)) {
            printf("epoll failtrue\n");
            break;
        }

        for(int i = 0;i < number; ++i) {
            int sockfd = events[i].data.fd;
            if (sockfd == listenfd) {
                struct sockaddr_in client_address;
                socklen_t client_addrlength = sizeof( client_address );
                int connfd = accept( listenfd, ( struct sockaddr* ) &client_address, &client_addrlength );
                if ( connfd < 0 )
                {
                    printf( "errno is: %d\n", errno );
                    continue;
                }
                if (m_user_count >= MAX_FD) {
                    show_error(connfd, "Internal server busy");
                    continue;
                }

                // 建立任务对象和这个connct socket的联系 各个子线程竞争这个task对象
                http_conn_init(&users[connfd], connfd, &client_address);
            }
            else if (events[i].events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) {
                // 连接socket终止了
                http_conn_close(&users[sockfd], true);
            }
            else if (events[i].events & EPOLLIN) {
                // 连接socket可读了 分析读取的数据 插入任务队列中
                if (http_conn_read(&users[sockfd])) {
                    pool_append_work(pool, users + sockfd);
                }
                else {
                    http_conn_close(&users[sockfd], true);
                }
            }
            else if (events[i].events & EPOLLOUT) {
                // 连接socket可写了 需要发送数据了 根据写的返回值来看是否关闭连接
                if (!http_conn_write(&users[sockfd])) {
                    http_conn_close(&users[sockfd], true);
                }
            }
            else {

            }
        }
    }

    // 释放资源
    free_sem();
    free_locker();
    // free_cond();

    close(epollfd);
    close(listenfd);

    free(users);
    threadpool_free(pool);
    free(pool);

    return 0;
}

编译的时候记得带上2个共享库

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
sudo gcc -g threadpool_server.c -lrt  -lpthread

最后,再次感谢 游双 大佬的《Linux高性能服务器编程》。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
犀牛鸟硬核 | 腾讯云数据库TDSQL两篇论文入选顶会SIGMOD,产学研结合助力国产数据库生态建设
6月20日-25日,2021 ACM SIGMOD 在西安举行,腾讯云数据库TDSQL两篇论文入选,领域涉及分布式数据库事务处理、存储等关键技术领域,致力于从基础理论层面推动数据库技术创新突破。 数据库作为三大基础软件技术之一,安全可控的发展不仅要依靠前沿基础技术创新,同时包括在人才、应用等生态领域的融合发展。腾讯云数据库TDSQL一直致力于进一步推动AI、新硬件、云计算等技术和数据库技术的深度融合创新,并加大对数据库基础研究创新、数据库产学研合作生态的建设投入,助力国产数据库学术人才培养和技术创新
腾讯高校合作
2021/06/25
7160
即将亮相 SIGMOD ,腾讯云TDSQL凭什么这么秀?
国产数据库的火热从来不是一夜时间的事情,虽然看起来像是一夜梨花开,但更重要是一直以来源头创新工作的厚积薄发。 6月20日-25日,2021 ACM SIGMOD 即将在西安举行。在今年的SIGMOD会议现场,来自腾讯云数据库TDSQL的大咖们将受邀分享自己的洞见和理论创新成果,展示腾讯云数据库TDSQL在数据库基础创新领域的最新探索和研究成果。 今年,TDSQL相关论文被入选SIGMOD,领域涉及分布式数据库事务处理、存储等关键技术领域,致力于从基础理论层面推动数据库技术创新突破。其中,腾讯云原生数据
腾讯云数据库 TencentDB
2021/06/18
9360
犀牛鸟硬核 | 腾讯云数据库三篇论文入选顶会SIGMOD,产学研协同创新助力技术突破
6月12日-17日,2022 ACM SIGMOD在美国费城如期举行。腾讯云数据库三篇论文入选,本次被收录的研究成果中,新型数据结构设计、AI智能调参优化等均属于业界首次提出。本次研究成果也是腾讯云数据库研发团队分别与北京大学、中国人民大学、华中科技大学基于联合实验室创新平台开展的创新合作成果,通过“产学研“一体的合作方式,将技术研究成果转换为应用落地,持续输出前沿技术创新与示范应用,推动我国数据库生态的发展与建设。 6月21日,我们邀请到了中国人民大学、华中科技大学的合作老师与腾讯云数据库研究人员进行本
腾讯高校合作
2022/06/16
5260
犀牛鸟硬核 | 腾讯云数据库三篇论文入选顶会SIGMOD,产学研协同创新助力技术突破
初心不改,推动国产数据库发展,我们永远在路上!
1917年4月,在《新青年》发表的《体育之研究》一文,其中论及“体育之效”时指出:人的身体会天天变化。目不明可以明,耳不聪可以聪。生而强者如果滥用其强,即使是至强者,最终也许会转为至弱;而弱者如果勤自锻炼,增益其所不能,久之也会变而为强。 因此,“生而强者不必自喜也,生而弱者不必自悲也。吾生而弱乎,或者天之诱我以至于强,未可知也”。 回顾TDSQL发展的十七年,从一开始的简单到如今的全面,从内部支撑到推广商用,TDSQL经历了一场由弱到强、从依赖到自主的自我历练。 1 冰山之下:中国商用数据库之痛 如果
腾讯云数据库 TencentDB
2021/06/10
4480
腾讯云数据库最新研究成果亮相SIGMOD大会,前沿技术在云原生数据库中落地应用
6月20-25日,2021届 SIGMOD大会在在西安举办,这也是07年后此大会再度回归中国。ACM SIGMOD大会自1974年美国首届起,被公认是数据库领域具有最高学术地位的国际性会议,收录的论文代表了数据库相关技术的最高水平,也是未来技术发展的重要风向标。此次腾讯云数据库技术最新研究成果入选SIGMOD论文,其技术创新和行业引领进一步受到认可。 (腾讯云数据库技术总监-邱敏) 6月23日,腾讯云数据库技术总监邱敏老师受邀为大会带来主题演讲《The Evolution of Tence
腾讯大讲堂
2021/06/30
1.2K0
MySQL之父,MySQL官方,三大顶会齐赞,凭什么?
工作久了,人总会有种忘记初心的感觉。 相信每个人小时候都想过成为一名科学家,只不过到后来,连自己都忘了。 当一名科学家确实不容易,学术研究难,薪酬待遇还不高,如果不是真的热爱,很难走下去。 但在腾讯,你可以把学术当成工作,工作就是做学术,而且两者都可以做得很好。是的,你没有听错。 今天,腾讯云两项数据库内核技术的研究成果再次入选SIGMOD和ICDE的收录论文。其中,SIGMOD、ICDE、VLDB并称为国际数据库三大顶级会议。 另外,据说在学术圈,一篇SIGMOD或者ICDE就能当副教授了?这么看的话,
腾讯云数据库 TencentDB
2021/01/28
6350
三篇论文入选国际顶会SIGMOD,厉害了腾讯云数据库
好消息!6月13日,腾讯云数据库三篇论文再次入选数据库行业顶会SIGMOD,被SIGMOD 2022 Research Full Paper(研究类长文)收录。 本次被收录的研究成果中,新型数据结构设计、AI智能调参优化等均属于业界首次提出。腾讯云数据库多次入选SIGMOD,表明腾讯云数据库在存储、智能管控等方面的积累与前沿创新获得了国际权威认可。 SIGMOD,全称数据管理国际会议(Special Interest Group on Management Of Data),是由美国计算机协会(ACM)
腾讯云数据库 TencentDB
2022/06/14
1.3K0
三篇论文入选国际顶会SIGMOD,厉害了腾讯云数据库
给所有数据库人的一封邀请函:DB TALK 为你深度解读 SIGMOD 论文
电影届有个奥斯卡奖,是国际上最具权威和专业性的电影类奖项。如果能拿到这个奖项,就代表你的演技已经走出国门,受到全世界的认可甚至喜爱。 而数据库领域,也有一个类似的“奖项”,那就是 SIGMOD 会议。 SIGMOD 会议全称 ACM SIGMOD 数据管理国际会议,是由美国计算机协会 (ACM) 数据管理专业委员会发起、在数据库领域具有最⾼学术地位的国际性学术会议。一直以来,中国学者的论文被 SIGMOD 接收的比例都很小,中国万维⽹联盟上的⼀个分析报告指出:截止到 2006 年,SIGMOD 上共接收了
腾讯云数据库 TencentDB
2022/06/14
4650
给所有数据库人的一封邀请函:DB TALK 为你深度解读 SIGMOD 论文
腾讯TDSQL全时态数据库系统论文入选VLDB
当地时间2019年8月26至30日,VLDB 2019会议在美国加利福尼亚召开,腾讯分布式数据库TDSQL与中国人民大学最新联合研究成果被VLDB 2019接收并将通过长文形式发表。VLDB是国际数据管理与数据库领域顶尖的学术会议之一,这是继去年腾讯TDSQL相似度计算的论文被VLDB录用后,腾讯TDSQL再一次迈进VLDB殿堂。
用户6127029
2019/08/28
6610
腾讯TDSQL全时态数据库系统论文入选VLDB
【犀牛鸟·视野】这些论文为何脱颖而出?腾讯TDSQL带你走进VLDB 2019
VLDB 2019(Very Large Data Bases)于2019.8.26-2019.8.30在洛杉矶召开,腾讯TDSQL分布式数据库团队带来现场报道。 VLDB是三大国际顶尖数据库会议之一(其余二者为SIGMOD和ICDE),根据大会官方公布,今年VLDB共接收了128篇Research Paper、22篇IndustryPaper和48个Demo。 (敬请期待TDSQL后续精彩论文解读) Keynote 本届VLDB带来三个Keynote,角度分别为:尝试从不同的技术角度更优雅地解决应用
腾讯高校合作
2019/09/04
9780
【犀牛鸟·视野】这些论文为何脱颖而出?腾讯TDSQL带你走进VLDB 2019
犀牛鸟硬核 | 产学协同共赢——腾讯云原生数据库内核技术再获创新突破
导语: 近日,腾讯云数据库又有两项数据库内核技术的最新研究成果分别入选了国际数据库顶级会议ICDE和SIGMOD的收录论文。其中,中国科技大学金培权老师团队和腾讯云原生数据库TDSQL-C(原CynosDB)内核团队联合发表的最新研究成果入选ICDE 2021。 该研究成果是基于双方与“2019年腾讯犀牛鸟基础平台技术专项计划”联合产生的产学合作成果。腾讯犀牛鸟基础平台技术专项计划旨在开放腾讯在基础平台层面的技术挑战,搭建产学研合作平台,共同推进技术应用落地与创新,并培育优秀人才。 1 近年来,随
腾讯高校合作
2021/01/27
5160
腾讯云数据库2篇论文入选行业顶会SIGMOD,技术创新获权威认可
SIGMOD(Special Interest Group on Management Of Data)作为数据库领域的“风向标”,拥有40余年发展历史,每年为数据库领域提供大量高质量的研究论文和研究成果,具有重要的学术价值和行业引导意义,与VLDB、ICDE并称数据库三大优质会议。
腾讯云数据库 TencentDB
2024/06/16
3040
腾讯云数据库2篇论文入选行业顶会SIGMOD,技术创新获权威认可
一文读懂数据库最新技术趋势:TDSQL带你深度纵览VLDB 2019
一年一度的数据库领域顶级会议 VLDB 2019于当地时间8月26日-8月30日在美国加利福尼亚州洛杉矶召开。来自学术界和工业界的参会者们汇聚一堂,共襄盛会,探讨交流数据库领域最前沿的技术和发展方向。
分布式数据库TDSQL
2019/08/30
1.9K0
一文读懂数据库最新技术趋势:TDSQL带你深度纵览VLDB 2019
产学研合作生态硬核来袭,共探数据库技术发展与应用
腾讯云数据库一直致力于推动数据库基础研究创新、数据库产学研合作生态建设,助力国产数据库学术人才培养和技术创新生态建设发展。 为让更多数据库从业者了解数据库领域的最新研究成果,熟悉更多行业前沿发展趋势,更好地探索前沿技术创新,8月16日下午,腾讯云数据库邀请到华南师范大学二级教授 汤庸、长江学者 毛睿、中国人民大学教授 卢卫和腾讯云数据库专家工程师 智雅楠带来主题为“数据库技术的发展与应用”的前沿学术分享直播。本期为大家带来各位专家的分享精华,都是硬核干货! 数据管理与数据应用 汤庸,学者网创始人、华南
腾讯云数据库 TencentDB
2022/08/26
3250
产学研合作生态硬核来袭,共探数据库技术发展与应用
腾讯云数据库两项内核技术入选国际顶会SIGMOD和ICDE
今天,腾讯云两项数据库内核技术最新研究成果同时入选SIGMOD和ICDE的收录论文。 其中,SIGMOD、ICED、VLDB并称为国际数据库三大顶级会议。 先上干货: 成功入选2021年SIGMOD 研究类长文的论文主题为“Spitfire:A Three-Tier Buffer Manager for Volatile and Non-Volatile Memory”。 在这篇论文中,腾讯云原生数据库内核团队首次提出了多级缓冲管理器Spitfire的概念,通过一种新颖、低代价且有效的热数据识别方法
腾讯数据库技术
2021/01/29
1.1K0
TDSQL:以2019的高光时刻,开启21世纪数据库新十年
从内部自研到向行业开放,TDSQL走过来十数年的发展,向上支撑着超过500+金融政企机构的信任和托付。保持持续的求实创新,满足客户需求,是我们永恒的坚持。
分布式数据库TDSQL
2020/01/06
6240
TDSQL:以2019的高光时刻,开启21世纪数据库新十年
【犀牛鸟·硬核】 VLDB 2019 | 揭秘腾讯与中国人民大学最新联合研究成果TDSQL全时态数据库系统
编者按:全时态数据为数据安全、数据重演、数据挖掘和AI技术的施展提供了物理基础。这篇入选VLDB 2019的论文介绍,基于腾讯云TDSQL扩展而来的全时态数据库系统(T-TDSQL),在保证OLTP性能的前提下提供了轻量级的全时态数据管理功能和全时态数据的事务处理能力、以及集当前态数据于生产系统集历史态数据于分析型系统的集群架构,构成了全时态数据的完备解决方案。 前言 01 腾讯与中国人民大学于2017年起,依托于腾讯TEG计费平台部丰富的实战经验和中国人民大学数据工程与知识工程教育部重点实验室的多年学术
腾讯高校合作
2019/08/29
7890
【犀牛鸟·硬核】 VLDB 2019 | 揭秘腾讯与中国人民大学最新联合研究成果TDSQL全时态数据库系统
TDSQL带你走进数据库顶会VLDB 2018 | 论文纵览
一年一届的数据库领域顶级会议VLDB 2018即将于当地时间8月27日-8月31日在巴西里约热内卢召开。 在本届大会上,腾讯公司TDSQL团队携手中国人民大学、武汉大学共同投中一篇Demo Paper,该工作旨在分布式数据库TDSQL上高效地实现相似查询。 TDSQL团队一直在努力为用户提供丰富的功能、高效的性能和便捷的服务。 在大会召开之际,腾讯TDSQL团队对本届大会的论文佳作进行了分析,萃取了精华之精华以飨读者,分享技术,让我们一起共同成长。 VLDB VLDB 会议全称Very Larg
腾讯技术工程官方号
2018/08/28
11.8K4
TDSQL带你走进数据库顶会VLDB 2018 | 论文纵览
前沿技术趋势大盘点,数据库专家邀你畅谈SIGMOD!
回顾数据库领域数十年的发展历程,每一次技术革新浪潮的开端,都源自于从业人员在行业技术前沿一次又一次的探索。 在当前,随着新一轮技术浪潮的兴起,以计算机、通信、互联网等为技术支撑的信息时代,正逐步过渡到以人工智能、云计算、大数据、物联网为关键技术引领的智能新时代。智能化、湖仓一体化、超融合化等发展趋势,被越来越多地落地实践。 比如,为应对海量复杂场景数据服务在性能、成本、服务等方面的需求,数据库正呈现出底层多模多引擎的融合、开发者接口的融合、软硬一体交付以及全链路生态的融合的趋势。腾讯云将原有的TDSQL
腾讯云数据库 TencentDB
2021/06/23
9150
TDSQL参加全球数据库顶级盛会 VLDB 2018回顾
作者介绍:卞昊穹(hankbian):腾讯数据库TDSQL团队成员,高级工程师。中国人民大学博士,曾在SIGMOD、ICDE、CIKM等数据库相关领域顶级会议发表论文多篇。目前参与和主导多项TDSQL查询和存储优化的研发工作。
腾讯云数据库 TencentDB
2018/10/15
2.1K0
推荐阅读
犀牛鸟硬核 | 腾讯云数据库TDSQL两篇论文入选顶会SIGMOD,产学研结合助力国产数据库生态建设
7160
即将亮相 SIGMOD ,腾讯云TDSQL凭什么这么秀?
9360
犀牛鸟硬核 | 腾讯云数据库三篇论文入选顶会SIGMOD,产学研协同创新助力技术突破
5260
初心不改,推动国产数据库发展,我们永远在路上!
4480
腾讯云数据库最新研究成果亮相SIGMOD大会,前沿技术在云原生数据库中落地应用
1.2K0
MySQL之父,MySQL官方,三大顶会齐赞,凭什么?
6350
三篇论文入选国际顶会SIGMOD,厉害了腾讯云数据库
1.3K0
给所有数据库人的一封邀请函:DB TALK 为你深度解读 SIGMOD 论文
4650
腾讯TDSQL全时态数据库系统论文入选VLDB
6610
【犀牛鸟·视野】这些论文为何脱颖而出?腾讯TDSQL带你走进VLDB 2019
9780
犀牛鸟硬核 | 产学协同共赢——腾讯云原生数据库内核技术再获创新突破
5160
腾讯云数据库2篇论文入选行业顶会SIGMOD,技术创新获权威认可
3040
一文读懂数据库最新技术趋势:TDSQL带你深度纵览VLDB 2019
1.9K0
产学研合作生态硬核来袭,共探数据库技术发展与应用
3250
腾讯云数据库两项内核技术入选国际顶会SIGMOD和ICDE
1.1K0
TDSQL:以2019的高光时刻,开启21世纪数据库新十年
6240
【犀牛鸟·硬核】 VLDB 2019 | 揭秘腾讯与中国人民大学最新联合研究成果TDSQL全时态数据库系统
7890
TDSQL带你走进数据库顶会VLDB 2018 | 论文纵览
11.8K4
前沿技术趋势大盘点,数据库专家邀你畅谈SIGMOD!
9150
TDSQL参加全球数据库顶级盛会 VLDB 2018回顾
2.1K0
相关推荐
犀牛鸟硬核 | 腾讯云数据库TDSQL两篇论文入选顶会SIGMOD,产学研结合助力国产数据库生态建设
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验