epoll 1

/*
 poll 在已连接的套接字中遍历
 epoll_wait 返回的都是活跃的套接字,所以减少了很多无效的套接字
 
 Poll模型 :
 每次调用 poll函数的时候。都需要把监听套接字与已连接套接字所感兴趣的事件数组 拷贝到内核。
 
 
 LT模式 :
 Write  EPOLLOUT 事件
    高电平 writebuf内核有空闲空间,我们就说他处于高电平状态,也就是一直处于活跃状态。此时可能会产生busy waitting loop
    低电平 当writebuf内核没有空闲空间,我们就说他处于低电平状态,没有激活。
Read EPOLLIN 事件
    xxx
 
 
 
 ET模型 边缘触发
 在该模式下,一开始我们就关注EPOLLIN事件和EPOLLOUT事件 , 此时writbuf一直处于高电平,不会触发EPOLLOUT事件.
        而EPOLLIN可能会产生,因为开始Readbuf是空的,如果在 epoll_wait前,readbuf有数据了,那么就有unreadablr--->readable,
        也就是产生了EPOLLLIN事件,这也是为什么监听socket 能够接收到外来请求的原因。
        关注EPOLLIN 和 EPOLLOUT事件后,我们也没必要取消他们的关注,只有到断开他们的socketfd时 , 我们才需要取消。
 
需要注意的是,在读写时,如果是读,一定要读到EAGAIN,写也是要写到EAGAIN
 socket从unreadable变为readable或从unwritable变为writable
 有些人说从readable变为unreadable或者writable变为unwritable时也会触发事件。
 我个人觉得第一种合理一点。
 
 
*/
 
#include <unistd.h> 
#include <sys/types.h> 
#include <fcntl.h> 
#include <sys/socket.h> 
#include <netnet/in.h> 
#include <arpa/inet.h> 
#include <signal.h> 
#include <sys/wait.h> 
#include <sys/epoll.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <errno.h> 
#include <string.h> 
 
#include <vector.h> 
#include <algorithm> 
#include <iostream> 
 
typedef std::vector<struct epoll_event> EventList ; 
 
#define ERR_EXIT(m) \ 
    do\ 
    {\ 
        perror(m);\ 
        exit(EXIT_FAILURE);\ 
    }while(0); 
 
