Linux - basic use of epoll

Posted by chomedey on Wed, 26 Jan 2022 04:52:12 +0100

catalogue

1, Epoll event driven process

2, Reactor design pattern

3, Epoll correlation function

1. Create: epoll_create

2. Add, modify or delete events of interest to the EPOLL object: epoll_ctl

3. Collect events that have occurred in the events monitored by the specified epoll: epoll_wait

4, Trigger mode

1. Level_triggered

2. Edge_triggered

5, Implement a simple web server using Epoll

1. C language source code

2.   ./ Resources/index.html source code

3.   ./ Reources/reply.html source code

6, Use of libraries encapsulated with Epoll

1. Library source code (globals.h)

2. Implementation files of epoll related interfaces (comm_epoll.c)

3. Implementation of external related interfaces (comm.c)

4. Use the library to implement the recovery server (main.c)

Attachment: query and modification of default maximum file descriptor under Linux

1, Epoll event driven process

The non ready event queue and ready event queue in the event driven process are implemented by red black tree.


2, Reactor design pattern

  1. Epoll is implemented using this design pattern
  2. advantage:

1. The response is fast and does not have to be blocked by a single synchronization event, although the Reactor itself is still synchronized

2. The programming is relatively simple, which can avoid complex multithreading and synchronization problems to the greatest extent, and avoid the switching overhead of multithreading / process

3. Scalability. You can easily make full use of CPU resources by increasing the number of Reactor instances

4. Reusability. reactor framework itself is independent of specific event processing logic and has high reusability

3, Epoll correlation function

1. Create: epoll_create

/*************************************************************************************************
Function: int epoll_create(int size);
Head: #include
 Function: create a new epoll instance
 Parameters:
    size        - Maximum number of listeners. From Linux 2 Starting from 6.8, this parameter is ignored, but must be greater than 0
 return:
    Success - returns the created file descriptor
    Failure - Returns - 1. errno indicates an error message
**************************************************************************************************/

2. Add, modify or delete events of interest to the EPOLL object: epoll_ctl

/*************************************************************************************************
Function: int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
Head: #include
 Function: add, modify or delete events of interest to the EPOLL object
 Parameters:
    epfd        - Through epoll_ Instance created by create()
    op            - Action, you can use the following macros:
                            EPOLL_CTL_ADD - Register a new fd into epfd
                            EPOLL_CTL_MOD - Modifying listening events of registered fd
                            EPOLL_CTL_DEL - Delete an fd from epfd
    fd            - fd to monitor
    event        - Events to be monitored
 return:
    Success - return 0
    Failure - Returns - 1. errno indicates an error message
**************************************************************************************************/

// Data structure corresponding to parameter event
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 value
EPOLLIN     // Indicates that the associated fd has a readable operation
EPOLLOUT    // Indicates that the associated fd has a writable operation
EPOLLERR    // Corresponding fd sending error
EPOLLHUP    // The corresponding connection is suspended

3. Collect events that have occurred in the events monitored by the specified epoll: epoll_wait

/*************************************************************************************************
Function: int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
Head: #include
 Function: collect the events that have occurred in the events monitored by the specified epoll
 Parameters:
    epfd        - Through epoll_ Instance created by create()
    events      - Is the assigned epoll_ For the event structure array, epoll will copy the events to the events array (events cannot be null pointers. The kernel is only responsible for copying the data to the events array and will not help us allocate memory in the user state. This method of the kernel is very efficient)
    maxevents   - The maximum number of events that can be returned this time. Generally, the maxevents parameter is equal to the size of the pre allocated events array
    timeout     - Indicates the maximum waiting time (in milliseconds) when no event is detected. If timeout is 0, it will return immediately and will not wait- 1 indicates indefinite blocking
 return:
    > 0         - Number of file descriptors with events
    = 0         - overtime
    -1          - Error, errno indicates error information
**************************************************************************************************/

// Data structure corresponding to the events parameter
struct epoll_event {
    __uint32_t      events;
    epoll_data_t    data;
};

typedef enum epoll_data {
    void         *ptr;
    int           fd;
    uint32_t      u32;
    uint64_t      u64;
} epoll_data_t;

4, Trigger mode

1. Level_triggered

Epoll when a read-write event occurs on the monitored file descriptor_ Wait () notifies the handler to read and write. If the data is not read and written all at once this time (for example, the read-write buffer is too small), epoll will be called next time_ When waiting (), it will also notify you to continue reading and writing on the file descriptor that you haven't finished reading and writing. Of course, if you don't read and write all the time, it will keep notifying you!!! If there are a large number of ready file descriptors in the system that you don't need to read and write, and they will be returned every time, it will greatly reduce the efficiency of the processor to retrieve the ready file descriptors that you care about.

