[network socket] temperature reporting based on poll and epoll communication

poll function

poll is a function in the character device driver in Linux. poll is essentially no different from select. It copies the array passed in by the user to the kernel space, and then queries the device status corresponding to each fd. If the device is ready, add an item to the device waiting queue and continue to traverse. If no ready device is found after traversing all fd, suspend the current process, Until the device is ready or active timeout, it will traverse fd again after being awakened. This process has gone through many unnecessary traversals.

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

parameter:
pollfd:Point to a struct pollfd Array of types, each pollfd Structure specifies a monitored file descriptor, indicating poll()Monitor multiple file descriptors. Of each structure events Domain is the event mask that monitors the file descriptor. This domain is set by the user. revents Domain is the operation result event mask of the file descriptor. The kernel sets this domain when the call returns, events Any event requested in the domain may be revents Field.

nfds Specifies the number of listening elements in the array.
    
timeout Specifies the number of milliseconds to wait, regardless of I/O Are you ready, poll Will return. timeout Specify a negative value to indicate an infinite timeout so that poll()
Hang until a specified event occurs; timeout 0 indicates poll Call returns immediately and lists ready I/O File descriptor, but does not wait for other events.
struct pollfd
{
 int fd; /* File descriptor */
 short events; /* Waiting events */
 short revents; /* What actually happened */
} ;

The following table lists some constant values of the specified events flag and the test events flag:

Timeout: when the function is called successfully, poll() returns the number of file descriptors whose events field in the structure is not 0; If no event occurs before the timeout, poll() returns 0; On failure, poll() returns - 1 and sets errno to one of the following values:

EBADF  	 The file descriptor specified in one or more structures is invalid.  
EFAULTfds The address pointed to by the pointer exceeds the address space of the process.  
EINTR   A signal is generated before the requested event, and the call can be restarted.  
EINVALnfds Parameter exceeded PLIMIT_NOFILE Value.  
ENOMEM    There is not enough available memory to complete the request.

epoll function

epoll operates on file descriptors in two modes: LT (level trigger) and ET (edge trigger). LT mode is the default mode. The differences between LT mode and et mode are as follows:

Horizontal trigger LT mode:

  • The main feature of horizontal trigger is that if the user is listening for epoll events, it will be copied when there are events in the kernel
    Give user status events, but if the user only processes them once, the remaining unprocessed events will be processed next time
    epoll_wait returns the event again.
    In this way, if the user never handles this event, there will be a copy of the event from the kernel to the user every time
    Bei, it consumes performance, but the horizontal trigger is relatively safe. At least, the event will not be lost unless the user handles it
    Finish

Edge triggered ET mode:

  • The edge trigger is opposite to the horizontal trigger. When an event arrives in the kernel, the user will be notified only once. As for the use of
    Whether the user handles it or not will not be notified in the future. This reduces the copy process and increases performance, but
    Relatively speaking, if the user forgets to handle it carelessly, the event will be lost.

The edge trigger is triggered only once, and the horizontal trigger will be triggered all the time.

int epoll_create(int size);
function:
establish epoll
 parameter:
	size Ignore not
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *ev);
function:modify epoll Addition, deletion, modification and query of
 parameter:
	epfd yes epoll_create Return value
    op Is used to specify the operation to be performed
    fd:Indicates the setting of which file descriptor in the interest list to modify. This parameter can be representative of pipeline FIFO,Socket POSIX Message queuing inotify Instance, terminal, device, or even another epoll File descriptor for instance
    ev:Pointer structure of

op specifies the values of operations: add, delete, and modify listening to fd

EPOLL_CTL_ADDAdd fd to epoll
EPOLL_CTL_MODModifying registered fd events
EPOLL_CTL_DEepfd delete an fd

Structure epoll_ The pointer to event and the structure are defined as follows:

typedef union epoll_data
{
 void *ptr; //Pointer to user-defined data
 int fd; //File descriptor
 uint32_t u32; //32-bit integer 
 uint64_t u64; //64 bit integer
} epoll_data_t;