int main ( void ) { 
    //防止进程由于客户端的关闭而使服务器进程退出 
    signal(SIGPIPE ,SIG_IGN) ;   
    //防止僵死进程的发生 
    signal(SIGCHLD,SIG_IGN); 
 
 
    //备胎描述符 
    int idlefd = open("/dev/null",O_RDONLY | O_CLOEXEC) ; 
    //监听描述符 
    int listenfd ; 
 
    if((listenfd =socket(PF_INET,SOCK_STRAM|SOCK_NONBLOCK|SOCK_CLOSEXEC,IPPROTO_TCP))<0){
 
        ERR_EXIT("socket"); 
    } 
 
    struct sockaddr_in servaddr ; 
    memset(&servaddr,0,sizeof(servaddr)) ; 
    servaddr.sin_family = AF_INET; 
    servaddr.sin_port = htons(5188); 
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY) ; 
 
    int on =1 ;  
    //socketfd 重用 
    if(setsocketopt(listenfd,SOL_SOCKET,SO_REUSERADDR,&on,sizeof(n))<0) 
 
    { 
        ERR_EXIT("setsocketopt"); 
    } 
 
    if( bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) <0) 
        ERR_EXIT("bind"); 
 
    if(listen(listen,SOMAXCONN) <0) 
        ERR_EXIT("listen"); 
 
    std::vector<int> clients; 
    int epollfd;  
    // 函数返回一个epoll专用的描述符epfd,epfd引用了一个新的epoll机制例程(instance.)。 
    epollfd = epoll_create1(EPOLL_CLOEXEC); 
 
    //事件结构体 
    struct epoll_event event ; 
    event.data.fd = listenfd ; //关注listenfd 
    event.events = EPOLLIN ; // /* | EPOLLET*/; 默认是LT模型 
 
    // 把lisenfd 添加到epollfd 中进行管理 
    //如果是poll的话,就不用这样了,poll直接使用一个数组就行了 
    /*函数声明:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)  
该函数用于控制某个epoll文件描述符上的事件,可以注册事件,修改事件,删除事件。  
参数:  
epfd:由 epoll_create 生成的epoll专用的文件描述符;  
op:要进行的操作例如注册事件,可能的取值EPOLL_CTL_ADD 注册、EPOLL_CTL_MOD 修 改、EPOLL_CTL_DEL 删除  
 
fd:关联的文件描述符;  
event:指向epoll_event的指针;  
如果调用成功返回0,不成功返回-1  
 
71 - 78 就交给epollfd 内核帮我们做了,并且只有epoll_ctl才会拷贝到内核,一次拷贝就行了,以后就不用拷贝了, 
如果我们使用poll 那么每次循环都要拷贝到内核里面去,大大降低了效率 
        */
    epoll_ctl(epollfd , EPOLL_CTL_ADD,listenfd,&event); 
 
    //事件列表,初始为16 个 
    EventList events(16) ; 
    struct sockaddr_in peeraddr ; 
    socklen_t peerlen ; 
    int connfd ; 
 
    int nready ;  
    while(1){ 
 
/*函数声明:int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout)
该函数用于轮询I/O事件的发生; 
参数: 
epfd:由epoll_create 生成的epoll专用的文件描述符; 
epoll_event:用于回传代处理事件的数组,要处理的事件都会存储在events里面了 
maxevents:每次能处理的事件数; 
timeout:等待I/O事件发生的超时值(单位我也不太清楚);-1相当于阻塞,0相当于非阻塞。一般用-1即可 
返回发生事件数。 
         -1 表示等待到至少一个事件发生*/
        nready = epoll_wait(epollfd,&*events.begin(),static_cast<int>(events.size()), -1 );  
        if( nready == -1){ 
 
            if(errno == EINTR) 
                continue ; 
            ERR_EXIT("epoll_wait") ; 
        } 
 
        if(nready ==0 ) //nothing to happened  
            continue ; 
        //  
        if((size_t)nready == events.size()) 
            events.resize(events.size()*2) ; 
 
        for (int i = 0; i < nready; ++i) 
        { 
            //如果是监听socket,则accetp 
            if (events[i].data.fd == listenfd) 
            { 
                peerlen = sizeof( peeraddr ) ; 
                // 这里的处理还不够好,因为accept4每一次只能到tcp就绪队列里面拿出一个就绪socketfd , 有可能这个队列不止一个,
                //在并发的时候这是必然的, 所以最后是while掉accept4,把里面的就绪socketfd全部拿出来,而不时只拿一个socketfd。
                //下面的代码可根据需求进行改进 
                connfd =::accept4(listenfd,(struct sockaddr *)&peeraddr,&peerlen,SOCK_NONBLOCK|SOCK_CLOSEXEC) ;
 
                if( connfd == -1 ) 
                { 
 
                    if(errno == EMFILE){ 
                        close(idlefd) ; 
                        idlefd = accept(listenfd,NULL,NULL) ; 
                        close(idlefd) ; 
                        idlefd = open("/dev/null",O_RDONLY|O_CLOEXEC) ; 
                    } 
 
                } 
                else
                    ERR_EXIT("accept4") ; 
                std::cout<<"ip="<<inet_ntoa(peeraddr.sin_addr)<<" port="<<ntohs(peeraddr.sin_port)<<std::endl;
                clients.push_back(connfd) ; 
 
                event.data.fd =connfd ; 
                event.events = EPOLLIN/*| EPOLLET*/ ; 
                epoll_ctl(epollfd,EPOLL_CTL_ADD ,connfd ,&event) ; 
            }else if (events[i].events & EPOLLIN) 
            { 
                //下面的这些都要改进 
                connfd = events[i].data.fd; 
                if (connfd < 0) 
                    continue; 
 
                char buf[1024] = {0}; 
                int ret = read(connfd, buf, 1024); 
                if (ret == -1) 
                    ERR_EXIT("read"); 
                if (ret == 0) 
                { 
                    std::cout<<"client close"<<std::endl; 
                    close(connfd); 
                    event = events[i]; 
                    epoll_ctl(epollfd, EPOLL_CTL_DEL, connfd, &event); 
                    clients.erase(std::remove(clients.begin(), clients.end(), connfd), clients.end());
                    continue; 
                } 
 
                std::cout<<buf; 
                write(connfd, buf, strlen(buf)); 
            } 
 
 
 
        } 
 
    } 
 
 
} 


 
/*
正确的读法
n = 0;
while ((nread = read(fd, buf + n, BUFSIZ-1)) > 0) {
    n += nread;
}
if (nread == -1 && errno != EAGAIN) {
    perror("read error");
}
*/
 
 
/*
正确的写法
int nwrite, data_size = strlen(buf);
n = data_size;
while (n > 0) {
    nwrite = write(fd, buf + data_size - n, n);
    if (nwrite < n) {
        if (nwrite == -1 && errno != EAGAIN) {
            perror("write error");
        }
        break;
    }
    n -= nwrite;
}
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值