【Linux網路程式設計-7】epoll邊沿觸發

徘徊彼岸花發表於2024-07-18

非阻塞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
}

相關文章