struct epoll_event
{
 uint32_t events; //What properties are set
 epoll_data_t data; //user data
};
int epoll_wait(int epfd, struct epoll_event *evlist, int maxevents, int timeout);
function:
Wait for ready event
 parameter:
	epfd yes epoll_create()Return value of
    evlist The information about the ready state file descriptor is returned in the structure array pointed to evlist The caller is responsible for applying for the space;
   	maxevents Designated Office evlist The number of elements contained in the array;
	timeout Used to determine epoll_wait()There are several blocking behaviors:
    If timeout be equal to-1,The call will block until an event is generated on the file descriptor in the interest list or until a signal is captured.
	If timeout Equal to 0, perform a non blocking check to see which event is generated on the descriptor in the interest list.
	If timeout Greater than 0, the call will block at most timeout Milliseconds until an event occurs on the file descriptor or until a signal is captured

The values in events are as follows:

constantexplainAs epoll_ Input of ctl()As epoll_ Return of wait()
EPOLLINNon high priority data can be readcancan
EPOLLPRIHigh priority data can be readcancan
EPOLLRDHUPsocket end-to-end shutdowncancan
EPOLLOUTCommon data writablecancan
EPOLLETUse edge triggered event notificationcan
EPOLLONESHOTDisable checking after event notificationcan
EPOLLERRAn error occurredcan
POLLHUPHang up occurredcan

Advantages of epoll

  • Support a process to open a large number of socket descriptors (fd)
  • IO efficiency does not decrease linearly with the increase of fd number
  • Using mmap to speed up message passing between kernel and user space
  • Kernel tuning

poll code implementation

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <getopt.h>
#include <stdlib.h>
#include <sys/select.h>
#include <ctype.h>
#include <libgen.h>
#include <poll.h>

#define ARRAY_ Size (x) (sizeof (x) / sizeof (x [0]) / / find the number of array elements


int socket_Server_init(char *listen_ip,int listen_port);
void sqlite_tem(char *buf);

void print_usage(char *progname)
{
     printf("%s usage: \n", progname);
     printf("-p(--port): sepcify server listen port.\n");
     printf("-h(--Help): print this help information.\n");
     printf("-d(--daemon):set program running on background\n");
     return ;
}

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

    int                 listenfd = -1;
    int                 clifd;
    struct sockaddr_in  servaddr;
    struct sockaddr_in  cliaddr;
    socklen_t           len;
    int                serv_port = 0;
    int                 ch;
    int                 rv ;
    int                 on = 1;
    
    char                buf[1024];
    int                 i,j;
    int                 found;
    int                 max;
    int                 daemon_run = 0;
    char                *progname = NULL;
    struct pollfd       fds_arry[1024];
    
