通知:epoll是仅限于在Linux上的函数->其正常流程可参考其他的,这里不多赘述,我主要想说的是非阻塞的套接字边缘模式多线程epoll(很绕,我懂)。。。
先上源代码

#include <iostream>
#include <string.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
int main()
{
	//创建监听套接字
	int lfd=socket(AF_INET,SOCK_STREAM,0);
	if(-1==lfd){
		std::cerr<<"socket error..."<<std::endl;
		exit(1);
	}
	struct sockaddr_in serv_addr;
	memset(&serv_addr,0,sizeof(serv_addr));
	serv_addr.sin_family=AF_INET;
	serv_addr.sin_port=htons(9999);
	serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
	//绑定端口
	int ret=bind(lfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr));
	if(-1==ret){
		std::cerr<<"bind error..."<<std::endl;
		close(lfd);
		exit(1);
	}

//监听
int lis_;
lis_=listen(lfd,64);
if(-1==lis_){
    std::cerr<<"lsiten error..."<<std::endl;
    close(lfd);
    exit(1);
}

//创建一个epoll模型
int epfd=epoll_create(100);
if(-1==epfd){
    std::cerr<<"epoll_create error..."<<std::endl;
    close(lfd);
    exit(1);
}
//往epoll实例中添加需要检测的节点,现在只有监听的文件描述符
struct epoll_event ev;
ev.events=EPOLLIN;  //读取 读缓冲区是否有数据
ev.data.fd=lfd;
int temp1=epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&ev);
if(-1==temp1){
    std::cerr<<"epoll_ctl of lfd error..."<<std::endl;
    close(lfd);
    exit(1);
}

struct epoll_event evs[1024];
int size=sizeof(evs)/sizeof(struct epoll_event);
//持续检测
while(1){
    //调用一次,检测一次
    int num=epoll_wait(epfd,evs,size,-1);
    for(int i=0;i<num;++i){
        int curfd=evs[i].data.fd;   //取出当前的文件描述符
        if(curfd==lfd){ //检测到监听描述符的缓冲区有变化
            int cfd=accept(curfd,nullptr,nullptr);
            //为了配合边缘模式循环读取数据而不阻塞(防止recv空数据阻塞)
            int flag=fcntl(curfd,F_GETFL);
            flag|=O_NONBLOCK;//非阻塞
            fcntl(curfd,F_SETFL,flag);
            //正常操作
            //将得到的新的文件描述符加入epoll模型中,下一轮调用即可检测了
            ev.events=EPOLLIN | EPOLLET;  //读取读缓冲区 | 边缘模式
            ev.data.fd=cfd;
            temp1=epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&ev);
            if(-1==temp1){
                std::cerr<<"epoll_ctl of "<<i<<" times error..."<<std::endl;
                continue;
            }
        }
        else{
            //循环读取数据
            //处理通信的文件描述符
            char buf[1024];
            while(1){
                memset(buf,0,sizeof(buf));
                int len=recv(curfd,buf,sizeof(buf),0);
                if(0==len){
                    std::cout<<"客户端已经断开连接..."<<std::endl;
                    epoll_ctl(epfd,EPOLL_CTL_DEL,curfd,NULL);
                    close(curfd);
                }
                else if(len>0){
                    std::cout<<"客户端says:"<<buf<<std::endl;
                    send(curfd,buf,len,0);
                }
                else{
                    if(errno==EAGAIN){
                        std::cout<<"数据已经读取完成,缓冲区数据已经为空..."<<std::endl;
                        break;
                    }
                    std::cerr<<"curd:"<<curfd<<" "<<"recv error..."<<std::endl;
                    continue;
                }
            }
        }

    }

}
	close(lfd);
}

注意:这里的epoll搭配多线程应该可以实现百万并发,哪里采用多线程就不赘述了。
注意点①:非阻塞的套接字:

int cfd=accept(curfd,nullptr,nullptr);
//为了配合边缘模式循环读取数据而不阻塞(防止recv空数据阻塞)
int flag=fcntl(curfd,F_GETFL);
flag|=O_NONBLOCK;//非阻塞
fcntl(curfd,F_SETFL,flag);

注意点②:边缘模式(啥是边缘模式,啥是水平模式,建议自己补充)

ev.events=EPOLLIN | EPOLLET;  //读取读缓冲区 | 边缘模式

我没贴多线程的(其实是懒得写了。。。)
over!!!