Epoll Classic Use method

Source: Internet
Author: User
Tags epoll set socket htons

EPOLL-I/O Event Notification Facility

In the Linux network programming, the long time uses the Select to do the event to trigger. In the new Linux kernel, there is a mechanism to replace it, that is epoll.
The biggest benefit compared to Select,epoll is that it does not reduce efficiency as the number of monitoring FD increases. Because in a select implementation in the kernel, it is handled by polling, the more the number of FD polled, the more natural time consuming. Also, in the Linux/posix_types.h header file there is such a declaration:
#define __FD_SETSIZE 1024
This indicates that select listens for up to 1024 FD at the same time, and of course, you can expand this number by modifying the header file and then recompiling the kernel, but this does not seem to be the root.

The Epoll interface is very simple, with a total of three functions:
1. int epoll_create (int size);
Create a handle to the Epoll, which is used to tell the kernel how large the number of listeners is. This parameter differs from the first parameter in select (), giving the value of the fd+1 that is the maximum listener. It should be noted that when the Epoll handle is created, it will occupy an FD value, under Linux if the view/proc/process id/fd/, is able to see the FD, so after the use of Epoll, you must call Close (), or it may cause FD to be exhausted.


2. int epoll_ctl (int epfd, int op, int fd, struct epoll_event *event);
The event registration function of Epoll, which differs from select () tells the kernel what type of event to listen to when listening to events, and instead registers the type of event to listen on first. The first parameter is the return value of Epoll_create (), and the second argument represents an action, represented by three macros:
Epoll_ctl_add: Register the new FD to EPFD;
Epoll_ctl_mod: Modify the monitoring events of the registered FD;
Epoll_ctl_del: Delete a fd from the EPFD;
The third parameter is the FD to listen to, the fourth parameter is to tell the kernel what to listen to, struct epoll_event structure as follows:

typedef Union EPOLL_DATA {
void *ptr;
int FD;
__uint32_t u32;
__uint64_t U64;
} epoll_data_t;

struct Epoll_event {
__uint32_t events; /* Epoll Events */
epoll_data_t data; /* USER Data variable */
};

Events can be a collection of several macros:
Epollin: Indicates that the corresponding file descriptor can be read (including a graceful shutdown of the peer socket);
Epollout: Indicates that the corresponding file descriptor can be written;
Epollpri: Indicates that the corresponding file descriptor has an urgent data readable (this should indicate the arrival of out-of-band data);
Epollerr: Indicates an error occurred in the corresponding file descriptor;
Epollhup: Indicates that the corresponding file descriptor is hung up;
Epollet: Set Epoll to edge triggered mode, which is relative to the horizontal trigger (level triggered).
Epolloneshot: Listen to only one event, when the event is monitored, if you still need to continue to listen to the socket, you need to add the socket to the Epoll queue again


3. int epoll_wait (int epfd, struct epoll_event * events, int maxevents, int timeout);
Waits for the event to occur, similar to a select () call. Parameter events is used to get the collection of event from the kernel, the maxevents tells the kernel how big the events are, the value of this maxevents cannot be greater than the size when the Epoll_create () was created, the parameter timeout is the timeout (in milliseconds, 0 returns immediately , 1 will be uncertain, and there are statements that are permanently blocked). The function returns the number of events that need to be processed, such as returning 0 to indicate a timeout.


4, about the ET, lt two Modes of operation:
The conclusion can be drawn that:
The ET pattern is only notified when the state changes, and the so-called state changes do not include the data in the buffer, which means that, if you want to use the ET pattern, you need to read/write until an error occurs. Many people reflect why the ET model receives only a subset of the data and is no longer notified, mostly because of this, while the LT mode is kept informed as long as there is no data processing.


So how do you actually use Epoll? It's actually very simple.
By including a header file in # # <sys/epoll.h> and a few simple APIs will greatly increase the number of support for your Web server.

Start by create_epoll (int Maxfds) to create a epoll handle, where Maxfds is the maximum number of handles that you epoll support. This function returns a new Epoll handle, and all subsequent operations are handled by this handle. When you're done, remember to close the created Epoll handle with close ().

Then in your main loop of the network, each frame calls epoll_wait (int epfd, epoll_event events, int max events, int timeout) to query all the network interfaces, to see which one can be read and which one can be written. The basic syntax is:
Nfds = epoll_wait (KDPFD, events, maxevents,-1);
Where KDPFD is the handle created with Epoll_create, events is a epoll_event* pointer, and when epoll_wait this function succeeds, all read and write events are stored in epoll_events. Max_events is the number of socket handles currently required to be monitored. The last timeout is a timeout of epoll_wait, which is 0 when the return is immediately, and 1 is the time to wait until there is an event range, for any positive integer to represent such a long time, if there is no event, then the range. In general, if the network main loop is a separate thread, you can use-one, and so on, this can guarantee some efficiency, if it is the same thread as the main logic, you can guarantee the efficiency of the main loop with zero.

