问题是:当epoll触发可读时,万一数据没有从缓冲区读完,下次有新的数据过来,还会触发么?

答案是: 会触发

et模式触发是这样说的:由不可读变为可读时触发读事件,由不可写变为可写触发写事件。 有点宏观

然后我们具体看代码:

#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/epoll.h>
#include<poll.h>
#include<iostream>
#include<string.h>
#include<vector>
#include<errno.h>
#include<iostream>
using namespace std;

int setSocketNonblocking(int fd)
{
    //将监听socker设置为非阻塞的
    int oldSocketFlag = fcntl(fd, F_GETFL, 0);
    int newSocketFlag = oldSocketFlag | O_NONBLOCK;
    if(fcntl(fd, F_SETFL, newSocketFlag)==-1) 
    {
        close(fd);
        cout << "set listenfd to nonblock error" << endl;
        return -1;
    }
    return oldSocketFlag;
}


int main()
{
    //创建一个监听socket
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if(listenfd == -1)
    {
        cout << "create listen socket error" << endl;
        return -1;
    }

    setSocketNonblocking(listenfd);

    //初始化服务器地址

    struct sockaddr_in bindaddr;
    bindaddr.sin_family = AF_INET;
    bindaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    bindaddr.sin_port = htons(3000);

    if(bind(listenfd, (struct sockaddr*)&bindaddr, sizeof(bindaddr))==-1)
    {
        cout << "bind listen socker error." << endl;
        close(listenfd);
        return -1;
    }

    //启动监听
    if(listen(listenfd, SOMAXCONN)==-1)
    {
        cout << "listen error." << endl;
        close(listenfd);
        return -1;
    }

    //复用地址和端口号
    int on = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEPORT, (char*)&on, sizeof(on));


    //创建epollfd
    int epollfd = epoll_create(1);
    if(epollfd == -1)
    {
        cout << "create epollfd error." << endl;
        close(listenfd);
        return -1;
    }

    epoll_event listen_fd_event;
    listen_fd_event.data.fd = listenfd;
    listen_fd_event.events = EPOLLIN;
    listen_fd_event.events |= EPOLLET;

    //将监听sokcet绑定到epollfd上去
    if(epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd,&listen_fd_event)==-1)
    {
        cout << "epoll_ctl error" << endl;
        close(listenfd);
        return -1;
    }

    int n;
    while(true)
    {
        epoll_event epoll_events[1024];
        n = epoll_wait(epollfd, epoll_events, 1024, 1000);
        if(n<0)
        {
            //被信号中断
            if(errno == EINTR) continue;
            //出错,退出
            break;
        }
        else if(n==0)
        {
            //超时,继续
            continue;
        }
        for(size_t i = 0; i<n;i++)
        {
            //事件可读
            if(epoll_events[i].events & EPOLLIN)
            {
                if(epoll_events[i].data.fd == listenfd)
                {
                    //侦听socket,接受新连接
                    struct sockaddr_in clientaddr;
                    socklen_t clientaddrlen = sizeof(clientaddr);
                    int clientfd = accept(listenfd, (struct sockaddr*)&clientaddr, &clientaddrlen);
                    if(clientfd != -1)
                    {
                        int oldSocketFlag = fcntl(clientfd, F_GETFL,0);
                        int newSocketFlag = oldSocketFlag | O_NONBLOCK;
                        if(fcntl(clientfd, F_SETFD, newSocketFlag)==-1)
                        {
                            close(clientfd);
                            cout << "set clientfd to nonblocking error." << endl;
                        }
                        else 
                        {
                            epoll_event client_fd_event;
                            client_fd_event.data.fd = clientfd;
                            client_fd_event.events = EPOLLIN;
                            client_fd_event.events |= EPOLLET; //设置为边缘出发
                            if(epoll_ctl(epollfd, EPOLL_CTL_ADD, clientfd, &client_fd_event)!=-1)
                            {
                                cout << "new client accepted,clientfd: " << clientfd << endl;
                            }
                            else 
                            {
                                cout << "add client fd to epollfd error" << endl;
                                close(clientfd);
                            }
                        }
                    }
                }
                else
                {
                    //普通clientfd
                    char ch;
                    int m = recv(epoll_events[i].data.fd, &ch, 1, 0);
                    if(m==0)
                    {
                        //对端关闭了连接,从epollfd上移除clientfd
                        if(epoll_ctl(epollfd, EPOLL_CTL_DEL, epoll_events[i].data.fd,NULL)!=-1)
                        {
                            cout << "client disconnected,clientfd:" <<epoll_events[i].data.fd << endl;
                        }
                        close(epoll_events[i].data.fd);
                    }
                    else if(m<0)
                    {
                        //出错
                        if(errno!= EWOULDBLOCK && errno !=EINTR)
                        {
                            if(epoll_ctl(epollfd, EPOLL_CTL_DEL, epoll_events[i].data.fd,NULL)!=-1)
                            {
                                cout << "client disconnected,clientfd:" <<epoll_events[i].data.fd << endl;
                            }
                            close(epoll_events[i].data.fd);
                        }
                        break;
                    }
                    else 
                    {
                        //正常收到数据
                        cout << "recv from client:" << epoll_events[i].data.fd << " " << ch << endl; 
                    }
                }
            }
            else if(epoll_events[i].events & POLLERR)
            {
                        // TODO 暂不处理
            }      
        }
    }
    close(listenfd);
    return 0;
}

然后我们去测试: 注意代码里每次只读取一个字节。

char ch;
                    int m = recv(epoll_events[i].data.fd,&ch,1,0);

看结果

在这里插入图片描述)在这里插入图片描述)
最后看到是会继续触发的。

当然我们一般是使用while循环一直读写的,直到返回小于0的数才退出。

这里有个错误,就是

 //复用地址和端口号
    int on = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEPORT, (char*)&on, sizeof(on));

必须在bind之前调用。