processpoll.h
#ifndef PROCESSPOOL_H
#define PROCESSPOOL_H
#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>
//全局变量
static int sig_pipefd[2];//用于信号处理的管道
class process//工作进程
{
public:
process():m_pid(-1){};
public:
pid_t m_pid;
int m_piped[2];//主进程与工作进程之间通信的管道
};
template<typename T>//进程池类
class processpoll
{
private:
processpoll(int listenfd, int process_number = 8);//单例模式
public:
static processpoll<T>* creat(int listenfd, int process_number = 8)
{
if(!m_instance)
{
m_instance = new processpoll<T>(listenfd, process_number);
}
return m_instance;
};
void run();//启动进程池
~processpoll()
{
delete[] m_sub_process;
};
private:
void setup_sig_pipe();
void run_parent();
void run_child();
private:
static const int MAX_PROCESS_NUMBER = 16;//进程池中允许的最多工作进程数量
static const int USER_PER_PROCESS = 65536;//每个工作进程最多能处理的客户数量
static const int MAX_EVENT_NUMBER = 10000;//epoll最多能处理的事件数量
int m_process_number;//当前进程池中的工作进程数量
int m_idx;//工作进程在进程池中的编号
int m_epollfd;//每个进程的epoll内核监听事件描述符
int m_listenfd;//每个进程的监听描述符
int m_stop;//控制工作进程何时终止
process* m_sub_process;//描述所有工作进程的
static processpoll<T>* m_instance;
};
template<typename T>
processpoll<T>* processpoll<T>::m_instance = NULL;
//一些常用的函数
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的监听队列中
void addfd(int epollfd, int fd)
{
epoll_event event;
event.data.fd = fd;
event.events = EPOLLIN | EPOLLET;//监听输入和登出事件
epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
setnonblocking(fd);
};
//从epoll事件表中删除某个事件
void removefd(int epollfd, int fd)
{
epoll_ctl(epollfd, EPOLL_CTL_DEL,fd,0);
close(fd);
};
//信号处理函数
void sig_handler(int sig)
{
printf("收到信号%d\n", sig);
int save_errno = errno;
int msg = sig;
send(sig_pipefd[1], (char*)&msg, 1,0 );//主进程在收到信号后写入管道储存
errno = save_errno;
};
//信号处理函数登记
void addsig(int sig, void(*handler)(int), bool restarat = true)
{
struct sigaction sa;
memset(&sa, '\0', sizeof(sa));
sa.sa_handler = handler;
if(restarat)
{
sa.sa_flags |= SA_RESTART;
}
sigfillset(&sa.sa_mask);
assert( sigaction(sig, &sa, NULL) != -1);
};
//进程池的初始化
template<typename T>
processpoll<T>::processpoll(int listenfd, int process_number)
:m_listenfd(listenfd), m_process_number(process_number), m_idx(-1), m_stop(false)
{
assert(process_number > 0 && process_number <= MAX_PROCESS_NUMBER);
m_sub_process = new process[process_number];
//创建process_number个工作进程
for(int i = 0;i<process_number;i++)
{
//建立每个工作进程与主进程的管道
int ret = socketpair(PF_UNIX, SOCK_STREAM, 0, m_sub_process[i].m_piped);
m_sub_process[i].m_pid = fork();
assert(m_sub_process[i].m_pid >= 0);
if(m_sub_process[i].m_pid > 0)//主进程
{
close(m_sub_process[i].m_piped[1]);
continue;
}
else//工作进程
{
close(m_sub_process[i].m_piped[0]);
m_idx = i;//工作进程需要标识自己是第几个工作进程
break;//工作进程退出循环
}
}
};
//统一事件源,添加各种监听事件
template<typename T>
void processpoll<T>::setup_sig_pipe()
{
m_epollfd = epoll_create(5);
assert(m_epollfd != -1);
int ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sig_pipefd);
assert(ret != -1);
setnonblocking(sig_pipefd[1]);
addfd(m_epollfd, sig_pipefd[0]);//监听信号管道
addsig(SIGCHLD, sig_handler);
addsig(SIGTERM, sig_handler);
addsig(SIGINT, sig_handler);
addsig(SIGPIPE, SIG_IGN);
};
template<typename T>
void processpoll<T>::run()
{
if(m_idx == -1)
{
run_parent();
return;
}
run_child();
};
template<typename T>
void processpoll<T>::run_child()//工作进程运行函数
{
setup_sig_pipe();//设置工作进程的统一事件源,每个工作进程有自己的信号管道
//找到与主进程的通信管道
int pipefd = m_sub_process[m_idx].m_piped[1];
addfd(m_epollfd, pipefd);
epoll_event events[MAX_EVENT_NUMBER];
T* user = new T[USER_PER_PROCESS];
assert(user);
int number = 0;//该工作进程已经接入了几个客户端
int ret = -1;
while(!m_stop)
{
int number = epoll_wait(m_epollfd, events, MAX_EVENT_NUMBER, -1);
if(number < 0 && errno != EINTR)
{
printf("child epoll failured\n");
break;
}
for(int i = 0;i<number;i++)
{
int sockfd = events[i].data.fd;
if(sockfd == pipefd && (events[i].events & EPOLLIN))//主进程有消息传入
{
printf("收到主进程传递的信号\n");
int client = 0;
ret = recv(sockfd, (char*)&client, sizeof(client),0);
if((ret < 0 && (errno != EAGAIN )) || ret == 0)
{
continue;
}
else{
//有新的客户端到来
sockaddr_in client_address;
socklen_t client_address_len = sizeof(client_address);
int connfd = accept(m_listenfd, (sockaddr*)&client_address, &client_address_len);
if(connfd < 0)
{
printf("errno is: %d\n", errno);
continue;
}
printf("工作进程%d 加入新的连接%d \n", m_idx, connfd);
addfd(m_epollfd, connfd);
//模板类T必须实现init初始化方法,以初始化一个客户链接
user[connfd].init(m_epollfd, connfd, client_address);
}
}
else if(sockfd == sig_pipefd[0] && (events[i].events & EPOLLIN))//工作进程接收到信号
{
printf("工作进程收到信号\n");
int sig;
char signals[1024];
ret = recv(sig_pipefd[0], signals, sizeof(signals), 0);
if(ret <= 0)
{
continue;
}
else{
for(int j = 0;j<ret;j++)
{
switch(signals[i])
{
case SIGCHLD:
{
printf("工作进程收到子进程结束信号\n");
pid_t pid;
int stat;
while((pid = waitpid(-1, &stat, WNOHANG)) > 0)
{
continue;
}
break;
}
case SIGTERM://主进程发出信号,让工作进程结束
{
printf("工作进程收到终止工作信号\n");
}
case SIGINT://结束进程
{
m_stop = true;
break;
}
default:
{
break;
}
}
}
}
}
else if(events[i].events & EPOLLIN)//客户端传进消息
{
//类型T 需要实现客户端有消息传入的处理方法process
printf("工作进程收客户端发来的消息\n");
user[sockfd].process();
}
else{
printf("工作线程接收到未知信号\n");
continue;
}
}
}
delete [] user;
user = NULL;
close(pipefd);
close(m_epollfd);
};
template<typename T>
void processpoll<T>::run_parent()//主线程的工作函数
{
setup_sig_pipe();
addfd(m_epollfd, m_listenfd);
epoll_event events[MAX_EVENT_NUMBER];
int sub_process_counter = 0;
int new_conn = 1;
int number = 0;
int ret = -1;
while(!m_stop)
{
int number = epoll_wait(m_epollfd, events, MAX_EVENT_NUMBER, -1);
if(number < 0 && errno != EINTR)
{
printf("parent epoll failured\n");
break;
}
for(int i = 0;i<number;i++)
{
int sockfd = events[i].data.fd;
if(sockfd == m_listenfd)//新的客户端连接
{
printf("主进程检查到新的客户端连接\n");
int j = sub_process_counter;
do
{
if(m_sub_process[j].m_pid != -1)
{
break;
}
j = (j=1)%m_process_number;
}
while(j != sub_process_counter);
if(m_sub_process[j].m_pid == -1)
{
m_stop = true;
break;
}
sub_process_counter = (j+1)%m_process_number;
send(m_sub_process[j].m_piped[0], (char*)&new_conn, sizeof(new_conn),0);
printf("send request to child %d\n", j);
}
else if(sockfd == sig_pipefd[0] && events[i].events & EPOLLIN)//主进程接收到信号
{
printf("主进程接收到信号\n");
int sig;
char signals[1024];
ret = recv(sig_pipefd[0], signals, sizeof(signals), 0);
if(ret <= 0)
{
continue;
}
else{
for(int j = 0;j<ret;j++)
{
switch(signals[i])
{
case SIGCHLD://工作进程终止
{
printf("主进程接收到子进程结束信号\n");
pid_t pid;
int stat;
while((pid = waitpid(-1, &stat, WNOHANG)) > 0)
{
for(int k = 0;k<m_process_number;k++)
{
//处理该工作进程的资源
if(m_sub_process[k].m_pid == pid)
{
printf("child %d join\n", k);
close(m_sub_process[k].m_piped[0]);
m_sub_process[k].m_pid = -1;
}
}
}
m_stop= true;
for(int k = 0;k<m_process_number;k++)
{
if(m_sub_process[k].m_pid != -1)
{
m_stop = false;
}
}
break;
}
case SIGTERM://主进程发出信号,让工作进程结束
{
}
case SIGINT://结束进程
{
printf("kill all child\n");
for(int k = 0;k<m_process_number;k++)
{
int pid = m_sub_process[k].m_pid;
if(pid != -1)
{
kill(pid, SIGTERM);
}
}
break;
}
default:
{
break;
}
}
}
}
}
else{
printf("未知信号\n");
continue;
}
}
}
//释放主进程的资源
//close(m_listenfd);//由这个描述符的创建者关闭该描述符
close(m_epollfd);
};
#endif
main.cpp
#include "processpoll.h"
//用于处理客户CGI请求的类
class cgi_conn
{
public:
cgi_conn(){};
~cgi_conn(){};
void init(int epollfd, int socketfd, const sockaddr_in& client_addr);
void process();
private:
static const int BUFFER_SIZE = 1024;
static int m_epollfd;
int m_sockfd;
sockaddr_in m_address;
char m_buf[BUFFER_SIZE];
int m_read_idx;
};
int cgi_conn::m_epollfd = -1;
void cgi_conn::init(int epollfd, int socketfd, const sockaddr_in& client_addr)
{
m_epollfd = epollfd;
m_sockfd = socketfd;
m_address = client_addr;
memset(m_buf, '\0', BUFFER_SIZE);
m_read_idx = 0;
}
void cgi_conn::process()
{
int idx = 0;
int ret = -1;
//循环读取和分析客户数据
while(true)
{
idx= m_read_idx;
ret = recv(m_sockfd, m_buf + idx, BUFFER_SIZE - 1 - idx, 0);
//如果读操作发生错误,则关闭客户端连接,如果只是暂时无数据可读,则退出循环
if(ret < 0)
{
if(errno != EAGAIN)
{
removefd(m_epollfd, m_sockfd);//从epoll书简队列上移除该客户端
}
break;
}
else if(ret == 0)//客户端断开
{
removefd(m_epollfd, m_sockfd);
break;
}
else
{
m_read_idx += ret;
printf("user content is: %s \n", m_buf);
const char re[]= "serve have get you message\n\0";
send(m_sockfd, re, sizeof(re), 0);
}
}
}
int main(int argc, char* argv[])
{
if(argc <= 2)
{
printf("usage: %s ip_address port_number\n", argv[0]);
return 1;
}
const char* ip = argv[1];
int port = atoi(argv[2]);
//服务器的地址信息处理
int ret = 0;
sockaddr_in serve_address;
bzero(&serve_address, sizeof(serve_address));
serve_address.sin_family = AF_INET;
inet_pton(AF_INET, ip, &serve_address.sin_addr);
serve_address.sin_port = htons(port);
int listenfd = socket(PF_INET, SOCK_STREAM, 0);
assert(listenfd > 0);
ret = bind(listenfd, (sockaddr*)&serve_address, sizeof(serve_address));
assert(ret != -1);
ret = listen(listenfd,5);
assert(ret != -1);
processpoll<cgi_conn>* pool = processpoll<cgi_conn>::creat(listenfd, 3);
if(pool)
{
pool->run();
delete pool;
}
close(listenfd);
return 0;
}