struct option opts[] =
{
     {"daemon",no_argument,NULL,'b'},
     {"port", required_argument, NULL, 'p'},
     {"help", no_argument, NULL, 'h'},
     {NULL, 0, NULL, 0}
};
progname = basename(argv[0]);

 while( (ch=getopt_long(argc, argv, "bp:h", opts, NULL)) != -1 )
      {
           switch(ch)
                {
                    case 'p':
                        serv_port=atoi(optarg);
                        break;
                    case 'b':
                        daemon_run=1;
                        break;
                    case 'h':
                        print_usage(argv[0]);
                        return 0;
                }
    }
  if( !serv_port )
  {
      print_usage(argv[0]);
      return 0;
  }
    if( (listenfd = socket_Server_init(NULL, serv_port)) <0)
    {
        printf("ERROR %s server listen on port %d failure\n",argv[0],serv_port);
        return -1;
    }

    printf("%s server start to listen on port %d\n",argv[0],serv_port);

    
    if(daemon_run)//Set process background running
    {
        daemon(0,0);
    }
    
    for(i=0; i<ARRAY_SIZE(fds_arry); i++)//Traversal array
    {
        fds_arry[i].fd=-1;//Initialize the entire array to - 1. Why - 1; Because this array stores file descriptors, the system will generate three file descriptors 0, 1 and 2
    }   
    fds_arry[0].fd = listenfd;//Assign the first array to listenfd
    //A tcp network link contains a quadruple: source ip, destination ip, source port, destination port
    fds_arry[0].events = POLLIN;
    max = 0;

    for( ; ; )
    {
        
        rv = poll(fds_arry, max+1, -1);
    if(rv < 0)
    {
        printf("POLL failure:%s\n",strerror(errno));
        break;
    }
    else if( rv ==0 )
    {
        printf("poll get timeout\n");
        continue;
    }
    if( fds_arry[0].revents & POLLIN )//Determines whether the specified descriptor is in the collection
    {
        if( (clifd=accept(listenfd,(struct sockaddr *)NULL,NULL)) < 0)
        {
            printf("accept new client failure:%s\n",strerror(errno));
            continue;
        }

        found = 0;
        for(i=0; i<ARRAY_SIZE(fds_arry);i++)
        {
            if( fds_arry[i].fd< 0 )
            {
                printf("accept new client [%d] and add it into array\n",clifd);
                fds_arry[i].fd = clifd;
                fds_arry[i].events = POLLIN;
                found = 1;
                break;
            }
        }
        if(!found)
        {
            printf("accept new client [%d] but full, so refuse it\n",clifd);
            close(clifd);
            continue;
        }
        max = i>max ? i:max;
        if( rv <=0 )
            continue;
    }
    else
    {
        for ( i=1; i<ARRAY_SIZE(fds_arry);i++)
        {
            if(fds_arry[i].fd < 0)
                continue;
            if( (rv=read(fds_arry[i].fd,buf,sizeof(buf))) <=0)
            {
                printf("socket [%d] read failure or get disconnected\n",fds_arry[i].fd);
                close(fds_arry[i].fd);
                fds_arry[i].fd=-1;
            }
            else
            {
                printf("%s\n",buf);
                sqlite_tem(buf);
                printf("Database inserted successfully!\n"); 
            }
        }
    }
}
cleanup:
    close(listenfd);
    return 0;
}



int socket_Server_init(char *listen_ip, int listen_port)
{

    struct sockaddr_in  servaddr;
    int     rv = 0;
    int     on = 1;
    int     listenfd;

    if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("use socket()to create a TCP socket failure:%s\n",strerror(errno));
                return -1;
    }
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

    memset(&servaddr,0,sizeof(servaddr));
    servaddr.sin_family=AF_INET;
    servaddr.sin_port = htons(listen_port);

    if( !listen_ip )
    {
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    }
    else
    {
        if(inet_pton(AF_INET, listen_ip, &servaddr.sin_addr) <=0)
        {
            printf("inet_pton set listen IP address failure\n");
            rv = -2;
            goto cleanup;
        }
    }
    
    if( bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) < 0)
    {
        printf("socket[%d] bind to port failure:%s\n",listenfd,strerror(errno));
        rv = -3;
        goto cleanup;
    }
      
    if( listen(listenfd,13) < 0)
    {
         printf("use bind to bind tcp socket failure:%s\n",strerror(errno));
         rv = -4;
         goto cleanup;
    }
    
cleanup:
    if(rv < 0)
        close(listenfd);
    else
        rv = listenfd;

    return rv;
}

void sqlite_tem(char *buf)
{  
    int             nrow=0;
    int             ncolumn = 0;
    char          **azResult=NULL;
    int             rv; 
    sqlite3        *db=NULL;
    char           *zErrMsg = 0;
    char            sql1[100];
    char           *ipaddr=NULL;
    char           *datetime=NULL;
    char           *temper=NULL;
    char           *sql = "create table if not exists temperature(ipaddr char(30), datetime char(50), temper  char(30))"; 


    ipaddr = strtok(buf,"/");
    datetime = strtok(NULL, "/");
    temper = strtok(NULL, "/");

    rv = sqlite3_open("tempreture.db", &db);
    if(rv)
    {
        printf("Can't open database:%s\n", sqlite3_errmsg(db));
        sqlite3_close(db);
        return;
    }
    printf("opened a sqlite3 database named tempreture.db successfully!\n");



    int ret = sqlite3_exec(db,sql, NULL, NULL, &zErrMsg);
    if(ret != SQLITE_OK)
    {
        printf("create table fail: %s\n",zErrMsg);
    }

    if(snprintf(sql1,sizeof(sql1), "insert into temper values('%s','%s','%s')", ipaddr, datetime, temper) < 0)
    {
        printf("Failed to write data\n");
    }
    //printf("Write data successfully!\n");

    sqlite3_exec(db, sql1, 0, 0, &zErrMsg);
    sqlite3_free(zErrMsg);
    sqlite3_close(db);
    return;
}

