linux网络编程十八:统一事件源

来源:互联网 发布:苗勒管永存综合征知乎 编辑:程序博客网 时间:2024/05/29 11:05

信号是一种异步事件:信号处理函数和程序的主循环是两条不同的执行路线。


//统一事件源#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 1024static 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高性能服务器编程》




0 0
原创粉丝点击