epoll实践

代码做的是回射服务


客户端:

#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,这会降低多少效率是最值得关心的。


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


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值