epoll 非阻塞IO 邊沿觸發模式

Mr.禾發表於2020-10-28

與之前的epoll預設的水平觸發模式相比
epoll的非阻塞IO模式,利用邊沿觸發模式,同時修改read()函式模式為非阻塞讀取,這樣在減少epoll_wait()函式呼叫的同時也實現了水平觸發的效果,是對epoll最優的模式。

伺服器端

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<string.h>
#include<arpa/inet.h>
#include<sys/epoll.h>
#include<errno.h>
#include<ctype.h>

#define MAXLINE 8196
#define SERV_PORT 6666
#define OPEN_MAX  1000
#define SERV_IP "127.0.0.1"
void perr_exit(const char *str)
{
    perror(str);
    exit(1);
}
int main(int argc,char *argv[])
{
    int i,n;
    int listenfd,sockfd,connfd;
    ssize_t nready,efd,res;
    char buf[MAXLINE],str[INET_ADDRSTRLEN];
    socklen_t clie_len;
    int flag;

    struct sockaddr_in clie_addr,serv_addr;
    struct  epoll_event tep,ep[OPEN_MAX];

    listenfd=socket(AF_INET,SOCK_STREAM,0);

    int opt=1;
    setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));//埠複用
    bzero(&serv_addr,sizeof(serv_addr));
    serv_addr.sin_family=AF_INET;
    //serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
    inet_pton(AF_INET,SERV_IP,&serv_addr.sin_addr.s_addr);
    serv_addr.sin_port=htons(SERV_PORT);

    bind(listenfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));//繫結IP和埠
    
    listen(listenfd,128);

    efd=epoll_create(OPEN_MAX);//建立epoll模型,efd指向紅黑樹根節點
    if(efd==-1)
        perr_exit("epoll error!");
    
    tep.events=EPOLLIN | EPOLLET;  //邊沿觸發模式
    
    printf("accept connect...\n");

    clie_len=sizeof(clie_addr);
    connfd=accept(listenfd,(struct sockaddr *)&clie_addr,&clie_len);
    printf(" connect client ip:%s---port:%d\n",
            inet_ntop(AF_INET,&clie_addr.sin_addr.s_addr,str,sizeof(str)),
            ntohs(clie_addr.sin_port)
            );

    flag=fcntl(connfd,F_GETFL);//修改connfd為非阻塞讀
    flag |= O_NONBLOCK;
    fcntl(connfd,F_SETFL,flag);

    tep.data.fd=connfd;
    epoll_ctl(efd,EPOLL_CTL_ADD,connfd,&tep);

    while(1)
    {
        printf("***************************************\n");
        printf("epoll_wait begin\n");
        res=epoll_wait(efd,ep,10,-1);
        printf("epoll_wait end %ld\n",res);

        if(ep[0].data.fd==connfd)
        {
            while((n=read(connfd,buf,5))>0)
                write(STDOUT_FILENO,buf,n);
        }
    }

    
    
    return 0;

}


測試結果

從截圖看,只用呼叫一次epoll_wait()函式就可以讀取所有資料。
客戶端
在這裡插入圖片描述
伺服器端
在這裡插入圖片描述

相關文章