Setting method: triggered horizontally by default

2. Edge_triggered

Epoll when a read-write event occurs on the monitored file descriptor_ Wait () notifies the handler to read and write. If the data is not read and written completely this time (for example, the read-write buffer is too small), epoll will be called next time_ When waiting (), it will not notify you, that is, it will only notify you once until the second read-write event appears on the file descriptor!!! This mode is more efficient than horizontal trigger, and the system will not be filled with a large number of ready file descriptors you don't care about.

Setting method: stat - >_ ev. events = EPOLLIN | EPOLLET

5, Implement a simple web server using Epoll

1. C language source code

#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include <sys/socket.h>

typedef struct connect_stat connect_stat_t;
typedef void (*page_process_func)(connect_stat_t*);

struct connect_stat {
    int fd;
    char name[64];
    char age[64];
    struct epoll_event _ev;
    int status;                 // 0 - not logged in 1 - logged in
    page_process_func handler;  // Processing functions for different pages
};

static int epfd = 0;

static const char* main_header = "HTTP/1.0 200 OK\r\nServer: Martin Server\r\nContent-Type: text/html\r\nConnection: Close\r\n";

// Get default connect_stat_t node
connect_stat_t* get_stat(int fd);
void set_nonblock(int fd);

// The server socket is returned successfully, and - 1 is returned for failure
int init_server(const char *ip, unsigned short port);
// Initialize the connection and wait for the browser to send a request
void add_event_to_epoll(int newfd);

void do_http_request(connect_stat_t* p);
void do_http_respone(connect_stat_t* p);

void welcome_response_handler(connect_stat_t* p);
void commit_respone_handler  (connect_stat_t* p);

int main(int argc, char* argv[]) {
    if (argc < 3) {
        fprintf(stderr, "%s:please input [ip][port]!\n", argv[0]);
        exit(1);
    }

    int sock = init_server(argv[1], atoi(argv[2]));
    if (sock < 0) { exit(2); }

    // 1. Create epoll
    epfd = epoll_create(256);
    if (epfd < 0) {
        fprintf(stderr, "epoll_create(): failed! reason: %s!\n", strerror(errno));
        exit(3);
    }

    struct epoll_event _ev; // epoll structure filling
    connect_stat_t* stat = get_stat(sock);
    _ev.events = EPOLLIN;   // The initial listening event is read
    _ev.data.ptr = stat;

    // trusteeship
    epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &_ev);  //Add the sock to epfd to listen for read events

    struct epoll_event revents[64]; // The array of events when there are events

    int timeout = -1;   // -1 - block indefinitely 0 - return immediately
    int count = 0;      // Number of events
    int done = 0;

    while (!done) {
        switch ((count = epoll_wait(epfd, revents, sizeof(revents)/sizeof(revents[0]), -1)))
        {
        case -1:
            fprintf(stderr, "epoll_wait(): failed! reason: %s!\n", strerror(errno));
            exit(4);
            break;
        case 0:
            fprintf(stderr, "timeout!\n");
            exit(5);
        default:
            for (int i = 0; i < count; ++i) {
                connect_stat_t* p = (connect_stat_t*)revents[i].data.ptr;
                int fd = p->fd;
                // If it is a server fd and a read event, the connection is received
                if (fd == sock && (revents[i].events & EPOLLIN)) {
                    struct sockaddr_in client;
                    int client_len = sizeof(struct sockaddr_in);
                    int newfd = accept(sock, (struct sockaddr*)(&client), &client_len);
                    if (newfd < 0) {
                        fprintf(stderr, "accept(): failed! reason: %s!\n", strerror(errno));
                        continue;
                    }
                    printf("get a new client: %d\n", newfd);
                    add_event_to_epoll(newfd);
                }
                else {  // Processing non server socket s
                    if (revents[i].events & EPOLLIN) {
                        do_http_request((connect_stat_t*)revents[i].data.ptr);
                    }
                    else if(revents[i].events & EPOLLOUT) {
                        do_http_respone((connect_stat_t*)revents[i].data.ptr);
                    }
                    else {

                    }
                }
            }
            break;
        }   // end switch
    }   // end while

    return 0;
}

connect_stat_t* get_stat(int fd) {
    connect_stat_t* p = (connect_stat_t*)malloc(sizeof(connect_stat_t));
    if (!p) { return NULL; }

    bzero(p, sizeof(connect_stat_t));
    p->fd = fd;

    return p;
}

void set_nonblock(int fd) {
    int fl = fcntl(fd, F_GETFL);
    fcntl(fd, F_SETFL, fl | O_NONBLOCK);
}

