Linux NIO Series (05) poll

Keywords: PHP socket Linux network Programming

Catalog

Linux NIO Series (05) poll

Netty Series Catalogue( https://www.cnblogs.com/binarylei/p/10117436.html)

I. Comparison of select and poll

Like poll() system calls, select() manages multiple descriptors for polling, which is processed according to the state of the descriptor, but poll() does not have a limit on the maximum number of file descriptors (but performance will degrade if the number is too large). poll() and select() also have a disadvantage that the array containing a large number of file descriptors is copied between the user state and the address space of the kernel as a whole. Whether these file descriptors are ready or not, their overhead increases linearly with the increase of the number of file descriptors.

II. poll API

Introduction of poll() function

#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);

(1) Functions:

Monitor and wait for property changes in multiple file descriptors

(2) Parameters:

  • fds: A pointer to the 0th element of an array of structured objects, each element of which is a struct pollfd structure that specifies the conditions for testing a given fd

    struct pollfd {
        int fd;         // File descriptor
        short events;   // Waiting events
        short revents;  // What actually happened
    };
  • nfds: Used to specify the number of elements in the first parameter array.

  • timeout: Specifies the number of milliseconds to wait, and poll() returns whether I/O is ready or not.

(3) pollfd data structure

  • fd: Each pollfd structure specifies a monitored file descriptor that can pass multiple structures to indicate that poll() monitors multiple file descriptors.

  • Events: Specify events (input, output, error) that monitor fd. Each event has multiple values, as follows:

    POLLIN has data to read
     POLLRDNORM has common data readable, equivalent to POLLIN
     POLLPRI has urgent data to read
     POLLOUT Writes Data without Blocking
     Error in file descriptor specified by POLLER
     The file descriptor pending event specified by POLLHUP
     POLLNVAL invalid request, cannot open the specified file descriptor
  • The revents: revents domain is the result event of the operation of the file descriptor, which is set by the kernel when the call returns. Any event requested in the events domain may be returned in the revents domain.

Note: The events domain of each structure is set by the user to tell the kernel what events we are interested in, and the revents domain is set by the kernel when it returns to explain what happened to the descriptor.

(4) Return value

  • When successful, poll() returns the number of file descriptors whose revents field is not zero in the structure; if no event occurs before the timeout, poll() returns 0;

  • When it fails, poll() returns - 1 and sets errno as one of the following values:

    EBADF: The file descriptor specified in one or more structures is invalid.
    EFAULT: The address pointed by the fds pointer exceeds the address space of the process.
    EINTR: A signal is generated before the requested event, and the call can be restarted.
    EINVAL: nfds parameter exceeds PLIMIT_NOFILE value.
    ENOMEM: The available memory is insufficient to complete the request.

Appendix 1: IO restrictions for each process in linux

# The maximum number of files that the current computer can open. Influenced by hardware, this value can also be changed (via limits.conf)
cat /proc/sys/fs/file-max

# View the upper limit of socket descriptors that a process can open. The default is 1024
ulimit -a 
# Change to the default maximum number of files. [Revoke the user and make it effective]
ulimit -n 2000

# Soft soft limit hard hard limit. The so-called soft limit can be modified by command, but it can not be greater than the hard limit.
vi /etc/security/limits.conf
* soft nofile 3000      # Set default values. Command modification can be used directly
* hard nofile 20000     # Maximum upper limit

Appendix 2: poll network programming

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

#define SERVER_PORT 8888
#define OPEN_MAX  3000
#define BACKLOG   10
#define BUF_SIZE 1024

int main() {
    int i, j, maxi;
    int listenfd, connfd, sockfd; // Define socket descriptors
    int nready;     // Accept the pool return value
    int recvbytes;  // Accept recv return value

    char recv_buf[BUF_SIZE];   // Send Buffer
    struct pollfd client[OPEN_MAX]; // struct pollfd* fds

    // Define IPV4 Interface Address Structure
    struct sockaddr_in seraddr;     // server address
    struct sockaddr_in cliaddr;     // client address
    int cliaddr_len;

    // Initialization of IPV4 Interface Address Structure
    seraddr.sin_family = AF_INET;   // Specify the address family
    seraddr.sin_port = htons(SERVER_PORT);    // port
    seraddr.sin_addr.s_addr = INADDR_ANY;   // Address of IPV4
    bzero(&(seraddr.sin_zero), 8);

    // socket () function
    if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("socket error");
        exit(1);
    }

    // Address reuse
    int on = 1;
    if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
        perror("setsockopt error");
        exit(1);
    }

    // bind() function
    if(bind(listenfd, (struct sockaddr *)&seraddr, sizeof(struct sockaddr)) == -1) {
        perror("bind error");
        exit(1);
    }

    // listen () function
    if(listen(listenfd, BACKLOG) == -1) {
        perror("listen error");
        exit(1);
    }

    client[0].fd = listenfd;    // Add listenfd to the listening sequence
    client[0].events = POLLIN;  // Listening for Reading Events

    // Initialize the remaining elements in client []
    for(i = 1;i < OPEN_MAX;i++) {
        client[i].fd = -1;      //Can't use 0, 0 is also a file descriptor
    }

    maxi = 0; //Maximum element subscript in client []
    while(1) {
        nready = poll(client, maxi + 1, -1);//Blocking monitoring
        if(nready < 0) {
            perror("poll error!\n");
            exit(1);
        }

        if(client[0].revents & POLLIN) {    //Bit and operation; Read event ready for listenfd        
            cliaddr_len = sizeof(cliaddr);
            if((connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &cliaddr_len))==-1) {
                perror("accept error");
                exit(1);
            }
            printf("client IP: %s\t PORT : %d\n", inet_ntoa(cliaddr.sin_addr), ntohs(cliaddr.sin_port));
            //Add sockfd to the listening sequence
            for(i = 1; i < OPEN_MAX; i++) {
                if(client[i].fd < 0) {
                    client[i].fd = connfd;
                    break;
                }
            }
            if(i == OPEN_MAX) {
                perror("too many clients!\n");
                exit(1);
            }

            client[i].events = POLLIN;//Listening for read events of connfd
            if(i > maxi) {
                maxi = i;
            }

            //Determine if the event has been processed
            if(--nready == 0) {
                continue;
            }
        }

        // Check whether the client sends a message or not
        for(i = 1; i <= maxi; i++) {
            if((sockfd = client[i].fd) < 0) {
                continue;
            }
            if(client[i].revents & POLLIN) {
                memset(recv_buf, 0, sizeof(recv_buf));
                recvbytes = recv(sockfd, recv_buf, BUF_SIZE, 0);

                if(recvbytes < 0) {
                    // ` Erno = EINTR `was abnormally interrupted and needs to be restarted. Receive RST logo
                    // ` Erno == EAGIN or EWOULDBLOCK `Read data in a non-blocking manner, but there is no data, so you need to read it again
                    // ` Erno = ECONNRESET `Connection is reset, need close, remove connection
                    // ` errno = other `other exceptions
                    if(errno == ECONNRESET) { // RET Mark                    
                        printf("client[%d] aborted connection!\n",i);
                        close(sockfd);
                        client[i].fd = -1;
                    } else {
                        perror("recv error!\n");
                        exit(1);
                    }
                } else if(recvbytes == 0) {
                    printf("client[%d],close!\n",i);
                    close(sockfd);
                    client[i].fd = -1;
                } else {
                    send(sockfd, recv_buf, recvbytes, 0);
                }
                if(--nready == 0) {
                    break;
                }

            }
        }
    }
    return 0;
}

Reference resources:

Record a little every day. Content may not matter, but habits matter!

Posted by cbassett03 on Tue, 02 Jul 2019 16:24:01 -0700