epoll实践

来源:互联网 发布:saas源码 java 编辑:程序博客网 时间:2024/06/04 18:03

代码做的是回射服务


客户端:

#include <errno.h>#include <netdb.h>#include <unistd.h>#include <cstdio>#include <cstdlib>#include <cstring>const int BUFFSIZ = 4096;int connect(const char * host, const char * serv){    int               connfd;    int               n;    struct addrinfo   hints;    struct addrinfo * res;    struct addrinfo * ressave;    memset(&hints, 0, sizeof(hints));    hints.ai_family = AF_UNSPEC;    hints.ai_socktype = SOCK_STREAM;    if (0 != (n = getaddrinfo(host, serv, &hints, &res))) {        printf("getaddrinfo error for %s, %s: %s\n",                (NULL == host) ? "(no hostname)" : host,                (NULL == serv) ? "(no servname)" : serv,                gai_strerror(n));        exit(1);    }    ressave = res;    while (NULL != res) {        connfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);        if (-1 != connfd) {            if (0 == ::connect(connfd, res->ai_addr, res->ai_addrlen)) {                break;            }            else {                close(connfd);            }        }        res = res->ai_next;    }    freeaddrinfo(ressave);    if (NULL == res) {        printf("connect failed\n");        exit(1);    }    return(connfd);}int main(int argc, char ** argv){    if (3 != argc) {        printf("usage: %s <host> <serv>\n", argv[0]);        exit(1);    }    int connfd = connect(argv[1], argv[2]);    char buff[BUFFSIZ] = { 0 };    while (NULL != fgets(buff, BUFFSIZ, stdin)) {        int len = strlen(buff);        int offset = 0;        while (offset < len) {            int nw = write(connfd, buff + offset, len - offset);            if (-1 == nw && EINTR == errno) {                continue;            }            else if (nw <= 0) {                perror("write");                exit(1);            }            else {                offset += nw;            }        }        offset = 0;        while (len > 0) {            int nr = read(connfd, buff + offset, len);            if (-1 == nr) {                if (EINTR != errno) {                    perror("read");                    exit(1);                }                continue;            }            else if (0 == nr) {                printf("connection close by peer\n");                exit(1);            }            else {                len -= nr;                offset += nr;            }        }        if (-1 == fputs(buff, stdout)) {            perror("fputs");            break;        }    }    if (ferror(stdin)) {        perror("fgets");    }    close(connfd);    exit(0);     }
客户端用的是阻塞式IO,所以服务器关闭时不能在第一时间得知,解决方法有:1. 改成非阻塞式IO,见UNP第十六章;2. 加上心搏函数,见UNP第二十四章。


服务器:

LT模式的epoll实现:

#include <errno.h>#include <netdb.h>#include <fcntl.h>#include <unistd.h>#include <sys/epoll.h>#include <cstdio>#include <cstdlib>#include <cstring>const int LISTENQ = 1024;const int EVENTSZ = 64;const int BUFFSIZ = 4096;int listen(const char * host, const char * serv){    int               listenfd;    int               n;    const int         on = 1;    struct addrinfo   hints;    struct addrinfo * res;    struct addrinfo * ressave;    memset(&hints, 0, sizeof(hints));    hints.ai_flags = AI_PASSIVE;    hints.ai_family = AF_UNSPEC;    hints.ai_socktype = SOCK_STREAM;    if (0 != (n = getaddrinfo(host, serv, &hints, &res))) {        printf("getaddrinfo error for %s, %s: %s\n",                (NULL == host) ? "(no hostname)" : host,                (NULL == serv) ? "(no servname)" : serv,                gai_strerror(n));        exit(1);    }    ressave = res;    while (NULL != res) {        listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);        if (-1 != listenfd) {            setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));            if (0 == bind(listenfd, res->ai_addr, res->ai_addrlen)) {                break;            }            else {                close(listenfd);            }        }        res = res->ai_next;    }    freeaddrinfo(ressave);    if (NULL == res) {        printf("listen failed\n");        exit(1);    }    if (-1 == ::listen(listenfd, LISTENQ)) {        printf("listen error: %s\n", strerror(errno));        exit(1);    }    return(listenfd);}bool setnonblock(int sockfd){    int flag = fcntl(sockfd, F_GETFL, 0);    if (-1 == flag) {        perror("fcntl(F_GETFL)");        return(false);    }    if (-1 == fcntl(sockfd, F_SETFL, flag | O_NONBLOCK)) {        perror("fcntl(F_SETFL)");        return(false);    }    return(true);}int main(int argc, char ** argv){    if (3 != argc) {        printf("usage: %s <host> <serv>\n", argv[0]);        exit(1);    }    struct epoll_event ev;    struct epoll_event events[EVENTSZ];    char buff[BUFFSIZ] = { 0 };    int epollfd = epoll_create(LISTENQ);    if (-1 == epollfd) {        perror("epoll_create");        exit(1);    }    int listenfd = listen(argv[1], argv[2]);    ev.data.fd = listenfd;    ev.events = EPOLLIN;    if (-1 == epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &ev)) {        perror("epoll_ctl(EPOLL_CTL_ADD)");        exit(1);    }    for ( ; ; ) {        int n = epoll_wait(epollfd, events, EVENTSZ, -1);        if (-1 == n) {            perror("epoll_wait");            break;        }        for (int i = 0; i < n; ++i) {            const int sockfd = events[i].data.fd;            if (sockfd == listenfd) {                int accefd = accept(listenfd, NULL, NULL);                if (-1 == accefd) {                    perror("accept");                    exit(1);                }                ev.data.fd = accefd;                ev.events = EPOLLIN;                if (-1 == epoll_ctl(epollfd, EPOLL_CTL_ADD, accefd, &ev)) {                    perror("epoll_ctl(EPOLL_CTL_ADD)");                    close(accefd);                }                printf("connect by new client\n");            }            else {                int nr = read(sockfd, buff, BUFFSIZ);                if (nr <= 0) {                    if (-1 == nr) {                        perror("read");                    }                    else {                        printf("connection close by peer\n");                    }                    if (-1 == epoll_ctl(epollfd, EPOLL_CTL_DEL,                                         sockfd, &ev)) {                        perror("epoll_ctl(EPOLL_CTL_DEL)");                    }                    close(sockfd);                    continue;                }                int nw = 0;                while (nw < nr) {                    int n = write(sockfd, buff + nw, nr - nw);                    if (-1 == n && EWOULDBLOCK == errno) {                        continue;                    }                    else if (n <= 0) {                        break;                    }                    else {                        nw += n;                    }                }                if (nw < nr) {                    if (-1 == epoll_ctl(epollfd, EPOLL_CTL_DEL,                                         sockfd, &ev)) {                        perror("epoll_ctl(EPOLL_CTL_DEL)");                    }                    close(sockfd);                }            }        }    }    close(epollfd);    close(listenfd);    exit(0);}

ET模式的epoll实现:

#include <errno.h>#include <netdb.h>#include <fcntl.h>#include <unistd.h>#include <sys/epoll.h>#include <cstdio>#include <cstdlib>#include <cstring>const int LISTENQ = 1024;const int EVENTSZ = 64;const int BUFFSIZ = 4096;int listen(const char * host, const char * serv){    int               listenfd;    int               n;    const int         on = 1;    struct addrinfo   hints;    struct addrinfo * res;    struct addrinfo * ressave;    memset(&hints, 0, sizeof(hints));    hints.ai_flags = AI_PASSIVE;    hints.ai_family = AF_UNSPEC;    hints.ai_socktype = SOCK_STREAM;    if (0 != (n = getaddrinfo(host, serv, &hints, &res))) {        printf("getaddrinfo error for %s, %s: %s\n",                (NULL == host) ? "(no hostname)" : host,                (NULL == serv) ? "(no servname)" : serv,                gai_strerror(n));        exit(1);    }    ressave = res;    while (NULL != res) {        listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);        if (-1 != listenfd) {            setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));            if (0 == bind(listenfd, res->ai_addr, res->ai_addrlen)) {                break;            }            else {                close(listenfd);            }        }        res = res->ai_next;    }    freeaddrinfo(ressave);    if (NULL == res) {        printf("listen failed\n");        exit(1);    }    if (-1 == ::listen(listenfd, LISTENQ)) {        printf("listen error: %s\n", strerror(errno));        exit(1);    }    return(listenfd);}bool setnonblock(int sockfd){    int flag = fcntl(sockfd, F_GETFL, 0);    if (-1 == flag) {        perror("fcntl(F_GETFL)");        return(false);    }    if (-1 == fcntl(sockfd, F_SETFL, flag | O_NONBLOCK)) {        perror("fcntl(F_SETFL)");        return(false);    }    return(true);}int main(int argc, char ** argv){    if (3 != argc) {        printf("usage: %s <host> <serv>\n", argv[0]);        exit(1);    }    struct epoll_event ev;    struct epoll_event events[EVENTSZ];    char buff[BUFFSIZ] = { 0 };    int epollfd = epoll_create(LISTENQ);    if (-1 == epollfd) {        perror("epoll_create");        exit(1);    }    int listenfd = listen(argv[1], argv[2]);    if (!setnonblock(listenfd)) {        exit(1);    }    ev.data.fd = listenfd;    ev.events = EPOLLIN | EPOLLET;    if (-1 == epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &ev)) {        perror("epoll_ctl(EPOLL_CTL_ADD)");        exit(1);    }    for ( ; ; ) {        int n = epoll_wait(epollfd, events, EVENTSZ, -1);        if (-1 == n) {            perror("epoll_wait");            break;        }        for (int i = 0; i < n; ++i) {            const int sockfd = events[i].data.fd;            if (sockfd == listenfd) {                for ( ; ; ) {                    int accefd = accept(listenfd, NULL, NULL);                    if (-1 == accefd) {                        if (EAGAIN == errno) {                            break;                        }                        continue;                    }                    if (!setnonblock(accefd)) {                        close(accefd);                        continue;                    }                    ev.data.fd = accefd;                    ev.events = EPOLLIN | EPOLLET;                    if (-1 == epoll_ctl(epollfd, EPOLL_CTL_ADD, accefd, &ev)) {                        perror("epoll_ctl(EPOLL_CTL_ADD)");                        close(accefd);                    }                    printf("connect by new client\n");                }            }            else {                for ( ; ; ) {                    int nr = read(sockfd, buff, BUFFSIZ);                    if (-1 == nr && EAGAIN == errno) {                        break;                    }                    if (0 == nr) {                        printf("connection close by peer\n");                    }                    if (nr <= 0) {                        if (-1 == epoll_ctl(epollfd, EPOLL_CTL_DEL,                                             sockfd, &ev)) {                            perror("epoll_ctl(EPOLL_CTL_DEL)");                        }                        close(sockfd);                        break;                    }                    int nw = 0;                    while (nw < nr) {                        int n = write(sockfd, buff + nw, nr - nw);                        if (-1 == n && EWOULDBLOCK == errno) {                            continue;                        }                        else if (n <= 0) {                             break;                        }                        else {                            nw += n;                        }                    }                    if (nw < nr) {                        if (-1 == epoll_ctl(epollfd, EPOLL_CTL_DEL,                                             sockfd, &ev)) {                            perror("epoll_ctl(EPOLL_CTL_DEL)");                        }                        close(sockfd);                        break;                    }                }            }        }    }    close(epollfd);    close(listenfd);    exit(0);}
ET模式的epoll实现,在write时处理得不好,因为sockfd是非阻塞的,所以当发送缓存不足时,write会一直返回-1(errno为EWOULDBLOCK),直到有足够的发送缓存。我估计要把EPOLLOUT加到sockfd的对应event中,但这样就得为每个sockfd分配一块独立的缓存,更重要的是,我们可能得反复调用epoll_ctl(EPOLL_CTL_MOD)去增 / 减EPOLLIN / EPOLLOUT,这会降低多少效率是最值得关心的。


当然对于回射服务器没必要这样做。