int init_server(const char* ip, unsigned short port) {
    if (!ip) {
        fprintf(stderr, "func:%s(): Parameter[ip] is empty!\n", __FUNCTION__);
        return -1;
    }

    int ret = 0;
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
        fprintf(stderr, "func:%s() - socket(): failed! reason: %s!\n", __FUNCTION__, strerror(errno));
        return -1;
    }

    int opt = 1;
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    // Bind IP address port number
    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_port = htons(port);
    local.sin_addr.s_addr = inet_addr(ip);
    ret = bind(sock, (struct sockaddr*)(&local), sizeof(local));
    if (ret < 0) {
        fprintf(stderr, "func:%s() - bind(): failed! reason: %s!\n", __FUNCTION__, strerror(errno));
        return -1;
    }

    // monitor
    ret = listen(sock, 5);
    if (ret < 0) {
        fprintf(stderr, "func:%s() - listen(): failed! reason: %s!\n", __FUNCTION__, strerror(errno));
        return -1;
    }

    return sock;
}

void add_event_to_epoll(int newfd) {
    if (newfd < 0) { return; }

    connect_stat_t* p = get_stat(newfd);
    set_nonblock(newfd);

    p->_ev.events = EPOLLIN;
    p->_ev.data.ptr = p;

    epoll_ctl(epfd, EPOLL_CTL_ADD, newfd, &p->_ev);
}

void do_http_request(connect_stat_t* p) {
    if (!p) { return; }

    char buffer[4096] = { 0 };

    ssize_t len = read(p->fd, buffer, sizeof(buffer) - 1);
    if (len == 0) {
        printf("close client[%d] socket!\n", p->fd);
        epoll_ctl(epfd, EPOLL_CTL_DEL, p->fd, NULL);
        close(p->fd);
        free(p);
        return;
    }
    else if (len < 0) {
        fprintf(stderr, "%s() - read(): failed! reason: %s\n", __FUNCTION__, strerror(errno));
        return;
    }

    buffer[len] = '\0';
    printf("recv request data: %s\n", buffer);

    char* pos = buffer;

    if (!strncasecmp(pos, "GET", 3)) {
        p->handler = welcome_response_handler;
    }
    else if (!strncasecmp(pos, "POST", 4)) {
        printf("---data: %s\n", buffer);
        // Get URL
        printf("---post---\n");
        pos += strlen("POST");
        while (*pos == ' ' || *pos == '/') ++pos;

        if (!strncasecmp(pos, "commit", 6)) {
            int len = 0;

            printf("post commit --------\n");
            pos = strstr(buffer, "\r\n\r\n");
            char* end = NULL;
            if (end = strstr(pos, "name=")) {
                pos = end + strlen("name=");
                end = pos;
                while (('a' <= *end && *end <= 'z') || ('A' <= *end && *end <= 'Z') || ('0' <= *end && *end <= '9'))	end++;
                len = end - pos;
                if (len > 0) {
                    memcpy(p->name, pos, end - pos);
                    p->name[len] = '\0';
                }
            }

            if (end = strstr(pos, "age=")) {
                pos = end + strlen("age=");
                end = pos;
                while ('0' <= *end && *end <= '9')	end++;
                len = end - pos;
                if (len > 0) {
                    memcpy(p->age, pos, end - pos);
                    p->age[len] = '\0';
                }
            }
            p->handler = commit_respone_handler;

        }
        else {
            p->handler = welcome_response_handler;
        }
    }
    else {
        p->handler = welcome_response_handler;
    }

    // Generate processing results
    p->_ev.events = EPOLLOUT;
    epoll_ctl(epfd, EPOLL_CTL_MOD, p->fd, &p->_ev);

}

void do_http_respone(connect_stat_t* p) {
    if (!p) { return; }
    p->handler(p);
}

void welcome_response_handler(connect_stat_t* p) {
    int fd = open("./Resources/index.html", O_RDONLY);
    if (fd < 0) {
        fprintf(stderr, "commit_respone_handler() - open: failed! reason: %s\n", strerror(errno));
        return;
    }

    char buffer[4096] = { 0 };
    int rlen = read(fd, buffer, sizeof(buffer));
    close(fd);
    if (rlen < 1) {
        fprintf(stderr, "commit_respone_handler() - read: failed! reason: %s\n", strerror(errno));
        return;
    }

    char* content = (char*)malloc(strlen(main_header) + 128 + rlen);
    char len_buf[64] = { 0 };
    strcpy(content, main_header);
    snprintf(len_buf, sizeof(len_buf), "Content-Length: %d\r\n\r\n", (int)rlen);
    strcat(content, len_buf);
    strcat(content, buffer);
    printf("send reply to client!\n");

    int wlen = write(p->fd, content, strlen(content));
    if (wlen < 1) {
        fprintf(stderr, "commit_respone_handler() - write: failed! reason: %s\n", strerror(errno));
        return;
    }
    
    p->_ev.events = EPOLLIN;
    epoll_ctl(epfd, EPOLL_CTL_MOD, p->fd, &p->_ev);
    free(content);
}