epoll code implementation

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


#include "sqlite3.h"

#define BACKLOG 13
#define MAX_EVENTS  512


void print_usage(char* progname);
void sig_stop(int signum);
void sqlite_tem(char *buf);
int socket_listen(char *listen_ip, int port);


static int g_stop = 0;

int main(int argc, char* argv[])
{
    int                     rv;
    int                     ret;
    int                     opt;
    int                     idx;
    int                     port;
    int                     log_fd;
    int                     ch = 1;
    int                     daemon_run = 0;

    int                     ser_fd = -1;
    int                     cli_fd = -1;
    struct sockaddr_in      cli_addr;
    socklen_t               cliaddr_len = 20;
    int                     epollfd;
    struct epoll_event      event;
    struct epoll_event      event_array[MAX_EVENTS];
    int                     events;
    int                     found;
    int                     a;
    int                     i;

    char                    *zErrMsg;
    sqlite3                 *db;
    char                    buf[1024];
   
    
    
    struct option            opts[] = {
            {"daemon", no_argument, NULL, 'd'},
            {"port", required_argument, NULL, 'p'},
            {"help", no_argument, NULL, 'h'},
            {NULL, 0, NULL, 0}
    };
    while ((opt = getopt_long(argc, argv, "dp:h", opts, &idx)) != -1)
    {
        switch (opt)
        {
        case 'd':
            daemon_run = 1;
            break;
        case 'p':
            port = atoi(optarg); 
            break;
        case 'h':
            print_usage(argv[0]);	
            return 0;
        }
    }

   
    if (!port)
    {
        print_usage(argv[0]);
        return 0;
    }

    /*Create log*/
    if (daemon_run)
    {
        printf("Program %s is running at the background now\n", argv[0]);

        
        log_fd = open("receive_temper.log", O_CREAT | O_RDWR, 0666);
        if (log_fd < 0)
        {
            printf("Open the logfile failure : %s\n", strerror(errno));
            return 0;
        }

        
        dup2(log_fd, STDOUT_FILENO);
        dup2(log_fd, STDERR_FILENO);

        
        if ((daemon(1, 1)) < 0)
        {
            printf("Deamon failure : %s\n", strerror(errno));
            return 0;
        }
    }

    /*Installation signal*/
    signal(SIGUSR1, sig_stop);

    /*Call socket*/
    if( (ser_fd = socket_listen(NULL, port)) < 0 )
    {
        printf("ERROR: %s server listen on serv_port %d failure\n", argv[0], port);
        return -2;
    }
    printf("server start to listen on serv_port %d\n",  port);


    /*Create opll*/
    if ((epollfd = epoll_create(MAX_EVENTS)) < 0)
    {
        printf("epoll_create failure:%s\n", strerror(errno));
        return 0;
    }

    event.events = EPOLLIN;
    event.data.fd = ser_fd;

    if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ser_fd, &event) < 0)
    {
        printf("epoll add ser_fd failure:%s\n", strerror(errno));
        return 0;
    }

    while (!g_stop)
    {
        events = epoll_wait(epollfd, event_array, MAX_EVENTS, -1);
        if (events < 0)
        {
            printf("epoll failure:%s\n", strerror(errno));
            break;
        }
        else if (events == 0)
        {
            printf("epoll get timeout\n");
            continue;
        }

        for (i = 0;i < events;i++)
        {
            if ((event_array[i].events & EPOLLERR) || (event_array[i].events & EPOLLHUP))
            {
                printf("epoll_wait get error on fd[%d]:%s\n", event_array[i].data.fd, strerror(errno));
                epoll_ctl(epollfd, EPOLL_CTL_DEL, event_array[i].data.fd, NULL);
                close(event_array[i].data.fd);
            }

            if (event_array[i].data.fd == ser_fd)
            {
                cli_fd = accept(ser_fd, (struct sockaddr*) & cli_addr, &cliaddr_len);
                if (cli_fd < 0)
                {
                    printf("Accept the request from client failure:%s\n", strerror(errno));
                    continue;
                }
                event.data.fd = cli_fd;
                event.events = EPOLLIN;
                if (epoll_ctl(epollfd, EPOLL_CTL_ADD, cli_fd, &event) < 0)
                {
                    printf("epoll add client socket failure:%s\n", strerror(errno));
                    close(cli_fd);
                    continue;
                }
            }
            else
            {
                memset(buf, 0, sizeof(buf));

                a = read(cli_fd, buf, sizeof(buf));		
                if (a < 0)		
                {
                    printf("Read information from client failure:%s\n", strerror(errno));
                    close(cli_fd);
                    exit(0);
                }
                else if (a == 0)		
                {
                    printf("The connection with client has broken!\n");
                    close(cli_fd);
                    exit(0);
                }
                else		
                {
                    printf("%s\n",buf);
                    sqlite_tem(buf);
                    printf("Database inserted successfully!\n");  
                }
                
            }
        }
    }
    close(ser_fd);

    return 0;
}


