|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
由于在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[MAX_EVENTS];intaddrlen,listenfd,conn_sock,nfds,epfd,fd,i,nread,n;structsockaddr_inlocal,remote;charbuf[BUFSIZ];//创立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[i].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[i].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[i].events|EPOLLOUT;if(epoll_ctl(epfd,EPOLL_CTL_MOD,fd,&ev)==-1){perror("epoll_ctl:mod");}}if(events[i].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,向人谈论起来头头是到,好像懂的很多。 |
|