void commit_respone_handler(connect_stat_t* p) {
    int fd = open("./Resources/reply.html", O_RDONLY);
    if (fd < 0) {
        fprintf(stderr, "commit_respone_handler() - open: failed! reason: %s\n", strerror(errno));
        return;
    }

    char buffer[1024] = { 0 };
    int rlen = read(fd, buffer, sizeof(buffer));
    close(fd);
    if (rlen < 1) {
        fprintf(stderr, "commit_respone_handler() - read: failed! reason: %s\n", strerror(errno));
        return;
    }

    char* content = (char*)malloc(strlen(main_header) + 128 + rlen);
    char* tmp = (char*)malloc(rlen + 128);
    char len_buf[64] = { 0 };
    strcpy(content, main_header);
    snprintf(len_buf, sizeof(len_buf), "Content-Length: %d\r\n\r\n", (int)rlen);
    strcat(content, len_buf);
    snprintf(tmp, rlen + 128, buffer, p->name, p->age);
    strcat(content, tmp);
    printf("send reply to client!\n");

    //printf("write data: %s\n", content);
    int wlen = write(p->fd, content, strlen(content));
    if (wlen < 1) {
        fprintf(stderr, "commit_respone_handler() - write: failed! reason: %s\n", strerror(errno));
        return;
    }

    p->_ev.events = EPOLLIN;
    epoll_ctl(epfd, EPOLL_CTL_MOD, p->fd, &p->_ev);
    free(tmp);
    free(content);

}

2.   ./ Resources/index.html source code

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Sign in</title>
    <style type="text/css">
        
html {
    width: 100%;
    height: 100%;
    overflow: hidden;
    font-style: sans-serif;
}

body{
    width: 100%;
    height: 100%;
    font-family: 'Open Sans',sans-serif;
    margin: 0;
    background-color: #4A374A;
}

#login {
    position: absolute;
    top: 50%;
    left: 50%;
    margin: -150px 0 0 -150px;
    width: 300px;
    height: 300px;
}

#login h1{
    color: #fff;
    text-shadow: 0 0 10px;
    letter-spacing: 1px;
    text-align: center;
}

h1 {
    font-size: 2em;
    margin: 0.67em 0;
}

input {
    width: 278px;
    height: 18px;
    margin-bottom: 10px;
    outline: none;
    padding: 10px;
    font-size: 13px;
    color: #fff;
    text-shadow: 1px 1px 1px;
    border-top: 1px solid #31213D;
    border-left: 1px solid #31213D;
    border-right: 1px solid #31213D;
    border-bottom: 1px solid #56536A;
    border-radius: 4px;
    background-color: #2D2D3F;
}

#login .but {
    width: 300px;
    min-height: 20px;
    display: block;
    background-color: #4A77D4;
    border: 1px solid #3762BC;
    color: #FFF;
    padding: 9px 14px;
    font-size: 15px;
    line-height: normal;
    border-radius: 5px;
    margin: 0;
}
</style>
</head>

<body>
    <div id="login">
        <h1>Sign in</h1>
        <form action="commit" method="POST">
            <input type="text" required="required" placeholder="user name" name="name"></input>
            <input type="password" required="required" placeholder="password" name="age"></input>
            <button class="but" type="submit">Sign in</button>
        </form>
    </div>
    
</body>
</html>

3.   ./ Reources/reply.html source code

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Welcome to visit</title>
</head>
<body>
    <div align="center" height="500px" >
        <br/><br/><br/>
        <h2>Welcome to Xueba&nbsp;%s &nbsp;,What is your age&nbsp;%s!</h2><br/><br/>
        </div>
</body>
</html>

6, Use of libraries encapsulated with Epoll

1. Provide timeout kick out function

1. Library source code (globals.h)

#ifndef GLOBALS_H
#define GLOBALS_H

#include <sys/time.h>
#include <sys/resource.h>	
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/stat.h>

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>

#define FD_DESC_SZ		64

