“This is the 13th day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021”

I. Function introduction

In the previous article juejin.cn/post/702874… In, the preparation of a Linux based on TCP protocol group chat system design (multi-thread + SELECT) case, demonstrated the use of select function. This article continues with the remaining poll, epoll functions. And is also the use of group chat system case writing examples, easy to understand the actual use.

Although SELECT was introduced in the previous article, I pasted the detailed description of Select to make it easier to compare the three functions.

1.1 the select function

#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout); The listener () function listens for a specified number of file descriptors. Function parameters:intNFDS: Listen for the largest file descriptor +1Fd_set *readfds: a set of file descriptors that listen for read eventsNULLFd_set *writefds: a set of file descriptors that listen for write eventsNULLFd_set * EXCEPtfDS: set of file descriptors to listen for exceptfds from other eventsNULL
struct timeval *timeout :Specify the wait time. If you fill in theNULLRepresents a permanent wait until either file descriptor generates an event before returning. If the normal time is specified, it also returns if no event is generated during the waiting time.struct timeval {
        long    tv_sec;         /* seconds */
        long    tv_usec;        /* microseconds */}; Return value: Number of event file descriptors generated. = =0No event is generated. >0Represents the number of events <0Indicates an error.void FD_CLR(int fd, fd_set *set);   // Clears the specified file descriptor in a collection
int  FD_ISSET(int fd, fd_set *set); // Determine whether the specified file descriptor in the specified collection generated an event. True means an event occurred
void FD_SET(int fd, fd_set *set);  // Adds the specified file descriptor to the specified collection
void FD_ZERO(fd_set *set);  // Empty the entire collection.
Copy the code

1.2 the poll function

#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout); Function Listens for multiple file descriptors. Function parameters:struct pollfd *fds :Listen to the event, can fill in the structure array.nfds_tNFDS: indicates the number of listeners.intTimeout: waiting time, ms unit. >0Indicates the normal waiting time. = =0Does not wait <0Return value: number of events generated struct pollfd {int   fd;         /* File descriptor to listen to */
    short events;     /* Requested events to listen to events POLLIN stands for readable events */
    short revents;    /* Returned Events Events generated as a judgment condition */
};

Copy the code

1.3 epoll function

#include <sys/epoll.h>

int epoll_create(int size); The epoll function creates a buffer for the epoll file descriptor.int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); Function Add, delete, modify listen file descriptor. Function parameters:intEpfd File descriptor dedicated to epollintOp Operation command. EPOLL_CTL_ADD EPOLL_CTL_MOD EPOLL_CTL_DELintFd operates on file descriptorsstruct epoll_event *eventA structure that holds listener file descriptor informationint epoll_wait(int epfd.struct epoll_event *events.int maxevents.int timeout);The function waits for an event to occur. Function parameters:intEpfd File descriptor dedicated to epollstruct epoll_event *events :The file description structure that holds the generated event.intMaxevents: indicates the maximum number of listens.intTimeout: ms unit of waiting for events.0  >0= =0Return value: The number of events generated.typedef union epoll_data {
    void    *ptr;
    int      fd;    // File descriptor
    uint32_t u32;
    uint64_t u64;
} epoll_data_t;

struct epoll_event {
    uint32_t     events;    /* Epoll events EPOLLIN input events */
    epoll_data_t data;      /* User data variable */
};

Copy the code

Use cases of epoll and poll functions

In the last article has posted a detailed group chat system source code, and there are operational renderings, train of thought.

Epoll: poll: epoll: poll

2.1 Usage Case of the poll function: This function is used on the TCP client

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
#include <sys/select.h>
#include <sys/time.h>
#include <poll.h>

// Message structure
struct MSG_DATA
{
    char type; Message type: 0 indicates that there is chat data. 1 Indicates that the friend is online. 2 Indicates that the friend is offline
    char name[50]; // Friend name
    int number;   // The number of people online
    unsigned char buff[100];  // Send chat data message
};
struct MSG_DATA msg_data;

