信号是一种异步事件,将其和IO事件统一起来交给主程序处理叫做统一事件源,一般方法是:信号发送函数将信号值写到管道一端,然后主程序采用IO复用技术监听管道读端,一旦可读事件发生那么主程序可以在事件处理逻辑中定义信号处理方法,这样就和IO事件处理一只。
服务端统一事件源程序:
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<assert.h>
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#include<fcntl.h>
#include<stdlib.h>
#include<sys/epoll.h>
#include<pthread.h>
#include<iostream>
#define MAX_EVENT_NUMBER 1024//最大事件数目
using namespace std;
static int pipefd[2];//管道描述符
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;
}
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);
}
void sig_handler(int sig){//信号处理函数
int save_errno=errno;
int msg=sig;
send(pipefd[1],(char*)&msg,1,0);//向管道发送信号值
errno=save_errno;
}
void addsig(int sig){//安装信号
struct sigaction sa;
memset(&sa,'\0',sizeof(sa));
sa.sa_handler=sig_handler;//信号处理句柄
sa.sa_flags|=SA_RESTART;//被信号中断的系统调用将被重启
sigfillset(&sa.sa_mask);//信号掩码为全部信号
assert(sigaction(sig,&sa,NULL)!=-1);//sigaction
}
int main(int argc,char* argv[]){
if(argc<=2){
cout<<"argc<=2"<<endl;
return 1;
}
const char* ip=argv[1];
int port=atoi(argv[2]);
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 listenfd=socket(PF_INET,SOCK_STREAM,0);
assert(listenfd>=0);
ret=bind(listenfd,(struct sockaddr*)&address,sizeof(address));
assert(ret!=-1);
ret=listen(listenfd,5);
assert(ret!=-1);
epoll_event events[MAX_EVENT_NUMBER];
int epollfd=epoll_create(5);
assert(epollfd!=-1);
addfd(epollfd,listenfd);
ret=socketpair(PF_UNIX,SOCK_STREAM,0,pipefd);
assert(ret!=-1);
setnonblocking(pipefd[1]);
addfd(epollfd,pipefd[0]);
addsig(SIGHUP);
addsig(SIGCHLD);
addsig(SIGTERM);
addsig(SIGINT);
bool stop=false;//是否终止服务端
while(!stop){
int number=epoll_wait(epollfd,events,MAX_EVENT_NUMBER,-1);//epoll无限期等待事件就绪
if((number<0)&&(errno!=EINTR)){
cout<<"epoll error"<<endl;
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);//允许建立连接
addfd(epollfd,connfd);//新连接注册到事件表
}
else if((sockfd==pipefd[0])&&(events[i].events&EPOLLIN)){//若管道读端可读事件就绪表明有信号传递给主线程
int sig;
char signals[1024];
ret=recv(pipefd[0],signals,sizeof(signals),0);//接收管道内的信号
if(ret!=-1){
continue;
}
else if(ret==0){
continue;
}
else{
for(int i=0;i<ret;i++){
switch(signals[i]){
case SIGCHLD:
case SIGHUP:{
continue;
}
case SIGTERM:
case SIGINT:{
stop=true;//停止服务端程序
}
}
}
}
}
else{}//这里没有定义处理客户端连接的逻辑
}
}
cout<<"close"<<endl;
close(listenfd);
close(pipefd[0]);
close(pipefd[1]);
return 0;
}