#define COMM_OK		  (0)
#define COMM_ERROR	 (-1)
#define COMM_NOMESSAGE	 (-3)
#define COMM_TIMEOUT	 (-4)
#define COMM_SHUTDOWN	 (-5)
#define COMM_INPROGRESS  (-6)
#define COMM_ERR_CONNECT (-7)
#define COMM_ERR_DNS     (-8)
#define COMM_ERR_CLOSING (-9)


//Commissioning related
#define DEBUG_LEVEL  0
#define DEBUG_ONLY   8
#define debug(m, n)    if( m >= DEBUG_LEVEL && n <= DEBUG_ONLY  ) printf
	
#define safe_free(x)	if (x) { free(x); x = NULL; }

typedef void PF(int, void *);

struct _fde {
    unsigned int type;
    u_short local_port;
    u_short remote_port;
    struct in_addr local_addr;
 
    char ipaddr[16];		/* dotted decimal address of peer */
   
   
    PF *read_handler;
    void *read_data;
    PF *write_handler;
    void *write_data;
    PF *timeout_handler;
    time_t timeout;
    void *timeout_data;
};

typedef struct _fde fde;

extern fde *fd_table;		// For example, if fd=1, the access method is fd_table[1]
extern int Biggest_FD;		

/*The system is time-dependent and set as a global variable for use by all modules*/
extern struct timeval current_time;
extern double current_dtime;
extern time_t sys_curtime;


/* epoll Related interface implementation */
extern void do_epoll_init(int max_fd);
extern void do_epoll_shutdown();
extern void epollSetEvents(int fd, int need_read, int need_write);
extern int do_epoll_select(int msec);

/*Frame peripheral interface*/
void comm_init(int max_fd);
extern int comm_select(int msec);
extern inline void comm_call_handlers(int fd, int read_event, int write_event);
void  commUpdateReadHandler(int fd, PF * handler, void *data);
void commUpdateWriteHandler(int fd, PF * handler, void *data);



extern const char *xstrerror(void);
int ignoreErrno(int ierrno);

#endif /* GLOBALS_H */

2. Implementation files of epoll related interfaces (comm_epoll.c)

#include "globals.h"
#include <sys/epoll.h>

#define MAX_EVENTS 	 two hundred and fifty-six 	/*  The largest event processed at a time*/

/* epoll structs */
static int kdpfd;
static struct epoll_event events[MAX_EVENTS];
static int epoll_fds = 0;
static unsigned *epoll_state;	/* Save the event state of each epoll */

static const char *
epolltype_atoi(int x)
{
    switch (x) {

    case EPOLL_CTL_ADD:
	return "EPOLL_CTL_ADD";

    case EPOLL_CTL_DEL:
	return "EPOLL_CTL_DEL";

    case EPOLL_CTL_MOD:
	return "EPOLL_CTL_MOD";

    default:
	return "UNKNOWN_EPOLLCTL_OP";
    }
}

void do_epoll_init(int max_fd)
{

    
    kdpfd = epoll_create(max_fd);
    if (kdpfd < 0)
	  fprintf(stderr,"do_epoll_init: epoll_create(): %s\n", xstrerror());
    //fd_open(kdpfd, FD_UNKNOWN, "epoll ctl");
    //commSetCloseOnExec(kdpfd);

    epoll_state = calloc(max_fd, sizeof(*epoll_state));
}


void do_epoll_shutdown()
{
    
    close(kdpfd);
    kdpfd = -1;
    safe_free(epoll_state);
}



void epollSetEvents(int fd, int need_read, int need_write)
{
    int epoll_ctl_type = 0;
    struct epoll_event ev;

    assert(fd >= 0);
    debug(5, 8) ("commSetEvents(fd=%d)\n", fd);

	memset(&ev, 0, sizeof(ev));
    
    ev.events = 0;
    ev.data.fd = fd;

    if (need_read)
	ev.events |= EPOLLIN;

    if (need_write)
	ev.events |= EPOLLOUT;

    if (ev.events)
	ev.events |= EPOLLHUP | EPOLLERR;

    if (ev.events != epoll_state[fd]) {
	/* If the struct is already in epoll MOD or DEL, else ADD */
	if (!ev.events) {
	    epoll_ctl_type = EPOLL_CTL_DEL;
	} else if (epoll_state[fd]) {
	    epoll_ctl_type = EPOLL_CTL_MOD;
	} else {
	    epoll_ctl_type = EPOLL_CTL_ADD;
	}

	/* Update the state */
	epoll_state[fd] = ev.events;

	if (epoll_ctl(kdpfd, epoll_ctl_type, fd, &ev) < 0) {
	    debug(5, 1) ("commSetEvents: epoll_ctl(%s): failed on fd=%d: %s\n",
		epolltype_atoi(epoll_ctl_type), fd, xstrerror());
	}
	switch (epoll_ctl_type) {
	case EPOLL_CTL_ADD:
	    epoll_fds++;
	    break;
	case EPOLL_CTL_DEL:
	    epoll_fds--;
	    break;
	default:
	    break;
	}
    }
}

