Multi-channel IO-POll function, epoll server development process

Introduction

“In computer network programming, multi-channel IO technology is a very common technology. Among them, the Poll function and the Epoll function are the two most commonly used multi-channel IO technologies. These two technologies can help the server handle the processing of multiple clients. Concurrent requests improve server performance. This article will introduce the use of Poll and Epoll functions, and discuss the processes and precautions for using these two technologies in server development.”

Introduction to poll function

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

(man poll call)

Function Description: Similar to select, it entrusts the kernel to monitor readable, writable, and abnormal events

Function parameters:

fds: The first address of a struct pollfd structure array

struct pollfd {

int fd;//The file descriptor to be monitored, if fd is -1, it means the kernel will no longer monitor

short events; //Input parameters, indicating events to be monitored by the kernel, read events, write events, exception events

short revents;//Output parameters, indicating that the kernel tells the application which file descriptors have events occurred

};

events/revents:

POLLIN: readable event, you need to write this to let the kernel monitor read events

POLLOUT: Writable event, the buffer can be written before it is full

POLLERR: Exception event

nfds: tells the kernel the scope of monitoring, specifically: the maximum value of the array subscript + 1

timeout:

=0: No blocking, return immediately

-1: means blocking until an event occurs

>0: Indicates the blocking duration. If an event occurs within the duration, it will return immediately;

If the time is exceeded, it will return immediately

Function return value:

>0: The number of changed file descriptors

=0: No file descriptors changed

-1: Indicates exception

poll function development process

1 Create socket and get the listening file descriptor, lfd —– socket();

2 Set port reuse———-setsockopt()

3 Binding —— bind()

4

struct pollfd client[1024];

client[0].fd = lfd; // You can put it anywhere, put it at the end for easy use

client[0].events = POLLIN; //Monitor read events. If you also want it to monitor writable events, use or

// Set fd to -1, indicating that the kernel is not monitoring. This is an initialization

int maxi = 0; // Define the maximum array index

for(int i = 0;i < 1024;i + + )

{

        client[i].fd = -1;

}

//Entrust the kernel to continuously monitor

k= 0;

while(1)

{

      nready = poll(client,maxi + 1,-1);
     //abnormal situation

     if(nready < 0 )

     {

            if(error == EINTR)

            {

                   continue;

            }
            break;

     }

     if(client[0].revents = POLLIN)

    {

          //Accept new client connections

         k++;

          cfd = Accept(lfd,NULL,NULL);

          /*Continue to entrust the kernel to listen for events

         Find available positions in client array*/

          for(i = 0;i < 1024;i + + )

         {

                 if(client[i].fd ==-1)

                {

                        client.fd[i] = cfd;

                        client.fd[i] = POLLIN;

                         break;

                }

          }

         //The number of client connections reaches the maximum value

          if(i == 1024)

          {

                 close(cfd);

                  continue; //Exit, there may be a client connection exit to facilitate continued search

           }

          //Modify the maximum subscript value of the client array

           if(maxi < i )

                maxi = i;

           if(--nready == 0 )

               continue;

    }

    //The following is the situation where the client sends data

     for(i = 1;i <= maxi;i + + )

    {

         //If fd in the client array is -1, it means that the kernel is no longer monitored.

          if(client[i].fd == -1)

               continue;



          if(client[i].revents == POLLIN)

          {

                 sockfd = client[i].fd;

                 memset(buf,0x00,sizeof(buf));

                  //read data

                  n = Read(sockfd, buf,sizeof(buf));

                  if(n <= 0)

                  {

                         printf("read error or client closed,n =[%d]\
",n);

                          close(sockfd);

                          client[i].fd = -1; //Tell the kernel not to monitor anymore

             

                  }

                  else
                  {

                             printf("read error,n == [%d],buf==[%s]\
,"n,buf);

                            //Send data to client

                            Write(sockfd,buf,n);

                   }

                    if(--nready == 0 )

                    {

                           break;

                     }

           }

     }

     close(lfd);

}

Multiple IO-epoll (key)

The detection of changes in file descriptors is entrusted to the kernel for processing, and then the kernel will
Events are returned to the application.

head File

#include

function

int epoll_create(int size)