The epoll_wait range should be followed by a loop, all the events of interest.

Almost all Epoll programs use the following framework:

for (;;)
{
Nfds = epoll_wait (epfd,events,20,500);
for (I=0;i<nfds;++i)
{
if (EVENTS[I].DATA.FD==LISTENFD)//There is a new connection
{
CONNFD = Accept (LISTENFD, (SOCKADDR *) &clientaddr, &clilen); Accept this connection
EV.DATA.FD=CONNFD;
ev.events=epollin| Epollet;
Epoll_ctl (Epfd,epoll_ctl_add,connfd,&ev); Add a new FD to the Epoll listening queue
}
else if (Events[i].events&epollin)//received data, read socket
{
n = Read (SOCKFD, line, MAXLINE)) < 0//Reading
EV.DATA.PTR = MD; MD for custom types, adding data
Ev.events=epollout| Epollet;
Epoll_ctl (Epfd,epoll_ctl_mod,sockfd,&ev);//Modify the identifier, wait for the next loop to send data, the essence of asynchronous processing
}
else if (events[i].events&epollout)//have data to send, write socket
{
struct myepoll_data* MD = (myepoll_data*) events[i].data.ptr; Fetch data
SOCKFD = md->fd;
Send (SOCKFD, md->ptr, strlen (char*) md->ptr), 0); Send data
EV.DATA.FD=SOCKFD;
ev.events=epollin| Epollet;
Epoll_ctl (Epfd,epoll_ctl_mod,sockfd,&ev); Modify identifiers to receive data while waiting for the next loop
}
Else
{
The other treatment
}
}
}



A complete example of server-side is given below:


#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 INFTIM 1000

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 argc, char* argv[])
{
int I, Maxi, LISTENFD, CONNFD, Sockfd,epfd,nfds, portnumber;
ssize_t N;
Char Line[maxline];
Socklen_t Clilen;


if (2 = = argc)
{
if ((PortNumber = Atoi (argv[1])) < 0)
{
fprintf (stderr, "usage:%s portnumber/a/n", argv[0]);
return 1;
}
}
Else
{
fprintf (stderr, "usage:%s portnumber/a/n", argv[0]);
return 1;
}



Declare variables for epoll_event struct, Ev for registering events, arrays for returning events to be processed

struct Epoll_event ev,events[20];
Generates a epoll dedicated file descriptor for handling the Accept

Epfd=epoll_create (256);
struct sockaddr_in clientaddr;
struct sockaddr_in serveraddr;
LISTENFD = socket (af_inet, sock_stream, 0);
Set socket to non-blocking mode

Setnonblocking (LISTENFD);

Set the file descriptor associated with the event to be processed

EV.DATA.FD=LISTENFD;
Set the type of event to be processed

ev.events=epollin| Epollet;
Ev.events=epollin;

Registering Epoll Events

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 (portnumber);

Serveraddr.sin_port=htons (PortNumber);
Bind (LISTENFD, (SOCKADDR *) &serveraddr, sizeof (SERVERADDR));
Listen (LISTENFD, Listenq);
Maxi = 0;
for (;;) {
Waiting for the Epoll event to occur

Nfds=epoll_wait (epfd,events,20,500);
Handle all events that occur

for (I=0;i<nfds;++i)
{
if (EVENTS[I].DATA.FD==LISTENFD)//if the newly monitored socket user is connected to the bound socket port, a new connection is established.

{
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 for the read operation

EV.DATA.FD=CONNFD;
To set the read operation event for the injection test

ev.events=epollin| Epollet;
Ev.events=epollin;

Registered EV

Epoll_ctl (Epfd,epoll_ctl_add,connfd,&ev);
}
else if (Events[i].events&epollin)//If the user is already connected and receives the data, then read in.

{
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 file descriptors for write operations

EV.DATA.FD=SOCKFD;
Set up write events for the injection test

Ev.events=epollout| Epollet;
Modify the event to be handled on SOCKFD as Epollout

Epoll_ctl (Epfd,epoll_ctl_mod,sockfd,&ev);

}
else if (events[i].events&epollout)//If there is data sent

{
SOCKFD = EVENTS[I].DATA.FD;
Write (sockfd, line, N);
Set the file descriptor for the read operation

EV.DATA.FD=SOCKFD;
To set the read operation event for the injection test

ev.events=epollin| Epollet;
Modify the event to be handled on SOCKFD as Epolin

Epoll_ctl (Epfd,epoll_ctl_mod,sockfd,&ev);
}
}
}
return 0;
}


It's good that the client connects directly to this server.

Transferred from: http://blog.csdn.net/ljx0305/article/details/4065058

Epoll Classic Use method

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.