int do_epoll_select(int msec)
{
    int i;
    int num;
    int fd;
    struct epoll_event *cevents;

    /*if (epoll_fds == 0) {
	assert(shutting_down);
	return COMM_SHUTDOWN;
    }
    statCounter.syscalls.polls++;
    */
    num = epoll_wait(kdpfd, events, MAX_EVENTS, msec);
    if (num < 0) {
	getCurrentTime();
	if (ignoreErrno(errno))
	    return COMM_OK;

	debug(5, 1) ("comm_select: epoll failure: %s\n", xstrerror());
	return COMM_ERROR;
    }
    //statHistCount(&statCounter.select_fds_hist, num);

    if (num == 0)
	return COMM_TIMEOUT;

    for (i = 0, cevents = events; i < num; i++, cevents++) {
		fd = cevents->data.fd;
		comm_call_handlers(fd, cevents->events & ~EPOLLOUT, cevents->events & ~EPOLLIN);
    }

    return COMM_OK;
}

3. Implementation of external related interfaces (comm.c)

#include "globals.h"

double current_dtime;
time_t sys_curtime;
struct timeval current_time;


int Biggest_FD = 1024;  /*The default maximum number of file descriptors is 1024*/
static int MAX_POLL_TIME = 1000;	/* see also comm_quick_poll_required() */
fde *fd_table = NULL;	

time_t getCurrentTime(void)
{
    gettimeofday(&current_time, NULL);
    current_dtime = (double) current_time.tv_sec +
	(double) current_time.tv_usec / 1000000.0;
    return sys_curtime = current_time.tv_sec;
}


void
comm_close(int fd)
{
	assert(fd>0);
	fde *F = &fd_table[fd];
	if(F) memset((void *)F,'\0',sizeof(fde));
	epollSetEvents(fd, 0, 0);
	close(fd);
}

void
comm_init(int max_fd)
{
	if(max_fd > 0 ) Biggest_FD = max_fd;
	fd_table = calloc(Biggest_FD, sizeof(fde));
    do_epoll_init(Biggest_FD);
}



void
comm_select_shutdown(void)
{
    do_epoll_shutdown();
	if(fd_table) free(fd_table);
}


//static int comm_select_handled;


inline void
comm_call_handlers(int fd, int read_event, int write_event)
{
    fde *F = &fd_table[fd];
    
    debug(5, 8) ("comm_call_handlers(): got fd=%d read_event=%x write_event=%x F->read_handler=%p F->write_handler=%p\n"
	,fd, read_event, write_event, F->read_handler, F->write_handler);
    if (F->read_handler && read_event) {
	    PF *hdl = F->read_handler;
	    void *hdl_data = F->read_data;
	    /* If the descriptor is meant to be deferred, don't handle */

		debug(5, 8) ("comm_call_handlers(): Calling read handler on fd=%d\n", fd);
		//commUpdateReadHandler(fd, NULL, NULL);
		hdl(fd, hdl_data);
    }
	
    if (F->write_handler && write_event) {
	
	    PF *hdl = F->write_handler;
	    void *hdl_data = F->write_data;
	
	    //commUpdateWriteHandler(fd, NULL, NULL);
	    hdl(fd, hdl_data);
    }
}


int
commSetTimeout(int fd, int timeout, PF * handler, void *data)
{
    fde *F;
    debug(5, 3) ("commSetTimeout: FD %d timeout %d\n", fd, timeout);
    assert(fd >= 0);
    assert(fd < Biggest_FD);
    F = &fd_table[fd];

	
    if (timeout < 0) {
	F->timeout_handler = NULL;
	F->timeout_data = NULL;
	return F->timeout = 0;
    }
    assert(handler || F->timeout_handler);
    if (handler || data) {
	F->timeout_handler = handler;
	F->timeout_data = data;
    }
    return F->timeout = sys_curtime + (time_t) timeout;
}

void
commUpdateReadHandler(int fd, PF * handler, void *data)
{
    fd_table[fd].read_handler = handler;
    fd_table[fd].read_data = data;
    
    epollSetEvents(fd,1,0); 
}

void
commUpdateWriteHandler(int fd, PF * handler, void *data)
{
    fd_table[fd].write_handler = handler;
    fd_table[fd].write_data = data;
	
    epollSetEvents(fd,0,1); 
}



