通知: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!!!