信号是一种异步事件:信号处理函数和程序的主循环是两条不同的执行路线。
//统一事件源
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <signal.h>
#define MAX_EVENT_NUMBER 1024
static int pipefd[2];
int setnonblocking(int fd); //设置非阻塞
void addfd(int epollfd, int fd); //添加描述符的事件
void sig_handler(int sig); //信号处理函数
void addsig(int sig); //添加信号处理
int main(int argc, char **argv)
{
if (argc != 2) {
fprintf(stderr, "Usage: %s port\n", basename(argv[0]));
return 1;
}
int port = atoi(argv[1]);
int ret = 0;
int error;
struct sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_family = AF_INET;
address.sin_port = htons(port);
address.sin_addr.s_addr = htonl(INADDR_ANY);
int sockfd = socket(PF_INET, SOCK_STREAM, 0);
if (sockfd == -1)
return 1;
printf("server start...\n");
//设置地址可重用
int reuse = 1;
ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
if (ret == -1) {
error = errno;
while ((close(sockfd) == -1) && (errno == EINTR));
errno = error;
return 1;
}
printf("server reuseaddr success\n");
if ((bind(sockfd, (struct sockaddr*)&address, sizeof(address)) == -1) ||
(listen(sockfd, 5) == -1)) {
error = errno;
while ((close(sockfd) == -1) && (errno == EINTR));
errno = error;
return 1;
}
printf("server bind and listen success\n");
epoll_event events[MAX_EVENT_NUMBER];
int epollfd = epoll_create(5);
if (epollfd == -1) {
error = errno;
while ((close(sockfd) == -1) && (errno == EINTR));
errno = error;
return 1;
}
addfd(epollfd, sockfd);
//使用socketpair创建管道,注册pipefd[0]上的可读事件
ret = socketpair(PF_UNIX, SOCK_STREAM, 0, pipefd);
if (ret == -1) {
error = errno;
while ((close(sockfd) == -1) && (errno == EINTR));
errno = error;
return 1;
}
setnonblocking(pipefd[1]);
addfd(epollfd, pipefd[0]);
//设置信号处理
addsig(SIGHUP);
addsig(SIGCHLD);
addsig(SIGTERM);
addsig(SIGINT);
bool stop_server = false;
while (!stop_server) {
int number = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);
if (number < 0 && errno != EINTR) {
fprintf(stderr, "epoll failed\n");
break;
}
for (int i = 0; i < number; i++) {
int listenfd = events[i].data.fd;
if (listenfd == sockfd) { //处理新连接
struct sockaddr_in client_address;
socklen_t client_addrlength = sizeof(client_address);
int connfd = -1;
while ( ((connfd = accept(listenfd, (struct sockaddr*)&client_address, &client_addrlength)) == -1) &&
(connfd == EINTR) );
addfd(epollfd, connfd);
}
else if (listenfd == pipefd[0] && events[i].events & EPOLLIN) { //处理信号
char signals[1024];
ret = recv(pipefd[0], signals, sizeof(signals), 0);
if (ret == -1)
continue;
else if (ret == 0)
continue;
else {
//每个信号值占1字节,所以按字节来逐个接收信号
for (int i = 0; i < ret; i++) {
switch(signals[i]) {
case SIGCHLD:
{
fprintf(stderr, "recv SIGCHLD\n");
continue;
break;
}
case SIGHUP:
{
fprintf(stderr, "recv SIGHUP\n");
continue;
break;
}
case SIGTERM:
{
fprintf(stderr, "recv SIGTERM, close server\n");
stop_server = true;
break;
}
case SIGINT:
{
fprintf(stderr, "recv SIGINT, close server\n");
stop_server = true;
break;
}
default:
break;
}
}
}
}
else {
}
}
}
printf("close fds\n");
close(sockfd);
close(pipefd[1]);
close(pipefd[0]);
return 0;
}
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);
}
参考:《linux高性能服务器编程》