static void
checkTimeouts(void)
{
    int fd;
    fde *F = NULL;
    PF *callback;

    for (fd = 0; fd <= Biggest_FD; fd++) {
	F = &fd_table[fd];
	/*if (!F->flags.open)
	    continue;
	*/
	
	if (F->timeout == 0)
	    continue;
	if (F->timeout > sys_curtime)
	    continue;
	debug(5, 5) ("checkTimeouts: FD %d Expired\n", fd);
	
	if (F->timeout_handler) {
	    debug(5, 5) ("checkTimeouts: FD %d: Call timeout handler\n", fd);
	    callback = F->timeout_handler;
	    F->timeout_handler = NULL;
	    callback(fd, F->timeout_data);
	} else {
	    debug(5, 5) ("checkTimeouts: FD %d: Forcing comm_close()\n", fd);
	    comm_close(fd);
	}
    }
}


int
comm_select(int msec)
{
    static double last_timeout = 0.0;
    int rc;
    double start = current_dtime;

    debug(5, 3) ("comm_select: timeout %d\n", msec);

    if (msec > MAX_POLL_TIME)
	msec = MAX_POLL_TIME;


    //statCounter.select_loops++;
    /* Check timeouts once per second */
    if (last_timeout + 0.999 < current_dtime) {
	last_timeout = current_dtime;
	checkTimeouts();
    } else {
	int max_timeout = (last_timeout + 1.0 - current_dtime) * 1000;
	if (max_timeout < msec)
	    msec = max_timeout;
    }
    //comm_select_handled = 0;

    rc = do_epoll_select(msec);


    getCurrentTime();
    //statCounter.select_time += (current_dtime - start);

    if (rc == COMM_TIMEOUT)
	debug(5, 8) ("comm_select: time out\n");

    return rc;
}


const char *
xstrerror(void)
{
    static char xstrerror_buf[BUFSIZ];
    const char *errmsg;

    errmsg = strerror(errno);

    if (!errmsg || !*errmsg)
	errmsg = "Unknown error";

    snprintf(xstrerror_buf, BUFSIZ, "(%d) %s", errno, errmsg);
    return xstrerror_buf;
}


int
ignoreErrno(int ierrno)
{
    switch (ierrno) {
    case EINPROGRESS:
    case EWOULDBLOCK:
#if EAGAIN != EWOULDBLOCK
    case EAGAIN:
#endif
    case EALREADY:
    case EINTR:
#ifdef ERESTART
    case ERESTART:
#endif
	return 1;
    default:
	return 0;
    }
    /* NOTREACHED */
}


4. Use the library to implement the recovery server (main.c)

#include "globals.h"
typedef struct _ConnectStat  ConnectStat;

#define BUFLEN  1024

struct _ConnectStat {
	int fd;
	char send_buf[BUFLEN];
	PF *handler;//Processing functions for different pages
};

//echo service implementation related code
ConnectStat * stat_init(int fd);
void accept_connection(int fd, void *data);
void do_echo_handler(int fd, void  *data);
void do_echo_response(int fd,void *data);
void do_echo_timeout(int fd, void *data);



void usage(const char* argv)
{
	printf("%s:[ip][port]\n", argv);
}

void set_nonblock(int fd)
{
	int fl = fcntl(fd, F_GETFL);
	fcntl(fd, F_SETFL, fl | O_NONBLOCK);
}

int startup(char* _ip, int _port)  //Create a socket, bind, and detect the server
{
	//sock
	//1. Create socket
	int sock = socket(AF_INET, SOCK_STREAM, 0);
	if (sock < 0)
	{
		perror("sock");
		exit(2);
	}

	int opt = 1;
	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

	//2. Fill in local sockaddr_in structure (set local IP address and port)
	struct sockaddr_in local;
	local.sin_port = htons(_port);
	local.sin_family = AF_INET;
	local.sin_addr.s_addr = inet_addr(_ip);

	//3.bind() binding
	if (bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0)
	{
		perror("bind");
		exit(3);
	}
	//4.listen() monitoring and detection server
	if (listen(sock, 5) < 0)
	{
		perror("listen");
		exit(4);
	}
	
	return sock;    //Such sockets return
}


ConnectStat * stat_init(int fd) {
	ConnectStat * temp = NULL;
	temp = (ConnectStat *)malloc(sizeof(ConnectStat));

	if (!temp) {
		fprintf(stderr, "malloc failed. reason: %m\n");
		return NULL;
	}

	memset(temp, '\0', sizeof(ConnectStat));
	temp->fd = fd;
	//temp->status = 0;
}