Function description: Create a poll tree and return a number of root nodes

Function parameters: size: a number greater than 0 must be passed

Return value: Returns a file descriptor. This file descriptor represents the root node of the epoll tree.

int epoll_ctl(int epfd,int op,int fd,struct epoll_event *event)

Function description: Delete and modify the epoll tree on fd from the tree

Function parameters:

epfd: the root node of the epoll tree

op:

EPOLL_CTL_ADD: Add event nodes to the tree
EPOLL_CTL_DEL: Delete event nodes from the tree
EPOLL_CTL_MOD: Modify the corresponding event node on the tree

fd: the file descriptor to be operated on

event:
Commonly used event.events are:
EPOLLIN: read events
EPOLLOUT: write events
EPOLLERR: error event
EPOLLET: edge triggered mode

event.fd: the file descriptor corresponding to the event to be monitored

typedef union epoll_data{

void *ptr;

int fd;

uint32_t u32;

uint64_t u64;

}epoll_data_t;

struct epoll_event{

uint32 events; / * Epoll events */

epoll_data data; /* User data variable */

};

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

Function description: Wait for the kernel return event to occur

Parameter Description:

epfd: epoll tree root

events: Outgoing parameters, actually an array of event structures

maxevents: array size

timeout:

-1: indicates permanent blocking

0: Return immediately

>0: indicates timeout waiting event

return value:

Success: Returns the number of events that occurred

Failure: If timeout=0, return if no event occurs; return -1, set errno value,

Use epoll model to develop server process

1: Create a socket and get the listening file descriptor lfd —- socket()

2: Set port reuse —– setsockopt()

3: Binding —— bind()

4: Listening ——– listen()

5. Create an epoll tree

Develop complete code

//EPOLL model testing
#include "wrap.h"
#include <sys/epoll.h>
#include <ctype.h>
int main()
{
int ret;
int n;
int nready;
int lfd;
int cfd;
int sockfd;
char buf[1024];
socklen_t socklen;
struct sockaddr_in svraddr;
struct epoll_event ev;
struct epoll_event events[1024];
int k;
int i;
\t
//Create socket
lfd = Socket(AF_INET,SOCK_STREAM,0);
\t
//Set the file descriptor for port reuse
    int opt = 1;
    setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR, & amp;opt,sizeof(int));
    
    //Bind
    svraddr.sin_family = AF_INET;
svraddr.sin_addr.s_addr = htonl(INADDR_ANY);
svraddr.sin_port = htons(8888);
Bind(lfd,(struct sockaddr *) & amp;svraddr,sizeof(struct sockaddr_in));
\t
//Listen
Listen(lfd,128);
\t
//Create an epoll tree
int epfd = epoll_create(1024);
if(epfd < 0 )
{
perror("create epoll error");
return -1;
}
\t
ev.data.fd = lfd;
ev.events = EPOLLIN;
epoll_ctl(epfd,EPOLL_CTL_ADD,lfd, & amp;ev); //The event node corresponding to lfd is on the tree
\t
while(1)
{
nready = epoll_wait(epfd,events,1024,-1); //Waiting for the kernel to return events
if(nready < 0)
{
perror("epoll_wait error");
if(nready == EINTR) //Determine whether an interrupt signal is received
{
continue;
}
break;
}
for(i = 0;i < nready;i + + ) //less than the number of events that occur
{
//There is a client connection sending a request
sockfd = events[i].data.fd;
if(sockfd == lfd)
{
cfd = Accept(lfd,NULL,NULL);
ev.data.fd = cfd;
ev.events = EPOLLIN;
epoll_ctl(epfd,EPOLL_CTL_ADD,cfd, & amp;ev);
}
//A client sends data
else {
memset(buf,0x00,sizeof(buf));
n = Read(sockfd,buf,sizeof(buf));
if(n <= 0)
{
close(sockfd);
epoll_ctl(epfd,EPOLL_CTL_DEL,sockfd,NULL); //Delete sockfd from the epfd tree
}
else
{
for(k = 0;k < n;k + + )
{
buf[k] = toupper(buf[k]); //Return to uppercase
}
Write(sockfd,buf,n);
}
}
\t\t\t
}
}
close(epfd);
close(lfd);
return 0;
}
 

