unix网络编程 str_cli epoll 非阻塞版本

unix网络编程 str_cli epoll 非阻塞版本

unix网络编程str_cli使用epoll实现讲了使用epoll配合阻塞io来实现str_cli,这个版本是配合非阻塞io.
可以看到采用非阻塞io以后复杂度大大提升了. 这个版本是在原书select版本基础之上修改而来,可以看出epoll又比select版本复杂了很多,每次都需要调用epoll_ctl三次,效率肯定比select还低.

存在一个问题!!就是epoll_wait对于重定向的stdin,始终阻塞,不晓得什么原因,以后再研究吧!

因为不能重定向stdin所以也不能测试性能,只能说是可以工作.

/* include nonb1 */
#include "../lib/unp.h"
#include <sys/epoll.h>
//epoll 非阻塞io, 采用了非阻塞io以后性能得到大幅提升,但是复杂度也飞速提升。

//确保events有足够的空间,这里足够了
//添加一个事件到队列中,可能会改变数组中的epoll_event数量。
static uint32_t addEvents(struct epoll_event * events,uint32_t nfds,int fd,uint32_t event){
    int i=0;
    for(i=0;i<nfds;i++){
        if(events[i].data.fd==fd){
            events[i].events|=event;
        }
    }
    if(i==nfds){
        events[i].data.fd=fd;
        events[i].events=event;
        nfds++;
    }
    return nfds;
}
#define VOL2
void str_cli(FILE *fp, int sockfd)
{
    int         val,stdineof=0;
    ssize_t     n, nwritten;
    char        to[MAXLINE], fr[MAXLINE];
    char        *toiptr, *tooptr, *friptr, *froptr;
    struct epoll_event event;
    struct epoll_event events[20];
    int i,efd,nfds;
    int noevent=0;

    val = Fcntl(sockfd, F_GETFL, 0);
    Fcntl(sockfd, F_SETFL, val | O_NONBLOCK);

    val = Fcntl(STDIN_FILENO, F_GETFL, 0);
    Fcntl(STDIN_FILENO, F_SETFL, val | O_NONBLOCK);

    val = Fcntl(STDOUT_FILENO, F_GETFL, 0);
    Fcntl(STDOUT_FILENO, F_SETFL, val | O_NONBLOCK);

    toiptr = tooptr = to;   /* initialize buffer pointers */
    friptr = froptr = fr;
    stdineof = 0;

    efd = epoll_create (10);
    if(efd<0){
        err_sys("epoll create failed");
    }


    event.data.fd=fileno(fp);
    event.events=EPOLLIN;
    epoll_ctl(efd,EPOLL_CTL_ADD,fileno(fp),&event);

    event.data.fd=sockfd;
    event.events=EPOLLIN;
    epoll_ctl(efd,EPOLL_CTL_ADD,sockfd,&event);

    event.data.fd=STDOUT_FILENO;
    event.events=EPOLLOUT;
    epoll_ctl(efd,EPOLL_CTL_ADD,STDOUT_FILENO,&event);


    for ( ; ; ) {

        event.data.fd = fileno(fp);
        event.events = 0;
        //fprintf(stderr, "tooptr=0x%x,toiptr=0x%x,froptr=0x%x,friptr=0x%x \n", tooptr, toiptr, froptr, friptr);
        if (stdineof == 0 && toiptr < &to[MAXLINE]) //并不能确定在不在里面,多做一次不是坏事
        {
            event.events = EPOLLIN;
            epoll_ctl(efd, EPOLL_CTL_MOD, fileno(fp), &event); //read from stdin
        }
        else
            epoll_ctl(efd, EPOLL_CTL_MOD, fileno(fp), &event);


        event.data.fd = sockfd;
        event.events = 0;
        if (friptr < &fr[MAXLINE])
            event.events |= EPOLLIN;            /* read from socket */
        if (tooptr != toiptr)
            event.events |= EPOLLOUT;            /* data to write to socket */
        epoll_ctl(efd, EPOLL_CTL_MOD, sockfd, &event);

        event.data.fd = STDOUT_FILENO;
        event.events = 0;
        if (froptr != friptr) {
            event.events = EPOLLOUT;
            epoll_ctl(efd, EPOLL_CTL_MOD, STDOUT_FILENO, &event);    /* data to write to stdout */
        }
        else {
            epoll_ctl(efd, EPOLL_CTL_MOD, STDOUT_FILENO, &event);
        }

        nfds = epoll_wait(efd, events, sizeof(events) / sizeof(struct epoll_event), -1);
        //fprintf(stderr, "nfds return:%d, %d,0x%x,sockfd=%d\n", nfds, events[0].data.fd, events[0].events, sockfd);
        startloop:
        for (i = 0; i < nfds; i++) {
            if (events[i].data.fd == STDIN_FILENO && events[i].events != 0) {
                events[i].events = 0; //清除处理过的事件
                if ((n = read(STDIN_FILENO, toiptr, &to[MAXLINE] - toiptr)) < 0) {
                    if (errno != EWOULDBLOCK)
                        err_sys("read error on stdin");

                } else if (n == 0) {
#ifdef VOL2
                    fprintf(stderr, "%s: EOF on stdin\n", gf_time());
#endif
                    stdineof = 1;            /* all done with stdin */
                    if (tooptr == toiptr)
                        Shutdown(sockfd, SHUT_WR);/* send FIN */

                } else {
#ifdef VOL2
                    fprintf(stderr, "%s: read %d bytes from stdin\n", gf_time(), n);
#endif
                    toiptr += n;            /* # just read */
                    int ret2=addEvents(events, nfds, sockfd, EPOLLOUT);
                    if(ret2!=nfds){
                        nfds=ret2;
                        goto startloop;
                    }
                }
            }

            if (events[i].data.fd == sockfd && events[i].events != 0) {
                if (events[i].events & EPOLLIN) {
                    printf("read socket\n");
                    if ((n = read(sockfd, friptr, &fr[MAXLINE] - friptr)) < 0) {
                        printf("read socket error");
                        if (errno != EWOULDBLOCK)
                            err_sys("read error on socket");

                    } else if (n == 0) {
#ifdef VOL2
                        fprintf(stderr, "%s: EOF on socket\n", gf_time());
#endif
                        if (stdineof)
                            return;        /* normal termination */
                        else
                            err_quit("str_cli: server terminated prematurely");

                    } else {
#ifdef VOL2
                        fprintf(stderr, "%s: read %d bytes from socket\n",
                                gf_time(), n);
#endif
                        friptr += n;        /* # just read */
                        int ret2 = addEvents(events, nfds, STDOUT_FILENO, EPOLLOUT);/* try and write for next loop */
                        if(ret2!=nfds){
                            nfds=ret2;
                            events[i].events&=~EPOLLIN; //清除已经处理过的in事件
                            goto startloop;
                        }
                    }
                }
                if (events[i].events & EPOLLOUT) {
                    if ((n = toiptr - tooptr) > 0) {
                        if ((nwritten = write(sockfd, tooptr, n)) < 0) {
                            if (errno != EWOULDBLOCK)
                                err_sys("write error to socket");

                        } else {
#ifdef VOL2
                            fprintf(stderr, "%s: wrote %d bytes to socket\n",
                                    gf_time(), nwritten);
#endif
                            tooptr += nwritten;    /* # just written */
                            if (tooptr == toiptr) {
                                toiptr = tooptr = to;    /* back to beginning of buffer */
                                if (stdineof)
                                    Shutdown(sockfd, SHUT_WR);    /* send FIN */
                            }
                        }
                    }
                }
                events[i].events = 0; //清除处理过的事件
            }

            if (events[i].data.fd == STDOUT_FILENO) {
                if ((n = friptr - froptr) > 0) {
                    if ((nwritten = write(STDOUT_FILENO, froptr, n)) < 0) {
                        if (errno != EWOULDBLOCK)
                            err_sys("write error to stdout");

                    } else {
#ifdef VOL2
                        fprintf(stderr, "%s: wrote %d bytes to stdout\n",
                                gf_time(), nwritten);
#endif
                        froptr += nwritten;        /* # just written */
                        if (froptr == friptr)
                            froptr = friptr = fr;    /* back to beginning of buffer */
                    }
                }

            }
        }
    }
}

你可能感兴趣的:(unix网络编程 str_cli epoll 非阻塞版本)