统一事件源epoll代码示例

可以将信号注册进pipe管道的写端,通过对读端的监听,来实现统一事件源。

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.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 <pthread.h>

#define MAX_EVENT_NUMBER 1024
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);
}

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

        int ret = 0;
        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, (sockaddr*)&address, sizeof(address));
        if (ret == -1) {
                printf("errno is %d\n", errno);
                return 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_server = false;

        while (!stop_server) {
                int number = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);
                if ((number < 0) && (errno != EINTR)) {
                        printf("epoll failure\n");
                        break;
                }

                for (int i=0; i<number; i++) {
                        int sockfd = events[i].data.fd;
                        if (sockfd == listenfd) {
                                printf("New connection is coming.\n");
                                sockaddr_in client_address;
                                socklen_t client_addrlen = sizeof(client_address);
                                int connfd = accept(listenfd, (sockaddr*)&client_address,
                                                &client_addrlen);
                                printf("New connection established.\n");
                                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:
                                                        {
                                                                printf("SIGCHLD CONTINUE\n");
                                                                continue;
                                                        }
                                                        case SIGHUP:
                                                        {
                                                                printf("SIGHUP CONTINUE\n");
                                                                continue;
                                                        }
                                                        case SIGTERM:
                                                        {
                                                                printf("SIGTERM END PROGRAM\n");
                                                                stop_server = true;
                                                                break;
                                                        }
                                                        case SIGINT:
                                                        {
                                                                printf("SIGINT END PROGRAM\n");
                                                                stop_server = true;
                                                                break;
                                                        }
                                                }
                                        }
                                }
                        }
                        else {
                                // handle connected fd, ignore
                        }
                }
        }

        printf("close fds\n");
        close(listenfd);
        close(pipefd[1]);
        close(pipefd[0]);
        return 0;

}

Makefile的文件内容:

uniserver : uniserver.cpp
        g++ -o uniserver uniserver.cpp -lpthread

编译出服务器程序之后,运行服务器:

$ ./uniserver 127.0.0.1 12111 
New connection is coming.
New connection established.
SIGTERM END PROGRAM
close fds
$ ./uniserver 127.0.0.1 12111 
^CSIGINT END PROGRAM
close fds

运行客户端:

$ telnet 127.0.0.1 12111 
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
a
^C^]
telnet> quit
Connection closed.
$ ps ux | grep uniserver
work     20265  0.0  0.0  8192  828 pts/0    S+   23:22   0:00 ./uniserver 127.0.0.1 12111
work     20806  0.0  0.0 51132  580 pts/1    S+   23:23   0:00 grep uniserver
$ kill 20265

可以看出,客户端的连接请求得到服务器端的响应;发出的内容"a"在服务器端没有处理和展现。

使用向服务器发信号,得到处理;服务器端直接Ctrl+C也得到了处理。

同时可以看出,信号是int类型,但是传输的时候,采用的是char*,并且只传输了一个字节就可以了;在接收端进行switch的时候,也只针对char数据的一个元素进行比对就可以了。 

 

posted @ 2016-06-02 23:25  blcblc  阅读(1451)  评论(0编辑  收藏  举报