Epoll Study Notes
Epoll has two modes: edge triggered (ET) and level triggered (LT ). when using the two modes, note that if the et mode is used, the notification will only be sent when the status changes. The lt mode is similar to the original select/poll operation, as long as there are any unhandled events, they will be notified all the time.
Use code to describe the problem:
First, the server code is provided. It must be noted that each accept connection uses the et mode when a readable set is added, and the receiving buffer is 5 bytes, that is, only 5 bytes of data are received at a time:
# Include <iostream>
# Include <sys/socket. h>
# Include <sys/epoll. h>
# Include <netinet/in. h>
# Include <ARPA/inet. h>
# Include <fcntl. h>
# Include <unistd. h>
# Include <stdio. h>
# Include <errno. h>
Using namespace STD;
# Define maxline 5
# Define open_max 100
# Define listenq 20
# Define serv_port 5000
# Define inftim1000
Void setnonblocking (INT sock)
{
Int opts;
Opts = fcntl (sock, f_getfl );
If (OPTs <0)
{
Perror ("fcntl (sock, getfl )");
Exit (1 );
}
Opts = opts | o_nonblock;
If (fcntl (sock, f_setfl, opts) <0)
{
Perror ("fcntl (sock, setfl, opts )");
Exit (1 );
}
}
Int main ()
{
Int I, Maxi, listenfd, connfd, sockfd, epfd, NFDs;
Ssize_t N;
Char line [maxline];
Socklen_t clilen;
// Declare the epoll_event struct variable. eV is used to register events, and array is used to return the events to be processed.
Struct epoll_event eV, events [20];
// Generate the epoll-specific file descriptor for processing accept
Epfd = epoll_creation (256 );
Struct sockaddr_in clientaddr;
Struct sockaddr_in serveraddr;
Listenfd = socket (af_inet, sock_stream, 0 );
// Set the socket to non-blocking mode
// Setnonblocking (listenfd );
// Set the file descriptor related to the event to be processed
Ev. Data. FD = listenfd;
// Set the event type to be processed
Ev. Events = epollin | epollet;
// EV. Events = epollin;
// Register the epoll event
Epoll_ctl (epfd, epoll_ctl_add, listenfd, & eV );
Bzero (& serveraddr, sizeof (serveraddr ));
Serveraddr. sin_family = af_inet;
Char * local_addr = "127.0.0.1 ";
Inet_aton (local_addr, & (serveraddr. sin_addr); // htons (serv_port );
Serveraddr. sin_port = htons (serv_port );
BIND (listenfd, (sockaddr *) & serveraddr, sizeof (serveraddr ));
Listen (listenfd, listenq );
Maxi = 0;
For (;;){
// Wait for the epoll event to occur
NFDs = epoll_wait (epfd, events, 20,500 );
// Process all events
For (I = 0; I <NFDs; ++ I)
{
If (events [I]. Data. FD = listenfd)
{
Connfd = accept (listenfd, (sockaddr *) & clientaddr, & clilen );
If (connfd <0 ){
Perror ("connfd <0 ");
Exit (1 );
}
// Setnonblocking (connfd );
Char * STR = inet_ntoa (clientaddr. sin_addr );
Cout <"accapt a connection from" <STR <Endl;
// Set the file descriptor used for read Operations
Ev. Data. FD = connfd;
// Set the read operation event for the test.
Ev. Events = epollin | epollet;
// EV. Events = epollin;
// Register EV
Epoll_ctl (epfd, epoll_ctl_add, connfd, & eV );
}
Else if (events [I]. Events & epollin)
{
Cout <"epollin" <Endl;
If (sockfd = events [I]. Data. FD) <0)
Continue;
If (n = read (sockfd, line, maxline) <0 ){
If (errno = econnreset ){
Close (sockfd );
Events [I]. Data. FD =-1;
} Else
STD: cout <"Readline error" <STD: Endl;
} Else if (n = 0 ){
Close (sockfd );
Events [I]. Data. FD =-1;
}
Line [N] = '\ 0 ';
Cout <"read" <line <Endl;
// Set the file descriptor used for write operations
Ev. Data. FD = sockfd;
// Set the write operation event for the test.
Ev. Events = epollout | epollet;
// Modify the event to be processed on sockfd to epollout
// Epoll_ctl (epfd, epoll_ctl_mod, sockfd, & eV );
}
Else if (events [I]. Events & epollout)
{
Sockfd = events [I]. Data. FD;
Write (sockfd, line, N );
// Set the file descriptor used for read Operations
Ev. Data. FD = sockfd;
// Set the read operation event for the test.
Ev. Events = epollin | epollet;
// Modify the event to be handled on sockfd to epolin
Epoll_ctl (epfd, epoll_ctl_mod, sockfd, & eV );
}
}
}
Return 0;
}
The following section describes the client written in Perl used for testing. The client sends 10 bytes of data, and the client enters an endless loop after sending the data, that is to say, after sending, the connection status does not change-neither data transmission nor connection is closed, so that the server status can be observed:
#! /Usr/bin/perl
Use IO: socket;
My $ host = "127.0.0.1 ";
My $ Port = 5000;
My $ socket = IO: Socket: iNet-> New ("$ HOST: $ port") or die "create socket error $ @";
My $ msg_out = "1234567890 ";
Print $ socket $ msg_out;
Print "now send over, go to sleep \ n ";
While (1)
{
Sleep (1 );
}
Running the server and client, the server only reads 5 bytes of data, while the client actually sends 10 bytes of data. That is to say, the server only listens to the epollin event for the first time, since the data has not been read and the et mode is used, the status does not change after this, so the server no longer receives the epollin event.
(Friendly reminder: the above test client, when you close it, will start the IO read event again to the server, then the server will read the remaining 5 bytes of data, however, this event is not in conflict with the et nature described above .)
If we change the client to the following:
#! /Usr/bin/perl
Use IO: socket;
My $ host = "127.0.0.1 ";
My $ Port = 5000;
My $ socket = IO: Socket: iNet-> New ("$ HOST: $ port") or die "create socket error $ @";
My $ msg_out = "1234567890 ";
Print $ socket $ msg_out;
Print "now send over, go to sleep \ n ";
Sleep (5 );
Print "5 second gonesend another line \ n ";
Print $ socket $ msg_out;
While (1)
{
Sleep (1 );
}
It can be found that after the server receives 5 bytes of data, it cannot listen to client events. When the client goes to sleep for 5 seconds and resends the data, the server listens to the changes again, however, because only five bytes are read, 10 bytes of data (the data sent by the client for the second time) are still not received.
If the above experiment uses the LT mode for the accept socket, the server will continue to be notified as long as there is data in the buffer, and the reader can modify the code for the experiment.
Based on these two experiments, we can conclude that the et mode is notified only when the State changes. The so-called state changes do not include any unprocessed data in the buffer zone, that is to say, if you want to adopt the et mode, you need to read/write until an error occurs. Many people reflect why the et mode only receives a part of the data and will no longer be notified. Most of the reasons are as follows; however, in the LT mode, the notification will continue as long as there is no data to be processed.
I would like to explain what the "State Change" has always been emphasized here:
1) when listening for readable events, if the socket is listening for the socket, the status changes when a new active connection arrives. For general sockets, new data in the corresponding buffer zone of the protocol stack changes to the status. however, if n connections (n> 1) are received at the same time, but the listening socket only accepts one connection, other unaccept connections will not send a notification to the listening socket in et mode, and the status will not change. For general sockets, as in the example, if the corresponding buffer has n bytes of data and only data smaller than n Bytes is retrieved, the residual data will not change the status.
2) When listening for writeable events, the same push will not be detailed.
Whether the listener is readable or writable, closing the socket connection will change the status. For example, in this example, if the client script is forcibly interrupted, the socket connection is automatically interrupted, in this case, the status of the server changes, and the server is notified to read the data already in the local buffer.
The preceding description can be summarized as follows: only events caused by the other party's actions (such as sending data and closing the connection) can cause status changes, events that have been processed in the protocol stack of this party (including receiving the data from the other party and receiving the request from the other party's active connections) are not necessary for changing the status, status changes must be caused by the other party. therefore, in et mode, the next action can be performed only after an error is processed or the process is complete. Otherwise, an error may occur.
In addition, some basic network programming concepts can be elaborated from this example. first, sending successfully at both ends of the connection does not mean that the application at the upper layer of the connection receives the message successfully. Taking the client test program above, the 10-byte data has been successfully sent, however, the upper-layer server does not call read to read data. Therefore, the successful sending only indicates that the data is received by the protocol stack of the other party and stored in the corresponding buffer, it is unknown whether upper-layer applications have received this part of data. Similarly, reading data only indicates that the corresponding buffer of the protocol stack is readable, at this time, it is unknown whether the data is being sent on the peer end.
Reference: http://www.cppblog.com/converse/archive/2008/04/29/48482.html