epoll’s two modes ET and LT mode

LT mode of epoll:

The default situation of epoll is LT mode. In this case, if the read data is not finished at one time,

If there is still readable data in the buffer, epoll_wait will notify again.

ET mode of epoll:

If epoll is set to ET mode, if the data is not read at once, epoll_wait will no longer notify

Until next time there is new data

In ET mode, in order to prevent the second client from being able to connect normally and send data, the socket needs to be set to non-blocking mode.

ET sets the non-blocking mode because it uses the edge trigger mode (EPOLLET). In edge trigger mode, when there is data to read, the EPOLLIN event will only be triggered once. If the read does not read all the data in the buffer, the EPOLLIN event will still be triggered next time. Therefore, in order to ensure that complete data is read every time, the socket needs to be set to non-blocking mode to avoid blocking when the buffer is not fully read.

Code:

//EPOLL model test ET
#include "wrap.h"
#include <sys/epoll.h>
#include <ctype.h>
#include <fcntl.h>
int main()
{
int ret;
int n;
int nready;
int lfd;
int cfd;
int sockfd;
char buf[1024];
socklen_t socklen;
struct sockaddr_in svraddr;
struct epoll_event ev;
struct epoll_event events[1024];
int k;
int i;
\t
//Create socket
lfd = Socket(AF_INET,SOCK_STREAM,0);
\t
//Set the file descriptor for port reuse
    int opt = 1;
    setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR, & amp;opt,sizeof(int));
    
    //Bind
    svraddr.sin_family = AF_INET;
svraddr.sin_addr.s_addr = htonl(INADDR_ANY);
svraddr.sin_port = htons(8888);
Bind(lfd,(struct sockaddr *) & amp;svraddr,sizeof(struct sockaddr_in));
\t
//Listen
Listen(lfd,128);
\t
//Create an epoll tree
int epfd = epoll_create(1024);
if(epfd < 0 )
{
perror("create epoll error");
return -1;
}
\t
ev.data.fd = lfd;
ev.events = EPOLLIN;
epoll_ctl(epfd,EPOLL_CTL_ADD,lfd, & amp;ev); //The event node corresponding to lfd is on the tree
\t
while(1)
{
nready = epoll_wait(epfd,events,1024,-1); //Waiting for the kernel to return events
if(nready < 0)
{
perror("epoll_wait error");
if(nready == EINTR) //Determine whether an interrupt signal is received
{
continue;
}
break;
}
for(i = 0;i < nready;i + + ) //less than the number of events that occur
{
//There is a client connection sending a request
sockfd = events[i].data.fd;
if(sockfd == lfd)
{
cfd = Accept(lfd,NULL,NULL);
ev.data.fd = cfd;
ev.events = EPOLLIN | EPOLLET; //
epoll_ctl(epfd,EPOLL_CTL_ADD,cfd, & amp;ev);
\t\t\t\t
//Set cfd to non-blocking mode
int flag = fcntl(cfd, F_GETFL);
                flag |= O_NONBLOCK; //The O_NONBLOCK (non-blocking) flag position is 1.
                fcntl(cfd, F_SETFL, flag);
}
//A client sends data
else {
\t\t\t\t
memset(buf,0x00,sizeof(buf));
while(1)
{
n = Read(sockfd,buf,sizeof(buf));
printf("n == [%d]\
",n);
\t\t\t\t\t
if(n == -1)
{
printf("read over,n == [%d]\
",n);
break;
}
if(n < 0 || (n <0 & amp; & amp; n!=-1)) //The other party closes the connection, or there is an abnormal situation
{
printf("n == [%d],buf == [%s]\
",n,buf);
close(sockfd);
epoll_ctl(epfd,EPOLL_CTL_DEL,sockfd,NULL); //Delete sockfd from the epfd tree
break;
}
else
{
printf("n == [%d],buf == [%s]\
",n,buf);
for(k = 0;k < n;k + + )
{
buf[k] = toupper(buf[k]); //Return to uppercase
}
Write(sockfd,buf,n);
}
}
\t        \t
}
\t\t\t
}
}
close(epfd);
close(lfd);
return 0;
}
 

Illustration of the epoll reactor process