/*Help information*/
void print_usage(char* progname)
{
    printf("-d(--daemon):let program run in the background.\n");
    printf("-p(--port):enter server port.\n");
    printf("-h(--help):print this help information.\n");

    return;
}

/*Signal function*/
void sig_stop(int signum)
{
    if (SIGUSR1 == signum) 
    {
        g_stop = 1;		
    }

    return;
}

/*socket function*/
int socket_listen(char *listen_ip, int port)
{
    int                     rv = 0;
    int                     on = 1;
    int                     ser_fd;
    struct sockaddr_in      servaddr;

    if ( (ser_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
    {   
        printf("Use socket() to create a TCP socket failure: %s\n", strerror(errno));
        return -1; 
    }   

    setsockopt(ser_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(port);


    if( !listen_ip )
    {   
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    }   
    else
    {   
        if( inet_pton(AF_INET, listen_ip, &servaddr.sin_addr) <= 0 )
        {   
            printf("Inet_pton() set listen IP address failure\n");
            rv = -2; 
            goto cleanup;
        }   
    }

    if( bind(ser_fd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 )
    {   
        printf("Use bind() to bind the TCP socket failure: %s\n", strerror(errno));
        rv = -3; 
        goto cleanup;
    }

    if( listen(ser_fd, 64) < 0 )
    {   
        printf("Use bind() to bind the TCP socket failure: %s\n", strerror(errno));
        rv = -4; 
        goto cleanup;
    }   

cleanup:
    if( rv < 0 )
        close(ser_fd);
    else
        rv = ser_fd;
    return rv;
}

/*database*/
void sqlite_tem(char *buf)
{  
    int             nrow=0;
    int             ncolumn = 0;
    char          **azResult=NULL;
    int             rv; 
    sqlite3        *db=NULL;
    char           *zErrMsg = 0;
    char            sql1[100];
    char           *ipaddr=NULL;
    char           *datetime=NULL;
    char           *temper=NULL;
    char           *sql = "create table if not exists temperature(ipaddr char(30), datetime char(50), temper  char(30))"; 


    ipaddr = strtok(buf,"/");
    datetime = strtok(NULL, "/");
    temper = strtok(NULL, "/");

    rv = sqlite3_open("tempreture.db", &db);
    if(rv)
    {
        printf("Can't open database:%s\n", sqlite3_errmsg(db));
        sqlite3_close(db);
        return;
    }
    printf("opened a sqlite3 database named tempreture.db successfully!\n");



    int ret = sqlite3_exec(db,sql, NULL, NULL, &zErrMsg);
    if(ret != SQLITE_OK)
    {
        printf("create table fail: %s\n",zErrMsg);
    }

    if(snprintf(sql1,sizeof(sql1), "insert into temper values('%s','%s','%s')", ipaddr, datetime, temper) < 0)
    {
        printf("Failed to write data\n");
    }
    //printf("Write data successfully!\n");

    sqlite3_exec(db, sql1, 0, 0, &zErrMsg);
    sqlite3_free(zErrMsg);
    sqlite3_close(db);
    return;
}


Keywords: C Linux SQLite vim

Added by FunkyELF on Fri, 21 Jan 2022 16:21:31 +0200