Because the ET pattern needs to be read in a loop, but during the reading process, if a new event arrives, it is likely that another thread is triggering the socket to handle it, which is a mess.
Epoll_oneshot is used to avoid this situation. Note that when a thread finishes processing a socket's data, that is, when the Eagain errno is triggered, the EPOLL_ONESHOT flag should be reset, and the new event can re-enter the triggering process.
The server code is as follows:
#include <stdio.h>#include<stdlib.h>#include<unistd.h>#include<errno.h>#include<string.h>#include<fcntl.h>#include<assert.h>#include<sys/types.h>#include<sys/socket.h>#include<netinet/inch.h>#include<arpa/inet.h>#include<sys/epoll.h>#include<pthread.h>#defineMax_event_number 1024#defineBuffer_size 1024structFDS {intEPOLLFD; intsockfd;};intSetnonblocking (intFD) { intOld_option =fcntl (FD, F_GETFL); intNew_option = Old_option |O_nonblock; Fcntl (FD, F_SETFL, new_option); returnold_option;}voidADDFD (intEPOLLFD,intFdBOOLoneshot) {epoll_eventEvent; Event. DATA.FD =FD; Event. Events = Epollin |Epollet; if(oneshot) {Event. Events |=Epolloneshot; } epoll_ctl (EPOLLFD, Epoll_ctl_add, FD,&Event); Setnonblocking (FD);}voidReset_oneshot (intEPOLLFD,intFD) {epoll_eventEvent; Event. DATA.FD =FD; Event. Events = Epollin | Epollet |Epolloneshot; Epoll_ctl (EPOLLFD, Epoll_ctl_mod, FD,&Event);}void* Worker (void*Arg) { intSOCKFD = ((fds*) arg)SOCKFD; intEPOLLFD = ((fds*) arg)EPOLLFD; pthread_t PID=pthread_self (); printf ("start new thread%u to recv data on FD:%d\n", PID, SOCKFD); CharBuf[buffer_size]; memset (BUF,' /', buffer_size); while(1) { intret = recv (SOCKFD, buf, buffer_size-1,0); if(ret = =0) {close (SOCKFD); printf ("Foreiner closed the connection\n"); Break; } Else if(Ret <0) { if(errno = =Eagain) {Reset_oneshot (EPOLLFD, SOCKFD); printf ("Eagain Read later\n"); Break; } } Else{Buf[ret]=' /'; printf ("thread%u get content:%s\n", PID, BUF); printf ("thread%u about to sleep\n", PID); Sleep (5); printf ("thread%u back from sleep\n", PID); } } //printf ("End thread%u receiving data on FD:%d\n", PID, SOCKFD);}intMainintargcChar*argv[]) { if(ARGC <=1) {printf ("Usage:%s port_number [ip_address]\n", BaseName (argv[0])); return 1; } intPort = atoi (argv[1]); intRET =0; Sockaddr_in address; Bzero (&address,sizeof(address)); Address.sin_family=af_inet; if(ARGC >=3) { Const Char*ip =argv[2]; Inet_pton (Af_inet, IP,&address.sin_addr); } Else{address.sin_addr.s_addr=Inaddr_any; } address.sin_port=htons (port); intLISTENFD = socket (pf_inet, Sock_stream,0); ASSERT (LISTENFD>=0); RET= Bind (LISTENFD, (sockaddr*) &address,sizeof(address)); ASSERT (Ret!= -1); RET= Listen (LISTENFD,5); ASSERT (Ret!= -1); Epoll_event Events[max_event_number]; intEPOLLFD = Epoll_create (5); ASSERT (EPOLLFD!= -1); ADDFD (EPOLLFD, LISTENFD,false); while(1) { intret = epoll_wait (EPOLLFD, events, Max_event_number,-1); if(Ret <0) {printf ("Epoll failure\n"); Break; } for(intI=0; i<ret; i++) { intSOCKFD =EVENTS[I].DATA.FD; if(SOCKFD = =LISTENFD) {sockaddr_in client_address; socklen_t Client_addrlength=sizeof(client_address); intCONNFD = Accept (LISTENFD, (sockaddr*) &client_address,&client_addrlength); ADDFD (EPOLLFD, CONNFD,true); printf ("New connection is added to epollfd\n"); } Else if(Events[i].events &Epollin) {pthread_t thread; FDS Fds_for_new_worker; FDS_FOR_NEW_WORKER.EPOLLFD=EPOLLFD; FDS_FOR_NEW_WORKER.SOCKFD=SOCKFD; //New ThreadPthread_create (&thread, NULL, worker, (void*) &fds_for_new_worker); } Else{printf ("something Else happened\n"); }}} close (LISTENFD); return 0;}
The following is the text sent using the Telnet client, with constant typing of the code:
127.0. 0.1 12346 127.0. 0.1 127.0. 0.1 is'^]'. Hi1hi2hi3hi4hi5hi6hi7hi8connection Closed by Foreign host.
The following is the operation and output of the server:
$./epoll_oneshot12346NewConnection isadded to EpollfdstartNewThread1734051584To recv data on FD:5Thread1734051584 GetContent:hi1thread1734051584About to Sleepthread1734051584Back fromSleepthread1734051584 GetContent:hi2hi3thread1734051584About to Sleepthread1734051584Back fromSleepthread1734051584 GetContent:hi4hi5hi6thread1734051584About to Sleepthread1734051584Back fromSleepthread1734051584 GetContent:hi7thread1734051584About to Sleepthread1734051584Back fromSleepeagain Read LaterstartNewThread1723561728To recv data on FD:5Thread1723561728 GetContent:hi8thread1723561728About to Sleepthread1723561728Back fromSleepeagain Read later^c
Finally, use CTRL + C to end the server.
As you can see, between the Hi7 text and the Hi8 text, the server receives the Eagain, which means the read is over. Instead, the thread ID page is replaced with the thread ID. Before Hi7, because every time the server sleep is finished, there is still no data to read, so the thread ID is always unchanged, and the same thread is always processing the data.
An important epoll_oneshot experiment in et+ multithreading mode in Epoll