zoukankan      html  css  js  c++  java
  • epoll学习和回显服务器实现(C语言版)

      select提供的轮询会随着fd数目的增长降低效率,并且其上限是系统定义过的。UNP上select的例子需要编程者自己维护一个fd使用情况的结构,很抽象繁琐。后面的poll的例子和本文提到的epoll使用方法差不多,这是因为高级polling技术没有标准,各个操纵系统的实现不一样,而epoll是由Linux提供的,是对poll的改进。个人感觉区别在于epoll对缓冲池的维护(事件的加入和维护)更简单。

      通过对一篇文章(http://blog.csdn.net/ljx0305/article/details/4065058,已转载至“文章”一栏)的学习,笔者试着修改了并调试通过了C语言版的epoll服务器,本文是对学习过程的总结,如果需要epoll相关的详细信息(函数接口、结构体、使用注意等)请参照原文。

    epoll的工作过程

      epoll_create(size)创建监听数目为size的epoll句柄,使用 epoll_ctl()对这个句柄的事件集进行初始化。这样之后调用epoll_wait()等待所关心的事件是否发生,并对已发生的事件进行相应的操作。操作时需要根据情况使用epoll_ctl()对事件的行为进行修改和删除。

    epoll回显服务器代码

    #include <sys/socket.h>
    #include <sys/epoll.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <string.h>
    #include <errno.h>
    #include <sys/types.h>
    
    #define    MAXLINE        4096
    #define    LISTENQ        20
    #define    SERV_PORT    9877
    
    int main(int argc, char* argv[])
    {
        int i, maxi, listenfd, connfd, sockfd, epfd,nfds;
        ssize_t n;
        char BUF[MAXLINE];
        socklen_t clilen;
    
        //ev用于注册事件,数组用于回传要处理的事件
    
        struct epoll_event ev,events[20];
        //生成用于处理accept的epoll专用的文件描述符
    
        epfd=epoll_create(256);
        struct sockaddr_in cliaddr, servaddr;
        listenfd = socket(AF_INET, SOCK_STREAM, 0);
     
        //setnonblocking(listenfd);
        //设置与要处理的事件相关的文件描述符
    
        ev.data.fd=listenfd;
        ev.events=EPOLLIN|EPOLLET;
        //注册epoll事件
    
        epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);
    
        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_addr.s_addr = htonl (INADDR_ANY);
        servaddr.sin_port = htons (SERV_PORT);
        bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
        listen(listenfd, LISTENQ);
        maxi = 0; //useless
        for ( ; ; ) {
            nfds=epoll_wait(epfd,events,20,0);
    
            for(i=0;i<nfds;++i)
            {
                if(events[i].data.fd==listenfd)//如果新监测到一个SOCKET用户连接到了绑定的SOCKET端口,建立新的连接。
                {
                    connfd = accept(listenfd,(struct sockaddr *)&cliaddr, &clilen);
                    if(connfd<0){
                        perror("connfd<0");
                        exit(1);
                    }
                    //setnonblocking(connfd);
    
                    char *str = inet_ntoa(cliaddr.sin_addr);
                    printf("accapt a connection from %s\n", str);
    
                    ev.data.fd=connfd;
                    ev.events=EPOLLIN|EPOLLET;
                    epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev);
                }
                else if (events[i].events&EPOLLIN)
                //如果是已经连接的用户,并且收到数据,那么进行读入。
    
                {
                    printf("EPOLLIN\n");
                    if ( (sockfd = events[i].data.fd) < 0)
                        continue;
                    if ( (n = read(sockfd, BUF, MAXLINE)) < 0) {
                        if (errno == ECONNRESET) {
                            close(sockfd);
                            events[i].data.fd = -1;
                        } else
                            printf("readline error\n");
                    } else if (n == 0) {
                        close(sockfd);
                        events[i].data.fd = -1;
                    }
                    BUF[n] = '\0';
                    printf("AFTER EPOLLIN\n");
    
                    ev.data.fd=sockfd;
                    ev.events=EPOLLOUT|EPOLLET;
                    //读完后准备写
                    epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);
    
                }
                else if(events[i].events&EPOLLOUT) // 如果有数据发送
    
                {
                    sockfd = events[i].data.fd;
                    write(sockfd, BUF, n);
    
                    ev.data.fd=sockfd;
                    ev.events=EPOLLIN|EPOLLET;
                    //写完后,这个sockfd准备读
                    epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);
                }
            }
        }
        return 0;
    }
  • 相关阅读:
    web前端学习笔记(CSS盒子的定位)
    web前端学习笔记(CSS盒子的浮动)
    数百篇「原创」文章,助你完成技术「体系化」
    linux quota磁盘限额,引发的rename系统调用 errno:18
    dnsperf
    stop容器,把信号量传给java进程,优雅退出
    JNA 调用操作系统函数 和 系统调用
    自顶向下深入分析Netty(五)--Future
    来测试下你的Java编程能力
    Netty笔记
  • 原文地址:https://www.cnblogs.com/wuyuegb2312/p/2478748.html
Copyright © 2011-2022 走看看