Linux network programming (multiplexing IO multiplexing poll)


Preface

In the previous article, we explained the use of select for IO multiplexing. In this article, we will explain the use of the poll function for multiple IO multiplexing.

1. Explanation of poll function

The poll() function is a system call function commonly used in network programming. It is used to monitor the status of multiple file descriptors to determine whether any file descriptors are ready for reading, writing, or an exception occurs.

以下是 poll() 函数的基本用法:

#include <poll.h>

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

The fds parameter is a pointer to an array of pollfd structures, each structure describing a file descriptor and the events it cares about.

The nfds parameter is the number of elements in the fds array.

The timeout parameter is the timeout in milliseconds. Specifying a value for timeout can control the blocking behavior of the poll() function.

pollfd 结构体的定义如下:

struct pollfd {
    
    
    int fd;       // 文件描述符
    short events; // 感兴趣的事件
    short revents; // 实际发生的事件
};

fd is the file descriptor being watched.

events 是要监视的事件的掩码,可以是以下值之一或它们的组合:

POLLIN: There is data to read.

POLLOUT: data can be written.

POLLERR: An error occurred.

POLLHUP: The connection is closed.

POLLNVAL: The file descriptor is illegal.

revents is the actual events that the poll() function populates.

poll() 函数的返回值表示有几个文件描述符准备好了,即满足所关心的事件。返回 值的三种情况如下:

Return value greater than 0: indicates the number of ready file descriptors.

A return value equal to 0: indicates that no file descriptors were ready within the specified timeout.

The return value is less than 0: an execution error occurred.

使用 poll() 函数的步骤如下:

1. Set the fd and events fields of each file descriptor in the pollfd array.

2. Call the poll() function and pass the pollfd array, number of elements and timeout.

3. Check the return value and determine the specific event based on the revents field.

Second, use the poll function to complete the concurrent server

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

#define MAX_CLIENT  1024//最大可连接客户端的数量

int main()
{
    
    
    int server = 0;
    struct sockaddr_in saddr = {
    
    0};
    int client = 0;
    struct sockaddr_in caddr = {
    
    0};
    socklen_t asize = 0;
    int len = 0;
    char buf[32] = {
    
    0};
    int maxfd;
    int ret = 0;
    int i = 0;

    server = socket(PF_INET, SOCK_STREAM, 0);

    if( server == -1 )
    {
    
    
        printf("server socket error\n");
        return -1;
    }

    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = htonl(INADDR_ANY);
    saddr.sin_port = htons(8888);

    if( bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1 )
    {
    
    
        printf("server bind error\n");
        return -1;
    }

    if( listen(server, 128) == -1 )
    {
    
    
        printf("server listen error\n");
        return -1;
    }

    printf("server start success\n");

	/*将fds中的fd全部置为-1*/
    struct pollfd fds[MAX_CLIENT];
    for(i = 0; i < MAX_CLIENT; i++)
    {
    
    
        fds[0].fd = -1;
    }

    /*将服务端套接字添加到fds数组中*/
    fds[0].fd = server;
    fds[0].events = POLLIN;

    while( 1 )
    {
    
            
        ret = poll(fds, MAX_CLIENT, -1);
        if(ret < 0)
        {
    
    
            printf("poll err\n");
            return -1;
        }

        if(fds[0].revents & POLLIN)
        {
    
    
            /*有客户端连接上来了*/
            asize = sizeof(caddr);  
            client = accept(server, (struct sockaddr*)&caddr, &asize);
            printf("client is connect\n");

            /*将新连接添加到fds数组中*/
            for(i = 1; i < MAX_CLIENT; i++)
            {
    
    
                if(fds[i].fd == -1)
                {
    
    
                    fds[i].fd = client;
                    fds[i].events = POLLIN;
                    break;
                }
            }            
        }

        /*遍历现有连接进行读取和处理数据*/
        for(i = 1; i < MAX_CLIENT; i++)
        {
    
                
            int clientfd = fds[i].fd;
            if(clientfd > 0 && (fds[i].revents & POLLIN))
            {
    
    
                printf("process data\n");
                len = read(clientfd, buf, 1024);
                if(len == 0)
                {
    
    
                    /*客户端断开连接,关闭客户端文件描述符*/
                    close(clientfd);
                    fds[i].fd = -1;
                    printf("client is disconnect\n");
                }
                else
                {
    
    
                    printf("read buf : %s\n", buf);
                    write(clientfd, buf, len);
                }
            }
        }
    }
    
    close(server);

    return 0;
}

Three, the advantages and disadvantages of poll

优点:

1. Simple and easy to use: Compared with low-level system calls such as select, poll provides a simpler API that is easier to use and understand.

2. No limit on the number of file descriptors: poll has no predefined limit on the number of file descriptors and can adapt to larger concurrent connections.

3. Support file descriptor array: Compared with the bitmap method of select, poll uses a file descriptor array, which can be more conveniently managed and operated.

4. Efficient: poll uses polling to monitor the status changes of file descriptors. Only active file descriptors will be returned, reducing useless polling processes and improving efficiency.

5. Support non-blocking IO: Similar to select, poll also supports non-blocking IO mode, which can continue to process other tasks during the waiting period.

缺点:

1. Each call requires traversing the entire file descriptor array: Even if only a few file descriptors are active, poll needs to traverse the entire file descriptor array each time it is called, which will cause performance overhead.

2. No timeout precision control is provided: The timeout parameter of poll is in milliseconds and cannot provide higher precision, so it is not suitable when a more precise timeout is required.

3. Non-portability: poll is a relatively new system call. Not all operating systems provide this interface, so compatibility needs to be considered when writing cross-platform code.

4. No support for signal processing: Unlike select, poll does not provide support for signal processing, so it cannot directly process signal events.

Summarize

This article mainly explains how to use the poll function and uses poll to implement a concurrent server. You can compare and think about this in conjunction with the select function in the previous article.

Guess you like

Origin blog.csdn.net/m0_49476241/article/details/132368785