void do_welcome_handler(int fd, void  *data) {
	const char * WELCOME= "Welcome.\n";
	int wlen = strlen(WELCOME);
	int n ;
	ConnectStat * stat = (ConnectStat *)(data);
	
	if( (n = write(fd, "Welcome.\n",wlen)) != wlen ){
		
		if(n<=0){
		    fprintf(stderr, "write failed[len:%d], reason: %s\n",n,strerror(errno));
		}else fprintf(stderr, "send %d bytes only ,need to send %d bytes.\n",n,wlen);
		
	}else {
		commUpdateReadHandler(fd, do_echo_handler,(void *)stat);
		commSetTimeout(fd, 10, do_echo_timeout, (void *)stat);
	}
}


void do_echo_handler(int fd, void  *data) {
	ConnectStat * stat = (ConnectStat *)(data);
	char * p = NULL;
	
	assert(stat!=NULL);
	
	
	p = stat->send_buf;
	*p++ = '-';
	*p++ = '>';
	ssize_t _s = read(fd, p, BUFLEN-(p-stat->send_buf)-1); //2 bytes "- >" + character terminator
    if (_s > 0)
    {
		
		*(p+_s) = '\0';
		printf("receive from client: %s\n", p);
		//_s--;
		//while( _s>=0 && ( stat->send_buf[_s]=='\r' || stat->send_buf[_s]=='\n' ) ) stat->send_buf[_s]='\0';
		
		if(!strncasecmp(p, "quit", 4)){//sign out.
			comm_close(fd);
                        free(stat);
			return ;
		}
		//write(fd,
		commUpdateWriteHandler(fd, do_echo_response, (void *)stat);
		commSetTimeout(fd, 10, do_echo_timeout, (void *)stat);
	}else if (_s == 0)  //client:close
    {
        fprintf(stderr,"Remote connection[fd: %d] has been closed\n", fd);
        comm_close(fd);
        free(stat);
    }
    else //err occurred.
    {
        fprintf(stderr,"read faield[fd: %d], reason:%s [%d]\n",fd , strerror(errno), _s);
    }
}

void do_echo_response(int fd, void  *data) {
	ConnectStat * stat = (ConnectStat *)(data);
	int len = strlen(stat->send_buf);
	int _s = write(fd, stat->send_buf, len);
	
	if(_s>0){
		commSetTimeout(fd, 10, do_echo_timeout, (void *)stat);
		commUpdateReadHandler(fd, do_echo_handler, (void *)stat);
		
	}else if(_s==0){
		fprintf(stderr,"Remote connection[fd: %d] has been closed\n", fd);
                comm_close(fd);
                free(stat);
	}else {
		fprintf(stderr,"read faield[fd: %d], reason:%s [%d]\n",fd ,_s ,strerror(errno));
	}
}

//read()
//Register write events
  
//Write event ready
//write()

void accept_connection(int fd, void *data){
	struct sockaddr_in peer;
	socklen_t len = sizeof(peer);

	ConnectStat * stat = (ConnectStat *)data;
	
	int new_fd = accept(fd, (struct sockaddr*)&peer, &len);

	if (new_fd > 0)
	{
		ConnectStat *stat = stat_init(new_fd);
		set_nonblock(new_fd);

		printf("new client: %s:%d\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port));
		commUpdateWriteHandler(new_fd, do_welcome_handler, (void *)stat);
		commSetTimeout(new_fd, 30,do_echo_timeout, (void *)stat);
	}
}

void do_echo_timeout(int fd, void *data){
	fprintf(stdout,"---------timeout[fd:%d]----------\n",fd);
	comm_close(fd);
        free(data);
}



int main(int argc,char **argv){

	if (argc != 3)     //Check whether the number of parameters is correct
	{
		usage(argv[0]);
		exit(1);
	}

	int listen_sock = startup(argv[1], atoi(argv[2]));      //Create a socket descriptor that binds the local ip and port number
	//Initialize the asynchronous event handling framework epoll
	
	comm_init(102400);
	
	ConnectStat * stat = stat_init(listen_sock);
	commUpdateReadHandler(listen_sock,accept_connection,(void *)stat);

	do{
		//Continuously cycle events
		comm_select(1000);
		
	}while(1==1);

	comm_select_shutdown();



}




Attachment: query and modification of default maximum file descriptor under Linux

1. Query the maximum value of file descriptor:

ulimit -a (query all)

ulimit -n (query the maximum openable file descriptor)

2. Modify the maximum value of the file descriptor:

(1) ulimit -n 102400 (this method is applicable to single use of a single terminal)

(2) edit / etc / RC Local file, the steps are as follows:

          a. vi /etc/rc.local

b. enter ulimit -n 102400

c. save and exit

Topics: Linux epoll