【Linux网络编程-7】epoll边沿触发

非阻塞recv
EAGAIN、EWOULDBLOCK错误码值11

返回值 含义
>0 接收字节数
0 接收FIN包,连接被对端断开
-1 (errno==EAGAIN||EWOULDBLOCK)表示还有数据未读。反之,则表示发生了错误。
//epollServer.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <errno.h>
#include <vector>
#include <time.h>
#include <sys/epoll.h>
#include <fcntl.h>

#define MAXEVENTS 100

int InitServer(const unsigned int port);

int setnoblocking(int fd);

bool addfd(int epollfd, int fd, struct epoll_event* ev);

bool addfdET(int epollfd, int fd, struct epoll_event* ev);

int main(int argc,char** argv)
{
    int listenfd=InitServer(atoi(argv[1]));
    printf("listenfd=%d\n",listenfd);
    if(listenfd==-1)
    {
        printf("InitServer failed..\n");
        return -1;
    }

    //创建epoll句柄
    int epollfd=epoll_create(1);
    if(epollfd<0)   printf("epoll_create failed..\n");

    //epoll_event封装listenfd和监视类型为有数据可读(EPOLLIN)
    struct epoll_event ev;
    addfd(epollfd, listenfd, &ev);

    while(true)
    {
        struct epoll_event events[MAXEVENTS];	//epoll_wait返回时保存发生的事件
        int infds=epoll_wait(epollfd,events,MAXEVENTS,-1);
        if(infds<0) 
        {
            printf("epoll_wait failed..\n");
            break;
        }
        if(infds==0)
        {
            printf("timeout..\n");
            break;
        }
        
        //发生的事件再events中从index=0依次保存
        for(int i=0;i<infds;i++) 
        {
            //listenfd上有数据可读事件发生
            if((events[i].data.fd==listenfd) && (events[i].events&EPOLLIN))
            {
                struct sockaddr_in client_addr;
                socklen_t client_addr_size=sizeof(client_addr);
                int clientfd = accept(listenfd, (struct sockaddr*)&client_addr,&client_addr_size);
                if(clientfd<0)
                {
                    printf("accept failed..\n");
                    continue;
                }

                //连接上的客户端添加进监视
                //边缘触发
                addfdET(epollfd,clientfd,&ev);	
                continue;
            }
            else if(events[i].events&EPOLLIN)	//有数据可读事件
            {
                //边缘触发:这段代码不会被epoll_wait重复触发,所以需要手动循环读取直到数据读完。因为循环recv,不能设为阻塞
                while(true)
                {
                    char buf[1024];
                    memset(buf,0,sizeof(buf));
                    int isize = recv(events[i].data.fd,buf,sizeof(buf),0);
                    
                    if(isize==0)	//连接被对端关闭
                	{
                        printf("对端关闭连接\n");
                    	memset(&ev,0,sizeof(struct epoll_event));
                    	ev.data.fd=events[i].data.fd;
                    	ev.events=EPOLLIN;
                    	epoll_ctl(epollfd,EPOLL_CTL_DEL,events[i].data.fd,&ev);
                    	close(events[i].data.fd);
                    	printf("%ld client(%d) disconneted..\n",time(0),events[i].data.fd);
                    	break;
                	}
                    else if(isize==-1) //数据未读完或发生错误
                    {
                        if((errno==EAGAIN) || (errno==EWOULDBLOCK))	//数据未读完
                        {
                            errno=0;	//全局变量errno被置为EAGAIN后,清空
                            continue;	//继续循环执行recv
                        }
                        else
                        {
                            //错误,终止recv循环
                            break;
                        }

                    }
                    
                    printf("client(%d)recv msg:%s,size=%d\n",events[i].data.fd,buf,isize);	//recv返回值>0

                	//判断sockfd是否可写
                    fd_set tmpfd;
                    FD_ZERO(&tmpfd);
                    FD_SET(events[i].data.fd,&tmpfd);
                    if(select(events[i].data.fd+1,0,&tmpfd,0,0)<0)
                    {
                        printf("没有可写数据的fd\n");
                        break;
                    }
                    if(write(events[i].data.fd,buf,strlen(buf))<0)	//发送
                    {
                        printf("write failed..\n");
                        close(events[i].data.fd);
                        break;
                    }

                }
            }
            
       }
    }
    return 0;
}

int InitServer(const unsigned int port)
{
    int sockfd=-1;
    sockfd = socket(PF_INET, SOCK_STREAM, 0);
    if(sockfd == -1) return -1;

    int opt=1;
    unsigned int len=sizeof(opt);
    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, len);
	
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family=AF_INET;
    server_addr.sin_addr.s_addr=htonl(INADDR_ANY);	
    server_addr.sin_port=htons(port);

    if(bind(sockfd, (struct sockaddr*) &server_addr, sizeof(server_addr)) == -1)
    {
        close(sockfd);
        sockfd=-1;
        return -1;
    }
    
    if(listen(sockfd, 5) == -1)
    {
        close(sockfd);
        sockfd=-1;
        return -1;
    }

    return sockfd;
}

int setnoblocking(int fd) 
{
    int flags = fcntl(fd, F_GETFL, 0);
    if (flags == -1) 
    {
        return -1;
 	}

    flags |= O_NONBLOCK;
    if (fcntl(fd, F_SETFL, flags) == -1) 
    {
        return -1;
    }

    return 0;
}

bool addfd(int epollfd, int fd, struct epoll_event* ev)
{
    memset(ev,0,sizeof(struct epoll_event));
    ev->data.fd=fd;
    ev->events=EPOLLIN;
    //setnoblocking(fd);
    return 0==epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,ev);	//添加到epollfd
}

bool addfdET(int epollfd, int fd, struct epoll_event* ev)
{
    memset(ev,0,sizeof(struct epoll_event));
    ev->data.fd=fd;
    ev->events=EPOLLIN|EPOLLET;
    setnoblocking(fd);
    return 0==epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,ev);	//添加到epollfd
}

posted @ 2024-07-18 18:38  仰望星河Leon  阅读(82)  评论(0)    收藏  举报