游戏电视苹果数码历史美丽
投稿投诉
美丽时装
彩妆资讯
历史明星
乐活安卓
数码常识
驾车健康
苹果问答
网络发型
电视车载
室内电影
游戏科学
音乐整形

万字总结IO多路复用技术

  IO多路复用概述
  IO多路复用技术是为了解决进程或线程阻塞到某个IO系统调用而出现的技术,使进程不阻塞于某个特定的IO系统调用。
  在IO多路复用技术描述前,先讲解下同步,异步,阻塞,非阻塞的概念。网络IO模型
  linux网络IO中涉及到的模型如下:
  (1)阻塞式IO
  (2)非阻塞式IO
  (3)IO多路复用
  (4)信号驱动IO
  (5)异步IO
  今天不谈信号驱动IO,略过。。同步异步
  在学习IO模型的时候,我们必须明确一个概念,处理IO的时候,阻塞和非阻塞都是同步的的IO。
  只有使用了特殊的API才是异步IO,例如Linux网络中的AIO。
  再看下POSIX对同步和异步这两个术语的定义:同步IO操作:导致请求进程阻塞,直到IO操作完成;异步IO操作:不导致请求进程阻塞;
  通俗的理解就是同步和异步同步:当执行系统调用read时,需要用户等待内核完成从内核缓冲区到用户缓冲区的数据拷贝。异步:当执行异步IO操作例如aioread时,用户不需要等待,只需要接收内核完成操作的通知,由内核来完成数据的读取。阻塞非阻塞
  在知晓阻塞和非阻塞都是同步IO后,阻塞和非阻塞就很好理解了
  阻塞IO:由系统调用read,导致线程一直等待数据返回。
  非阻塞IO:系统调用read后立即返回一个状态,当数据达到内核缓冲区之前都是非阻塞的,即返回一个系统调用状态。
  ps:闪客的动图做得非常的形象,上述gif动图来源低并发编程IO多路复用
  IO多路复用是一种同步IO模型,实现一个线程可以监视多个文件句柄;select
  select是操作系统提供的系统调用函数,select()用来等待文件描述词(普通文件、终端、伪终端、管道、FIFO、套接字及其他类型的字符型)状态的改变。是一个轮循函数,循环询问文件节点,可设置超时时间,超时时间到了就跳过代码继续往下执行。
  通过select,我们可以把一个文件描述符的数组发给操作系统,让操作系统去遍历,确定哪个文件描述符可以读写,然后告诉我们去处理:
  头文件includesysselect。hincludesystime。hincludesystypes。hincludeunistd。hselect调用
  拥塞函数,拥塞等待文件描述符事件的到来intselect(intmaxfdp,fdsetreadset,fdsetwriteset,fdsetexceptset,structtimevaltimeout);
  参数说明:
  maxfdp:被监听的文件描述符的最大值,它比所有文件描述符集合中的文件描述符的最大值大1,因为文件描述符是从0开始计数的;
  readfds、writefds、exceptset:分别指向可读、可写和异常等事件对应的描述符集合。
  timeout:用于设置select函数的超时时间,即告诉内核select等待多长时间之后就放弃等待。timeoutNULL表示等待无限长的时间,timeout0,select立即返回timeval结构体structtimeval{longtvsec;秒longtvusec;微秒};select置位intFDZERO(intfd,fdsetfdset);一个fdset类型变量的所有位都设为0intFDCLR(intfd,fdsetfdset);清除某个位时可以使用intFDSET(intfd,fdsetfdset);设置变量的某个位置位intFDISSET(intfd,fdsetfdset);测试某个位是否被置位
  当声明了一个文件描述符集后,必须用FDZERO将所有位置零
  调用select函数,拥塞等待文件描述符事件的到来;如果超过设定的时间,则不再等待,而是继续往下执行
  select返回后,用FDISSET测试给定位是否置位:if(FDISSET(fd,rset){。。。dosomething}fdset结构体
  fdset其实这是一个数组的宏定义,实际上是一long类型的数组,每一个数组元素都能与一打开的文件句柄(socket、文件、管道、设备等)建立联系,建立联系的工作由程序员完成,当调用select()时,由内核根据IO状态修改fdset的内容,由此来通知执行了select()的进程哪个句柄可读。select使用
  整个select流程图如下:
  Demo1:select示例Serverincludestdio。hincludestdlib。hincludeerrno。hincludestring。hincludesystypes。hincludenetinetin。hincludesyssocket。hincludesyswait。hincludeunistd。hincludeincludesystime。hincludesystypes。hdefineMAXBUF1024defineLISTENNUM2intmain(intargc,charargv){intdefaultport8000;intoptch0;while((optchgetopt(argc,argv,s:p:))!1){switch(optch){casep:defaultportatoi(optarg);printf(port:s,optarg);break;case?:printf(Unknownoption:c,(char)optopt);break;default:break;}}intsockfd,newfd;socklentlen;structsockaddrinmyaddr,theiraddr;charbuf〔MAXBUF1〕;fdsetrfds;selectstructtimevaltv;超时时间intretval,maxfd1;select返回值select监听句柄的最大数量if((sockfdsocket(PFINET,SOCKSTREAM,0))1){perror(socket);exit(EXITFAILURE);}bzero(myaddr,sizeof(myaddr));myaddr。sinfamilyPFINET;myaddr。sinporthtons(defaultport);myaddr。sinaddr。saddrINADDRANY;if(bind(sockfd,(structsockaddr)myaddr,sizeof(structsockaddr))1){perror(bind);exit(EXITFAILURE);}if(listen(sockfd,LISTENNUM)1){perror(listen);exit(EXITFAILURE);}数据处理while(1){printf(waitfornewconnectport:d,defaultport);lensizeof(structsockaddr);if((newfdaccept(sockfd,(structsockaddr)theiraddr,len))1){perror(accept);exit(errno);}elseprintf(server:gotconnectionfroms,portd,socketd,inetntoa(theiraddr。sinaddr),ntohs(theiraddr。sinport),newfd);while(1){FDZERO(rfds);FDSET(0,rfds);FDSET(newfd,rfds);maxfdnewfd;tv。tvsec1;tv。tvusec0;retvalselect(maxfd1,rfds,NULL,NULL,tv);if(retval1){perror(select);exit(EXITFAILURE);}elseif(retval0){continue;}else{标准输入if(FDISSET(0,rfds)){bzero(buf,MAXBUF1);fgets(buf,MAXBUF,stdin);if(!strncasecmp(buf,quit,4)){printf(iwillquit!);break;}lensend(newfd,buf,strlen(buf)1,0);if(len0)printf(sendsuccessful,dbytesend。。,len);else{printf(sendfailure!);break;}}if(FDISSET(newfd,rfds)){bzero(buf,MAXBUF1);lenrecv(newfd,buf,MAXBUF,0);if(len0)printf(recvsuccess:s,dbyterecv。。,buf,len);else{if(len0)printf(recvfailure);else{printf(theclientclose,quit);break;}}}}}close(newfd);printf(needotheconnecdt(noquit));fflush(stdout);bzero(buf,MAXBUF1);fgets(buf,MAXBUF,stdin);if(!strncasecmp(buf,no,2)){printf(quit!);break;}}close(sockfd);return0;}
  makefile:TARGETserverSRC(wildcard。cpp。c)OBJ(patsubst。cpp。c,。o,(SRC))DEFSCFLAGSgCCgLIBSlpthread(TARGET):(OBJ)(CC)(CFLAGS)(DEFS)o(LIBS)。PHONY:clean:rmrf。o(TARGET)ubuntuVM165ubuntu:learnbaseIO复用selectmakeggoserverselect。clpthreadubuntuVM165ubuntu:learnbaseIO复用select。serverwaitfornewconnectport:8000clientincludestdio。hincludestring。hincludeerrno。hincludesyssocket。hincluderesolv。hincludestdlib。hincludenetinetin。hincludeincludeunistd。hincludesystime。hincludesystypes。hdefineMAXBUF1024intmain(intargc,charargv){intsockfd,len;structsockaddrindest;charbuffer〔MAXBUF1〕;fdsetrfds;structtimevaltv;intretval,maxfd1;intoptch,ret1;constcharserveraddr;intdefaultport8000;判断是否为合法输入必须传入一个参数:服务器Ipif(argc3){printf(usage:tcpcliIPaddress);return0;}while((optchgetopt(argc,argv,s:p:))!1){switch(optch){cases:serveraddroptarg;break;casep:defaultportatoi(optarg);printf(port:s,optarg);break;case?:printf(Unknownoption:c,(char)optopt);break;default:break;}}if((sockfdsocket(AFINET,SOCKSTREAM,0))0){perror(Socket);exit(EXITFAILURE);}bzero(dest,sizeof(dest));dest。sinfamilyAFINET;dest。sinporthtons(defaultport);if(inetaton(serveraddr,(structinaddr)dest。sinaddr。saddr)0){perror(serveraddr);exit(EXITFAILURE);}if(connect(sockfd,(structsockaddr)dest,sizeof(dest))!0){perror(Connect);exit(EXITFAILURE);}printf(getreadymessagechat:);while(1){FDZERO(rfds);FDSET(0,rfds);FDSET(sockfd,rfds);maxfdsockfd;tv。tvsec1;tv。tvusec0;retvalselect(maxfd1,rfds,NULL,NULL,tv);if(retval1){printf(selects,strerror(errno));break;}elseif(retval0)continue;else{if(FDISSET(sockfd,rfds)){bzero(buffer,MAXBUF1);lenrecv(sockfd,buffer,MAXBUF,0);if(len0)printf(recvmessage:s,dbyterecv。。,buffer,len);else{if(len0)printf(messagerecvfailure);else{printf(serverclose,quit);break;}}}if(FDISSET(0,rfds)){bzero(buffer,MAXBUF1);fgets(buffer,MAXBUF,stdin);if(!strncasecmp(buffer,quit,4)){printf(iwillquit);break;}lensend(sockfd,buffer,strlen(buffer)1,0);if(len0){printf(messagesendfailure);break;}elseprintf(sendsuccess,dbytesend。。,len);}}}close(sockfd);return0;}TARGETserverSRC(wildcard。cpp。c)OBJ(patsubst。cpp。c,。o,(SRC))DEFSCFLAGSgCCgLIBSlpthread(TARGET):(OBJ)(CC)(CFLAGS)(DEFS)o(LIBS)。PHONY:clean:rmrf。o(TARGET)ubuntuVM165ubuntu:learnbaseIO复用selectclientmakeggoclientclient。clpthreadubuntuVM165ubuntu:learnbaseIO复用selectclient。clients0。0。0。0getreadymessagechat:简易聊天室select版本serverincludestdio。hincludesystypes。hincludestdlib。hincludesyssocket。hincludesysselect。hincludenetinetin。hincludeincludeunistd。hincludestring。hdefineBACKLOG5监听队列里允许等待的最大值defineMAXCONNECT20intfds〔MAXCONNECT〕;用来存放需要处理的IO事件intlistensock1;intcreatsock(intport){intsocksocket(AFINET,SOCKSTREAM,0);if(sock0){perror(creatsockerror);exit(1);}structsockaddrinlocal;local。sinfamilyAFINET;local。sinporthtons(port);local。sinaddr。saddrINADDRANY;inetaddr(0。0。0。0)设置允许socket立即重用setsockopt(sock,SOLSOCKET,SOREUSEADDR,(constchar)sock,sizeof(sock));if(bind(sock,(structsockaddr)local,sizeof(local))0){perror(bind);exit(2);}if(listen(sock,BACKLOG)0){perror(listen);exit(4);}returnsock;}intacceptsock(){structsockaddrinclient;socklentlensizeof(client);intacceptsockaccept(listensock,(structsockaddr)client,len);if(acceptsock0){perror(accept);exit(5);}printf(connectbyaclient,ip:sport:d,inetntoa(client。sinaddr),ntohs(client。sinport));sizeti0;for(;iMAXCONNECT;i)将新接受的描述符存入集合中{if(fds〔i〕1){fds〔i〕acceptsock;break;}}if(iMAXCONNECT){printf(acceptisupperlimit。。);close(acceptsock);}}intgroupChat(intsockFd,voidpBuf,intiSize){for(intindex0;indexMAXCONNECT;index){if(fds〔index〕sockFdfds〔index〕listensock){continue;}if(fds〔index〕!1){printf(writefd:d。。socketFd:d,fds〔index〕,sockFd);write(fds〔index〕,pBuf,iSize);}}}inthandleread(intsocketFd){intsocketsocketFd;charbuf〔1024〕;memset(buf,,sizeof(buf));ssizetsizeread(socket,buf,sizeof(buf)1);if(size0){perror(read);exit(6);}elseif(size0){printf(clientclose。。);close(socket);socketFd1;}else{printf(clientsay:s,buf);groupChat(socket,buf,size);}}intmain(intargc,charargv〔〕){intdefaultport8000;intoptch0;while((optchgetopt(argc,argv,s:p:))!1){switch(optch){casep:defaultportatoi(optarg);printf(port:s,optarg);break;case?:printf(Unknownoption:c,(char)optopt);break;default:break;}}listensockcreatsock(defaultport);sizetfdsnumsizeof(fds)sizeof(fds〔0〕);sizeti0;for(;ifdsnum;i){fds〔i〕1;}intmaxfdlistensock;fds〔0〕listensock;fdsetrset;while(1){FDZERO(rset);FDSET(listensock,rset);structtimevaltimeout{10,0};sizeti0;for(;ifdsnum;i){if(fds〔i〕0){FDSET(fds〔i〕,rset);if(maxfdfds〔i〕){maxfdfds〔i〕;}}}switch(select(maxfd1,rset,NULL,NULL,timeout)){case1:perror(select);break;case0:printf(timeout。。);break;default:{sizeti0;for(;ifdsnum;i){连接请求当为listensocket事件就绪的时候,就表明有新的连接请求if(FDISSET(fds〔i〕,rset)fds〔i〕listensock){acceptsock();}普通请求elseif(FDISSET(fds〔i〕,rset)(fds〔i〕0)){handleread(fds〔i〕);}else{}}}break;}}return0;}
  makfeileTARGETserverSRC(wildcard。cpp。c)OBJ(patsubst。cpp。c,。o,(SRC))DEFSCFLAGSgCCgLIBSlpthread(TARGET):(OBJ)(CC)(CFLAGS)(DEFS)o(LIBS)。PHONY:clean:rmrf。o(TARGET)ubuntuVM165ubuntu:learnbaseIO复用selectmakeggoserverselect。clpthreadubuntuVM165ubuntu:learnbaseIO复用select。serverconnectbyaclient,ip:127。0。0。1port:42964clientincludestdio。hincludestring。hincludeerrno。hincludesyssocket。hincluderesolv。hincludestdlib。hincludenetinetin。hincludeincludeunistd。hincludesystime。hincludesystypes。hdefineMAXBUF1024defineMAXNAME64intmain(intargc,charargv){intsockfd,len;structsockaddrindest;charbuffer〔MAXBUF1〕;fdsetrfds;structtimevaltv;intretval,maxfd1;intoptch,ret1;constcharserveraddr;intdefaultport8000;charclientName佚名;判断是否为合法输入必须传入一个参数:服务器Ipif(argc3){printf(usage:tcpcliIPaddress);return0;}while((optchgetopt(argc,argv,s:p:n:))!1){switch(optch){cases:serveraddroptarg;break;casep:defaultportatoi(optarg);printf(port:s,optarg);break;casen:clientNameoptarg;printf(clientName:s,optarg);break;case?:printf(Unknownoption:c,(char)optopt);break;default:break;}}if((sockfdsocket(AFINET,SOCKSTREAM,0))0){perror(Socket);exit(EXITFAILURE);}bzero(dest,sizeof(dest));dest。sinfamilyAFINET;dest。sinporthtons(defaultport);if(inetaton(serveraddr,(structinaddr)dest。sinaddr。saddr)0){perror(serveraddr);exit(EXITFAILURE);}if(connect(sockfd,(structsockaddr)dest,sizeof(dest))!0){perror(Connect);exit(EXITFAILURE);}printf(getreadymessagechat:);while(1){FDZERO(rfds);FDSET(0,rfds);FDSET(sockfd,rfds);maxfdsockfd;tv。tvsec1;tv。tvusec0;retvalselect(maxfd1,rfds,NULL,NULL,tv);if(retval1){printf(selects,strerror(errno));break;}elseif(retval0)continue;else{if(FDISSET(sockfd,rfds)){bzero(buffer,MAXBUF1);lenrecv(sockfd,buffer,MAXBUF,0);if(len0)printf(recvbyted,s,len,buffer);else{if(len0)printf(messagerecvfailure);else{printf(serverclose,quit);break;}}}if(FDISSET(0,rfds)){charnamemsg〔MAXNAMEMAXBUF〕;bzero(buffer,MAXBUF1);fgets(buffer,MAXBUF,stdin);if(!strncasecmp(buffer,quit,4)){printf(iwillquit);break;}sprintf(namemsg,〔s〕:s,clientName,buffer);lensend(sockfd,namemsg,strlen(namemsg)1,0);if(len0){printf(messagesendfailure);break;}elseprintf(sendsuccess,dbytesend。。,len);}}}close(sockfd);return0;}
  makefileTARGETserverSRC(wildcard。cpp。c)OBJ(patsubst。cpp。c,。o,(SRC))DEFSCFLAGSgCCgLIBSlpthread(TARGET):(OBJ)(CC)(CFLAGS)(DEFS)o(LIBS)。PHONY:clean:rmrf。o(TARGET)ubuntuVM165ubuntu:learnbaseIO复用selectclient。clients0。0。0。0n梦凡clientName:梦凡getreadymessagechat:poll调用
  Poll就是监控文件是否可读的一种机制,作用与select一样。includepoll。hintpoll(structpollfdfds〔〕,nfdstnfds,inttimeout);
  参数说明structpollfd
  fds:是一个structpollfd结构类型的数组,列出了我们需要poll()检查的文件描述符typedefstructpollfd{intfd;需要被检测或选择的文件描述符shortevents;对文件描述符fd上感兴趣的事件shortrevents;文件描述符fd上当前实际发生的事件}pollfdt;
  events:想要监听的事件
  revents:实际上发生的事件POLLINPOLLOUTPOLLPRIPOLLRDHUBPOLLHUPPOLLERRnfds
  指定了fds中元素的个数,nfdst为无符号整形timeout
  决定阻塞行为,一般如下:1:一直阻塞到fds数组中有一个达到就绪态或者捕获到一个信号0:不会阻塞,立即返回0:阻塞时间返回值0:数组fds中准备好读、写或出错状态的那些socket描述符的总数量;0:数组fds中没有任何socket描述符准备好读、写,或出错;此时poll超时1:poll函数调用失败poll使用includestdio。hincludepoll。hincludestring。hintmain(){inttimeout0;charbuf〔1024〕;structpollfdfdpoll〔1〕;设置只有一个事件while(1){fdpoll〔0〕。fd0;fdpoll〔0〕。eventsPOLLIN;fdpoll〔0〕。revents0;memset(buf,,sizeof(buf));switch(poll(fdpoll,1,1)){case0:perror(timeout!);break;case1:perror(poll);break;default:{if(fdpoll〔0〕。reventsPOLLIN){gets(buf);printf(buf:s,buf);}}break;}}return0;}
  makefiletcppoll:tcppoll。cgcco。PHONY:cleanclean:rmftcppollepoll调用
  epoll没有对描述符数目的限制,它所支持的文件描述符上限是整个系统最大可以打开的文件数目,例如,在1GB内存的机器上,这个限制大概为10万左右。
  epoll只有epollcreate、epollctl和epollwait这三个系统调用。
  第一步,创建一个epoll句柄
  第二步,向内核添加、修改或删除要监控的文件描述符。
  第三步,发起了select()调用
  其定义如下:includesysepoll。hintepollcreate(intsize);intepollctl(intepfd,intop,intfd,structepolleventevent);intepollwait(intepfd,structepolleventevents,intmaxevents,inttimeout);epollcreateincludesysepoll。hintepollcreate(intsize);
  调用epollcreate方法创建一个epoll的句柄,使用完epoll后使用close函数进行关闭epollctlincludesysepoll。hintepollctl(intepfd第一个参数epfd:epollcreate函数的返回值。,intop第二个参数events:表示动作类型。有三个宏来表示,intfd第三个参数fd:需要监听的fd。,structepolleventevent);第四个参数event:告诉内核需要监听什么事件。
  op:EPOLLCTLADD:注册新的fd到epfd中;EPOLLCTLMOD:修改已经注册的fd的监听事件;EPOLLCTLDEL:从epfd中删除一个fd。
  fd:需要注册监视对象文件描述符structepollevent感兴趣的事件和被触发的事件structepollevent{uint32tevents;Epolleventsepolldatatdata;Userdatavariable};保存触发事件的某个文件描述符相关的数据typedefunionepolldata{voidptr;intfd;uint32tu32;uint64tu64;}epolldatat;EpollEvents:
  EPOLLIN:表示对应的文件描述符可读(包括对端Socket);
  EPOLLOUT:表示对应的文件描述符可写;
  EPOLLPRI:表示对应的文件描述符有紧急数据可读(带外数据);
  EPOLLERR:表示对应的文件描述符发生错误;
  EPOLLHUP:表示对应的文件描述符被挂断;
  EPOLLET:将EPOLL设为边缘触发(EdgeTriggered),这是相对于水平触发(LevelTriggered)而言的。
  EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket,需要再次添加
  例如:structepolleventepev;intacceptsockaccept(listensock,(structsockaddr)remote,len);epev。eventsEPOLLINEPOLLET;epev。data。fdacceptsock;epollctl(epollfd,EPOLLCTLADD,acceptsock,epev)epollwait
  收集在epoll监控的事件中已经发生的事件includesysepoll。hintepollwait(intepfd第一个参数epfd:epollcreate函数的返回值。,structepolleventevents,intmaxevents,inttimeout);超时时间(毫秒)
  第一个参数epfd:epollcreate函数的返回值。
  第二个参数events:是分配好的epollevent结构体数组,epoll将会把发生的事件赋值到events数组中(events不可以是空指针,内核只负责把数据赋值到这个event数组中,不会去帮助我们在用户态分配内存)
  第三个参数maxevents:maxevents告诉内核这个events数组有多大,这个maxevents的值不能大于创建epollcreate时的size。
  第四个参数:是超时时间(毫秒),如果函数调用成功,则返回对应IO上已准备好的文件描述符数目,如果返回0则表示已经超时。基于epoll的简易http服务器
  基于epoll的简单回显服务器includestdio。hincludeunistd。hincludesystypes。hincludesyssocket。hincludenetinetin。hincludeincludesysepoll。hincludefcntl。hincludestdlib。hincludestring。hintlistensock1;intepollfd1;设置非阻塞intsetnoblock(intsock){intoptsfcntl(sock,FGETFL);returnfcntl(sock,FSETFL,optsONONBLOCK);}intcreatsocket(intport){intsocksocket(AFINET,SOCKSTREAM,0);if(sock0){perror(socket);exit(2);}调用setsockopt使当server先断开时避免进入TIMEWAIT状态,将其属性设定为SOREUSEADDR,使其地址信息可被重用intopt1;if(setsockopt(sock,SOLSOCKET,SOREUSEADDR,opt,sizeof(opt))0){perror(setsockopt);exit(3);}structsockaddrinlocal;local。sinfamilyAFINET;local。sinporthtons(port);local。sinaddr。saddrINADDRANY;inetaddr(0。0。0。0);if(bind(sock,(structsockaddr)local,sizeof(local))0){perror(bind);exit(4);}if(listen(sock,5)0){perror(listen);exit(5);}printf(listenportd。。,port);returnsock;}intacceptsocket(){structsockaddrinremote;socklentlensizeof(remote);intacceptsockaccept(listensock,(structsockaddr)remote,len);if(acceptsock0){perror(accept);return1;}printf(acceptaclient。。〔ip〕:s,〔port〕:d,inetntoa(remote。sinaddr),ntohs(remote。sinport));将新的事件添加到epoll集合中structepolleventepev;epev。eventsEPOLLINEPOLLET;edge边沿触发,只触发一次epev。data。fdacceptsock;setnoblock(acceptsock);if(epollctl(epollfd,EPOLLCTLADD,acceptsock,epev)0){perror(epollctl);close(acceptsock);return1;}return0;}inthandlerequest(intsocketFd){申请空间同时存文件描述符和缓冲区地址charbuf〔102400〕;memset(buf,,sizeof(buf));ssizetsrecv(socketFd,buf,sizeof(buf)1,0);if(s0){perror(recv);return1;}elseif(s0){printf(remoteclose。。);远端关闭了,进行善后epollctl(epollfd,EPOLLCTLDEL,socketFd,NULL);close(socketFd);}else{读取成功,输出数据printf(clients,buf);fflush(stdout);将事件改写为关心事件,进行回写structepolleventepev;epev。data。fdsocketFd;epev。eventsEPOLLOUTEPOLLET;在epoll实例中更改同一个事件,触发socket可写事件epollctl(epollfd,EPOLLCTLMOD,socketFd,epev);}return0;}inthandleresponse(intsocketFd){constcharmsgHTTP1。1200OKrrh1higirlh1r;send(socketFd,msg,strlen(msg),0);epollctl(epollfd,EPOLLCTLDEL,socketFd,NULL);close(socketFd);}intmain(intargc,charargv〔〕){intdefaultport8000;intoptch0;while((optchgetopt(argc,argv,s:p:))!1){switch(optch){casep:defaultportatoi(optarg);printf(port:s,optarg);break;case?:printf(Unknownoption:c,(char)optopt);break;default:break;}}listensockcreatsocket(defaultport);epollfdepollcreate(256);if(epollfd0){perror(epollcreat);exit(6);}structepolleventepev;epev。eventsEPOLLIN;数据的读取epev。data。fdlistensock;添加关心的事件if(epollctl(epollfd,EPOLLCTLADD,listensock,epev)0){perror(epollctl);exit(7);}structepolleventreadyev〔128〕;申请空间来放就绪的事件。intmaxnum128;inttimeout1;设置超时时间,若为1,则永久阻塞等待。intret0;intdone0;while(!done){switch(retepollwait(epollfd,readyev,maxnum,timeout)){case1:perror(epollwait);break;case0:printf(timeout。。。);break;default:至少有一个事件就绪{inti0;for(;iret;i){判断是否为监听套接字,是的话acceptintfdreadyev〔i〕。data。fd;if((fdlistensock)(readyev〔i〕。eventsEPOLLIN)){acceptsocket();}else{普通IOif(readyev〔i〕。eventsEPOLLIN){handlerequest(fd);}elseif(readyev〔i〕。eventsEPOLLOUT){handleresponse(fd);}}}}break;}}close(listensock);return0;}
  makefileTARGETserverSRC(wildcard。cpp。c)OBJ(patsubst。cpp。c,。o,(SRC))DEFSCFLAGSgCCgLIBSlpthread(TARGET):(OBJ)(CC)(CFLAGS)(DEFS)o(LIBS)。PHONY:clean:rmrf。o(TARGET)复制BuildmakeUsageubuntuVM165ubuntu:learnbaseIO复用epoll。serverlistenport8000
  浏览器输入:http:服务器ip:8000例如,http:49。234。35。128:8000
  原文链接:https:www。cnblogs。comderoyp15700636。html

猫一年会发多少次情猫发情的征兆对于铲屎官来说最痛苦的日子应该就是猫咪的发情期了。猫咪白天还好好地,一到晚上就可以发情,叫声像小孩子的哭声一样,吵得所有人都睡不好觉。猫一年会发多少次情猫咪一年通常会发四……养公猫好还是母猫好新手养公猫还是母猫很多爱猫人士在养猫前都会做好十足的准备,毕竟养猫是一件很长久的事情,要肩负起照顾猫咪的责任。但是很多人都不知道养公猫还是母猫,今天为大家介绍一下公猫和母猫的区别。养公猫好还是母……阿莫西林不能喝酒吗?吃阿莫西林喝酒严重可致死阿莫西林很多人都知道是吃了不能喝酒的,那么有什么后果呢,下面5号网的小编为你们介绍阿莫西林不能喝酒吗?吃阿莫西林喝酒严重可致死。阿莫西林不能喝酒吗阿莫西林属于贝塔内酰胺类……田亮高度评价全红婵!退役仍爱体育,女儿继承天赋练网球滑雪问:如何才能每天都能收到这样的体育资讯?答:只需点击右上角的关注即可。2022年2月4日是北京冬奥会,距离冬奥会开幕只剩下70多天时间了。虽然冬季奥运会在影响力方面……乌青膏的功效是什么?乌青膏好用吗?泰国有一款叫乌青膏的药,很多人去泰国的时候都看到过,却不知道这款药膏是做什么的,下面5号网的小编为你们介绍乌青膏的功效是什么?乌青膏好用吗?乌青膏的功效是什么超级便宜的泰……妇科千金片要配消炎药吃吗妇科千金片有止血的功效吗千金片是我们大家很多人都很熟悉的一种中成药,主要是用来治疗妇科炎症的药物,有些人在服用该药物的时候会配合服用消炎药,那么妇科千金片要配消炎药吃吗?妇科千金片有止血的功效吗?妇科……遏制整牙低龄化需加强科普和监管张淳艺暑期是儿童正畸整牙的高峰时间,不少儿童口腔诊所、口腔医院一号难求。儿童正畸千万要趁早早一步,让孩子颜值赢在起跑线新华社记者调查发现,一些商家和机构刻意制造未成年人牙……广东最受欢迎的六道特色名菜,本地人最爱吃,吃一次就爱上了说到美食,广东是最不可少的地方,从古至今,它一直被资深吃货惦记。相信去了广东旅游的朋友,都会爱上这座城市。如果要深度游玩一座城市,最佳的体验就是融入这座城市的烟火气之中,尝尝本……李湘晒12岁王诗龄日常,背5千手提包,身材暴瘦腿部成竹竿腿都说邻家有女初长成,这句话用在李湘女儿王诗龄身上,再适合不过。年仅12岁的她,即便是在路人看来,也都是一位美人胚子,不出意外的话,以后也是粉丝们心目中的女神。近日李湘在社……贝亲桃子水有封口吗?贝亲桃子水的包装贝亲桃子水是一款代购率很高的产品,好用自然不必多说,有的人发现自己买的没有封口,于是就很担心有质量问题,下面5号网的小编为你们介绍贝亲桃子水有封口吗?贝亲桃子水的包装。贝亲桃子……久诚固定在AG2队训练,云黎频繁跟AG1队5排,与EDGM一时间过得很快,不知不觉之间,2022年KPL夏季赛常规赛已经正式进入尾声了。在第3轮开打之前,6支B组战队已经提前被淘汰。而在季后赛开打前,还将再淘汰2支A组队伍。而就目前的情……新车要买哈弗神兽DHT再等等!奇瑞瑞虎7PLUS新能源即将来文:懂车帝原创周桐〔懂车帝原创产品〕日前,奇瑞发布了一张瑞虎7PLUS新能源的车型预告图。新车预计会是一款搭载PHEV插电式混合动力的车型,根据奇瑞品牌的命名规则,新车或……
我想让生命燃烧一次郝有花(图片来自网络)我想做一盏子夜的路灯用微弱的灯光照亮晚归的夜人让最后一盏灯芯点燃希望的梦,驱赶夜的凉多少次梦回故里母……杜锋祭出2大重炮手,任骏飞接班人上线,凯皇硬仗表现征服杜导新赛季杜锋开启了双塔模式,结合之前的五小阵容,让广东男篮的阵型打法更具变化。这样的用人策略,对于球队的三分投射要求更高。这个赛季广东阵中多位射手都找到了比赛感觉,他们在三分投射……董璇破碎婚姻不是生命的全部摄影:任欣羽妆发:Julymomo服装:孟欣欣经过她老公高云翔性侵案的风波,董璇沉寂了很久。如今重新回归的娱乐圈,身上多了一份从容淡定。以前的大美人,现在虽然……玻璃人锡安的坎坷头条创作挑战赛今天我们来聊聊锡安威廉姆斯,锡安出生于美国南卡罗来纳州斯山帕坦堡。在2018年3月27日锡安参加美当劳举办的扣篮大赛中获得冠军。在2019年以状元的身份加盟……旅游卡背后不为人知的秘密大家都知道的,旅游卡就是专门为旅游行业服务的一种免费旅行方式,一般是四天五天等长时间的免费旅行,但是里面的套路你们真的清楚吗?结合我在圈里的同学分享,我给你们说一说这旅游卡不为……TIAPortal软件SCL编程常见问题问题1:如何监视循环指令?正常监视如图1所示,无法监控到循环程序内部的执行情况。图1监视页面点击监视按钮可以看到FOR循环内部没有任何变量显示如果……东邻有女初长成,西屋无人挂彩灯东邻有女初长成,西屋无人挂彩灯,晚风吹得花儿落,蛙声一片月色明。夜半无眠听晚风,蛙声一片月朦胧,银盘高挂嫦娥西复东。蛙鼓闹荷丛,柳影婆娑朦胧东逝水,一轮明镜挂长空。……利拉德回击贝弗利无家可归的疯子昨天湖人121112逆转开拓者,完成25分大逆转。帕特里克贝弗利在赛后采访中谈到达米安利拉德说:如果他打爆了我,你们会说‘我防不了他。’但他投丢了几个球,就会说‘这是慢热……跟领导一起出差,发现她包里带着这些!晒给大家瞧瞧,真先进随着科技的大力发展,不管是我们的学习生活还是我们的工作生活,都已经进入了一个加速的时代。不管从事哪个岗位的工作者,都难免要上外地出差。对于比较粗犷的男士来说,出差算不上什……阿萨姆,可否回到华夏的怀抱?雅鲁藏布江发源于喜马拉雅山中段北麓的杰马央宗冰川,自西向东流,源流称为杰马央宗曲,过仲巴县桑木张后称为马泉河,自日喀则地区萨嘎县开始称为雅鲁藏布江,沿途接纳多雄藏布、雅砻河、年……这12道家常菜简单易学,好吃下饭,喜欢的朋友记得收藏大家好,欢迎大家来到我的美食自媒体,我是美食领域创作者锦绣V山东:专注美食,让生活更有味。今天为大家带来了几道家常美食的做法,这几道美食也是深受大家的喜欢,而且是很常见的几道美……末日血战朵赛莉斯秩序阵营(技能加测评)英雄基本信息英雄名称:朵赛莉斯英文名称:Toxaris英雄阵营:秩序英雄类型:攻击初始星级:5最高等级:33016星英雄综合推荐深渊魔窟推荐:0遗迹魔窟推荐:0……
友情链接:易事利快生活快传网聚热点七猫云快好知快百科中准网快好找文好找中准网快软网