// File receiver
int main(int argc,char **argv)
{   
    if(argc! =4)
    {
        printf("./app 
      
        < port number > < name >\n"
      );
        return 0;
    }
    int sockfd;
    // Ignore SIGPIPE signal -- mode server writes data to invalid socket causing process exit
    signal(SIGPIPE,SIG_IGN); 

    /*1. Create socket */
    sockfd=socket(AF_INET,SOCK_STREAM,0);
    /*2. Connect to server */
    struct sockaddr_in addr;
    addr.sin_family=AF_INET;
    addr.sin_port=htons(atoi(argv[2])); // The port number ranges from 0 to 65535
    addr.sin_addr.s_addr=inet_addr(argv[1]); / / IP address
    if(connect(sockfd,(const struct sockaddr *)&addr,sizeof(struct sockaddr_in))! =0)
    {
        printf("Client: server connection failed.\n");
        return 0;
    }

    /*3. Send a message indicating online */
    msg_data.type=1;
    strcpy(msg_data.name,argv[3]);
    write(sockfd,&msg_data,sizeof(struct MSG_DATA));

    int cnt;
    struct pollfd fds[2].
    fds[0].fd=sockfd;
    fds[0].events=POLLIN;
    fds[1].fd=0;
    fds[1].events=POLLIN;
    
    while(1)
    {
       // Listen on events
       cnt=poll(fds,2.- 1);
        if(cnt)
        {
            if(fds[0].events&fds[0].revents) // Determine whether a message is received from the server
            {
                cnt=read(sockfd,&msg_data,sizeof(struct MSG_DATA));
                if(cnt<=0) // Determine whether the server is disconnected
                {
                    printf("Server has exited.\n");
                    break;
                }
                else if(cnt>0)
                {
                    if(msg_data.type==0)
                    {
                        printf("%s:%s Number of online users :%d\n",msg_data.name,msg_data.buff,msg_data.number);
                    }
                    else if(msg_data.type==1)
                    {
                        printf("%s friends online. Number of people online :%d\n",msg_data.name,msg_data.number);
                    }
                    else if(msg_data.type==2)
                    {
                        printf("%s friends offline. Number of people online :%d\n",msg_data.name,msg_data.number); }}}if(fds[1].events&fds[1].revents)  // There is input on the keyboard
            {
                gets(msg_data.buff); // Read the message on the keyboard
                msg_data.type=0; // Indicates a normal message
                strcpy(msg_data.name,argv[3]); / / name
                write(sockfd,&msg_data,sizeof(struct MSG_DATA)); }}}close(sockfd);
    return 0;
}
Copy the code

2.2 Use case of the epoll function: Using the TCP client

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
#include <sys/select.h>
#include <sys/time.h>
#include <poll.h>
#include <sys/epoll.h>

// Message structure
struct MSG_DATA
{
    char type; Message type: 0 indicates that there is chat data. 1 Indicates that the friend is online. 2 Indicates that the friend is offline
    char name[50]; // Friend name
    int number;   // The number of people online
    unsigned char buff[100];  // Send chat data message
};
struct MSG_DATA msg_data;

#define MAX_EVENTS 10
struct epoll_event ev.events[MAX_EVENTS];
int epollfd;
int nfds;

// File receiver
int main(int argc,char **argv)
{   
    if(argc! =4)
    {
        printf("./app 
      
        < port number > < name >\n"
      );
        return 0;
    }
    int sockfd;
    // Ignore SIGPIPE signal -- mode server writes data to invalid socket causing process exit
    signal(SIGPIPE,SIG_IGN); 

    /*1. Create socket */
    sockfd=socket(AF_INET,SOCK_STREAM,0);
    /*2. Connect to server */
    struct sockaddr_in addr;
    addr.sin_family=AF_INET;
    addr.sin_port=htons(atoi(argv[2])); // The port number ranges from 0 to 65535
    addr.sin_addr.s_addr=inet_addr(argv[1]); / / IP address
    if(connect(sockfd,(const struct sockaddr *)&addr,sizeof(struct sockaddr_in))! =0)
    {
        printf("Client: server connection failed.\n");
        return 0;
    }

    /*3. Send a message indicating online */
    msg_data.type=1;
    strcpy(msg_data.name,argv[3]);
    write(sockfd,&msg_data,sizeof(struct MSG_DATA));

    int cnt;
    int i;
    // Create a dedicated file descriptor
    epollfd = epoll_create(10);
    // Add the file descriptor to listen on
    ev.events = EPOLLIN;
    ev.data.fd = sockfd;
    epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &ev);

    ev.events = EPOLLIN;
    ev.data.fd = 0; // Standard input file descriptor
    epoll_ctl(epollfd, EPOLL_CTL_ADD, 0, &ev);

    while(1)
    {
        // Listen on events
        nfds=epoll_wait(epollfd,events,MAX_EVENTS,- 1);
        if(nfds)
        {
            for(i=0; i<nfds; i++) {if(events[i].data.fd==sockfd) // Determine whether a message is received from the server
                {
                    cnt=read(sockfd,&msg_data,sizeof(struct MSG_DATA));
                    if(cnt<=0) // Determine whether the server is disconnected
                    {
                        printf("Server has exited.\n");
                        goto SERVER_ERROR;
                    }
                    else if(cnt>0)
                    {
                        if(msg_data.type==0)
                        {
                            printf("%s:%s Number of online users :%d\n",msg_data.name,msg_data.buff,msg_data.number);
                        }
                        else if(msg_data.type==1)
                        {
                            printf("%s friends online. Number of people online :%d\n",msg_data.name,msg_data.number);
                        }
                        else if(msg_data.type==2)
                        {
                            printf("%s friends offline. Number of people online :%d\n",msg_data.name,msg_data.number); }}}else if(events[i].data.fd==0) // Indicates data input on the keyboard
                {
                    gets(msg_data.buff); // Read the message on the keyboard
                    msg_data.type=0; // Indicates a normal message
                    strcpy(msg_data.name,argv[3]); / / name
                    write(sockfd,&msg_data,sizeof(struct MSG_DATA));
                }
            }
        }
    }
SERVER_ERROR:
    close(sockfd);
    return 0;
}
Copy the code