Linux教程之Epoll在LT和ET形式下的读写体例仓酷云
由于在linux中,用户权限很大,做任何事情都很自由,所以,你往往需要知道你做的每一步在干什么。在一个非堵塞的socket上挪用read/write函数,前往EAGAIN大概EWOULDBLOCK(注:EAGAIN就是EWOULDBLOCK)
从字面上看,意义是:EAGAIN:再试一次,EWOULDBLOCK:假如这是一个堵塞socket,操纵将被block,perror输入:Resourcetemporarilyunavailable
总结:
这个毛病暗示资本临时不敷,能read时,读缓冲区没无数据,大概write时,写缓冲区满了。碰到这类情形,假如是堵塞socket,read/write就要堵塞失落。而假如长短堵塞socket,read/write当即前往-1,同时errno设置为EAGAIN。
以是,关于堵塞socket,read/write前往-1代表收集堕落了。但关于非堵塞socket,read/write前往-1纷歧定收集真的堕落了。多是Resourcetemporarilyunavailable。这时候你应当再试,直到Resourceavailable。
综上,关于non-blocking的socket,准确的读写操纵为:
读:疏忽失落errno=EAGAIN的毛病,下次持续读
写:疏忽失落errno=EAGAIN的毛病,下次持续写
关于select和epoll的LT形式,这类读写体例是没有成绩的。但关于epoll的ET形式,这类体例另有毛病。
epoll的两种形式LT和ET
两者的差别在于level-trigger形式下只需某个socket处于readable/writable形态,不管甚么时分举行epoll_wait城市前往该socket;而edge-trigger形式下只要某个socket从unreadable变成readable或从unwritable变成writable时,epoll_wait才会前往该socket。
以是,在epoll的ET形式下,准确的读写体例为:
读:只需可读,就一向读,直到前往0,大概errno=EAGAIN
写:只需可写,就一向写,直到数据发送完,大概errno=EAGAIN
准确的读
n=0;while((nread=read(fd,buf+n,BUFSIZ-1))>0){n+=nread;}if(nread==-1&&errno!=EAGAIN){perror("readerror");}准确的写
intnwrite,data_size=strlen(buf);n=data_size;while(n>0){nwrite=write(fd,buf+data_size-n,n);if(nwrite<n){if(nwrite==-1&&errno!=EAGAIN){perror("writeerror");}break;}n-=nwrite;}准确的accept,accept要思索2个成绩
(1)堵塞形式accept存在的成绩
思索这类情形:TCP毗连被客户端短命,即在服务器挪用accept之前,客户端自动发送RST停止毗连,招致方才创建的毗连从停当行列中移出,假如套接口被设置成堵塞形式,服务器就会一向堵塞在accept挪用上,直到其他某个客户创建一个新的毗连为止。可是在此时代,服务器纯真地堵塞在accept挪用上,停当行列中的其他形貌符都得不各处理。
办理举措是把监听套接口设置为非堵塞,当客户在服务器挪用accept之前中断某个毗连时,accept挪用能够当即前往-1,这时候源自Berkeley的完成会在内核中处置该事务,其实不会将该事务关照给epool,而其他完成把errno设置为ECONNABORTED大概EPROTO毛病,我们应当疏忽这两个毛病。
(2)ET形式下accept存在的成绩
思索这类情形:多个毗连同时抵达,服务器的TCP停当行列刹时堆集多个停当毗连,因为是边沿触发形式,epoll只会关照一次,accept只处置一个毗连,招致TCP停当行列中剩下的毗连都得不各处理。
办理举措是用while轮回抱住accept挪用,处置完TCP停当行列中的一切毗连后再加入轮回。怎样晓得是不是处置完停当行列中的一切毗连呢?accept前往-1而且errno设置为EAGAIN就暗示一切毗连都处置完。
综合以上两种情形,服务器应当利用非堵塞地accept,accept在ET形式下的准确利用体例为:
while((conn_sock=accept(listenfd,(structsockaddr*)&remote,(size_t*)&addrlen))>0){handle_client(conn_sock);}if(conn_sock==-1){if(errno!=EAGAIN&&errno!=ECONNABORTED&&errno!=EPROTO&&errno!=EINTR)perror("accept");}一道腾讯背景开辟的口试题
利用Linuxepoll模子,程度触发形式;当socket可写时,会一直的触发socket可写的事务,怎样处置?
第一种最广泛的体例:
必要向socket写数据的时分才把socket到场epoll,守候可写事务。
承受到可写事务后,挪用write大概send发送数据。
当一切数据都写完后,把socket移出epoll。
这类体例的弱点是,即便发送很少的数据,也要把socket到场epoll,写完后在移出epoll,有必定操纵价值。
一种改善的体例:
入手下手不把socket到场epoll,必要向socket写数据的时分,间接挪用write大概send发送数据。假如前往EAGAIN,把socket到场epoll,在epoll的驱动下写数据,全体数据发送终了后,再移出epoll。
这类体例的长处是:数据未几的时分能够制止epoll的事务处置,进步效力。
最初贴一个利用epoll,ET形式的复杂HTTP服务器代码:
#include<sys/socket.h>#include<sys/wait.h>#include<netinet/in.h>#include<netinet/tcp.h>#include<sys/epoll.h>#include<sys/sendfile.h>#include<sys/stat.h>#include<unistd.h>#include<stdio.h>#include<stdlib.h>#include<string.h>#include<strings.h>#include<fcntl.h>#include<errno.h>#defineMAX_EVENTS10#definePORT8080//设置socket毗连为非堵塞形式voidsetnonblocking(intsockfd){intopts;opts=fcntl(sockfd,F_GETFL);if(opts<0){perror("fcntl(F_GETFL)n");exit(1);}opts=(opts|O_NONBLOCK);if(fcntl(sockfd,F_SETFL,opts)<0){perror("fcntl(F_SETFL)n");exit(1);}}intmain(){structepoll_eventev,events;intaddrlen,listenfd,conn_sock,nfds,epfd,fd,i,nread,n;structsockaddr_inlocal,remote;charbuf;//创立listensocketif((listenfd=socket(AF_INET,SOCK_STREAM,0))<0){perror("sockfdn");exit(1);}setnonblocking(listenfd);bzero(&local,sizeof(local));local.sin_family=AF_INET;local.sin_addr.s_addr=htonl(INADDR_ANY);;local.sin_port=htons(PORT);if(bind(listenfd,(structsockaddr*)&local,sizeof(local))<0){perror("bindn");exit(1);}listen(listenfd,20);epfd=epoll_create(MAX_EVENTS);if(epfd==-1){perror("epoll_create");exit(EXIT_FAILURE);}ev.events=EPOLLIN;ev.data.fd=listenfd;if(epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev)==-1){perror("epoll_ctl:listen_sock");exit(EXIT_FAILURE);}for(;;){nfds=epoll_wait(epfd,events,MAX_EVENTS,-1);if(nfds==-1){perror("epoll_pwait");exit(EXIT_FAILURE);}for(i=0;i<nfds;++i){fd=events.data.fd;if(fd==listenfd){while((conn_sock=accept(listenfd,(structsockaddr*)&remote,(size_t*)&addrlen))>0){setnonblocking(conn_sock);ev.events=EPOLLIN|EPOLLET;ev.data.fd=conn_sock;if(epoll_ctl(epfd,EPOLL_CTL_ADD,conn_sock,&ev)==-1){perror("epoll_ctl:add");exit(EXIT_FAILURE);}}if(conn_sock==-1){if(errno!=EAGAIN&&errno!=ECONNABORTED&&errno!=EPROTO&&errno!=EINTR)perror("accept");}continue;}if(events.events&EPOLLIN){n=0;while((nread=read(fd,buf+n,BUFSIZ-1))>0){n+=nread;}if(nread==-1&&errno!=EAGAIN){perror("readerror");}ev.data.fd=fd;ev.events=events.events|EPOLLOUT;if(epoll_ctl(epfd,EPOLL_CTL_MOD,fd,&ev)==-1){perror("epoll_ctl:mod");}}if(events.events&EPOLLOUT){sprintf(buf,"HTTP/1.1200OKrnContent-Length:%drnrnHelloWorld",11);intnwrite,data_size=strlen(buf);n=data_size;while(n>0){nwrite=write(fd,buf+data_size-n,n);if(nwrite<n){if(nwrite==-1&&errno!=EAGAIN){perror("writeerror");}break;}n-=nwrite;}close(fd);}}}return0;}
有些人号称用过十几种甚至几十种linux,向人谈论起来头头是到,好像懂的很多。 即便是非英语国家的人发布技术文档,Linux也都首先翻译成英语在国际学术杂志和网络上发表。 要增加自己Linux的技能,只有通过实践来实现了。所以,赶快找一部计算机,赶快安装一个Linux发行版本,然后进入精彩的Linux世界,相信对于你自己的Linux能力必然大有斩获。 主流Linux发行版都自带非常详细的文档(包括手册页和FAQ),从系统安装到系统安全,针对不同层次的人的详尽文档,仔细阅读文档后40%问题都可在此解决。 我是学习嵌入式方向的,这学期就选修了这门专业任选课。 笔者五分钟后就给出了解决方法: “首先备份原文件到其他目录,然后删掉/usr/local/unispim/unispimsp.ksc,编辑 /usr/local/unispim/unispimsp.ini,最后重启动计算机 以前觉得Linux就跟dos一样,全是用命令窗口,相对于窗口界面来说多麻烦呀。 得到到草率的回答或者根本得不到任何Linux答案。越表现出在寻求帮助前为解决问题付出的努力,你越能得到实质性的帮助。 安装一个新的软件时先看README,再看INSTALL然后看FAQ,最后才动手安装,这样遇到问题就知道为什么。如果Linux说明文档不看,结果出了问题再去论坛来找答案反而浪费时间。 可以说自己收获很大,基本上完成了老师布置的任务,对于拔高的题目没有去做,因为我了解我的水平,没有时间和精力去做。? 其次,Linux简单易学,因为我们初学者只是学的基础部分,Linux的结构体系非常清晰,再加上老师循序渐进的教学以及耐心的讲解,使我们理解起来很快,短期内就基本掌握了操作和运行模式。 老实说,第一个程序是在C中编译好的,调试好了才在Linux下运行,感觉用vi比较麻烦,因为有错了不能调试,只是提示错误。 把这个问题放在其他Linux社区请求帮助也是一种选择。如果Linux得不到答案,请不要以为我们觉得无法帮助你。有时只是看到你问题的人不知道答案罢了。这时换一个社区是不错的选择。 我是学习嵌入式方向的,这学期就选修了这门专业任选课。
页:
[1]