epoll 非阻塞IO 边沿触发模式

与之前的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);
    //fcntl(connfd,F_SETFL,O_NONBLOCK);另一种写法

    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;
}

客户端

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<ctype.h>
#include<arpa/inet.h>
#include<string.h>

#define SERV_IP "127.0.0.1"
#define SERV_PORT  6666
#define MAXLINE 10
int main()
{
    int cfd,sfd;
    struct sockaddr_in serv_addr;
    socklen_t serv_addr_len;
    char buf[MAXLINE],clien_ip[BUFSIZ];
    int n,i;
    char ch='a';
    int res;

    memset(&serv_addr,0,sizeof(serv_addr));
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_port=htons(SERV_PORT);//转换为网络字节序
    //serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);//INADDR_ANY可以自行寻找ip
    inet_pton(AF_INET,SERV_IP,&serv_addr.sin_addr.s_addr);

    cfd=socket(AF_INET,SOCK_STREAM,0);//创建本地套接字

    // bind(lfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));//绑定,客户端隐式绑定

    res=connect(cfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr) );
    if(res==-1)
    {
        perror("connect error");
        exit(1);
    }
    while(1)    //循环发送10个字符
    {
        printf("**************************************\n");
        //aaaa\n
        for(i=0;i<MAXLINE/2;i++)
            buf[i]=ch;
        buf[i-1]='\n';
        ch++;
        // bbbb\n
        for(;i<MAXLINE;i++)
            buf[i]=ch;
        buf[i-1]='\n';
        ch++;
        printf("client write--\n");
        write(STDOUT_FILENO,buf,sizeof(buf));
        write(cfd,buf,sizeof(buf));
        sleep(5);
    }   
    close(cfd);
    return 0;
}

测试结果

从截图看,因为read函数非阻塞读取,每次客户端发送数据,只调用一次epoll_wait()函数就可以读取所有数据。
客户端:
在这里插入图片描述
服务器端
在这里插入图片描述

©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页