Programación de red Linux (uso de función de selección de multiplexación IO multicanal)


prefacio

Este artículo le enseñará cómo utilizar la función de selección de IO multiplexada.

1. ¿Qué es la multiplexación de E/S múltiple?

1. La multiplexación de E/S es una tecnología utilizada para monitorear y procesar múltiples fuentes de entrada/salida (E/S) simultáneamente. Permite que un proceso escuche y procese múltiples descriptores de archivos (sockets, archivos, tuberías, etc.) al mismo tiempo, implementando así un modelo de programación eficiente basado en eventos.

2. En el modelo de E / S tradicional, las E / S con bloqueo o sin bloqueo generalmente se usan para operaciones de lectura y escritura, y se crea un hilo o proceso para cada fuente de E / S (como una conexión de socket). para procesar. En escenarios de alta concurrencia, este método conducirá a un fuerte aumento en la cantidad de subprocesos o procesos, un desperdicio de recursos del sistema y una alta sobrecarga de cambio de contexto.

3. La multiplexación de E/S multicanal utiliza mecanismos proporcionados por el sistema operativo, como seleccionar, sondear y epoll (en Linux), para permitir que los programas monitoreen múltiples fuentes de E/S al mismo tiempo y realicen E/S asíncronas. Oh operaciones. La aplicación puede registrar múltiples fuentes de E/S en el multiplexor y esperar a que ocurran eventos en el multiplexor. Una vez que un evento está listo (por ejemplo, la lectura y la escritura están listas), el programa puede estar listo para que estos eventos realicen las operaciones correspondientes.

多路I/O复用的主要优点如下:

1. Eficiente: mediante el enfoque basado en eventos, se evita el cambio frecuente de subprocesos y procesos y se reduce la sobrecarga del sistema.

2. Ahorre recursos: utilice menos subprocesos o procesos para manejar múltiples fuentes de E/S, ahorrando recursos del sistema y pudiendo manejar una gran cantidad de conexiones simultáneas.

3. Modelo de programación simplificado: en comparación con los métodos tradicionales de programación multiproceso o multiproceso, el uso de multiplexación de E/S múltiples puede simplificar el modelo de programación, haciendo que el código sea más claro y más fácil de mantener.

需要注意的是,多路I/O复用并非适用于所有情况,特别是在处理高延迟和大数据量的场景下可能不太适合。此外,不同的操作系统上多路复用器的机制和性能表现也有所不同。

2. Explicación de la función de selección.

La función select() es un modelo de E/S para multiplexación, que puede monitorear múltiples descriptores de archivos al mismo tiempo y realizar el procesamiento correspondiente cuando cualquiera de los descriptores de archivos esté listo. Se utiliza ampliamente para implementar una programación eficiente basada en eventos.

select()函数的原型如下:

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

参数说明:

nfds: aumenta el valor máximo de los descriptores de archivos monitoreados en 1.

readfds: puntero a una colección de descriptores de archivos legibles.

writefds: puntero a una colección de descriptores de archivos grabables.

exceptfds: puntero a la colección de descriptores de archivos de excepción.

tiempo de espera: especifique el tiempo de espera y seleccione la duración del bloqueo.

fd_set es un tipo de conjunto de descriptores de archivos que se opera mediante definiciones de macros y funciones de operación.

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

Cree e inicialice una colección de descriptores de archivos.

Agregue los descriptores de archivos que se van a monitorear al conjunto de descriptores de archivos correspondiente.

Llame a la función select() y pase el conjunto de descriptores de archivos.

Verifique el valor de retorno. Si es negativo, significa un error. Si es 0, significa un tiempo de espera. Si es mayor que 0, significa que hay un descriptor de archivo listo.

Utilice la macro FD_ISSET() para comprobar qué descriptores de archivos están listos y procesarlos en consecuencia.

下面是一个简单的示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int main() {
    
    
    fd_set readfds;
    int max_fd, ret;

    FD_ZERO(&readfds);
    FD_SET(STDIN_FILENO, &readfds);  // 监视标准输入

    max_fd = STDIN_FILENO + 1;

    struct timeval timeout;
    timeout.tv_sec = 5;  // 设置超时时间为5秒
    timeout.tv_usec = 0;

    ret = select(max_fd, &readfds, NULL, NULL, &timeout);

    if (ret == -1) {
    
    
        perror("select");
        exit(EXIT_FAILURE);
    } else if (ret == 0) {
    
    
        printf("Timeout\n");
    } else {
    
    
        if (FD_ISSET(STDIN_FILENO, &readfds)) {
    
    
            printf("Stdin is ready for reading\n");

            // 读取标准输入数据
            char buffer[100];
            fgets(buffer, sizeof(buffer), stdin);
            printf("Received: %s", buffer);
        }
    }

    return 0;
}

3. Utilice select para programar servidores simultáneos

#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 <sys/select.h>



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");

    maxfd = server;

    fd_set rets;
    fd_set allrets;

    FD_ZERO(&allrets);
    FD_SET(server, &allrets);

    while( 1 )
    {
    
            
        rets = allrets;
        ret = select(maxfd + 1, &rets, NULL, NULL, NULL);//使用select函数进行监听
        if(ret > 0)
        {
    
    
            if(FD_ISSET(server, &rets))
            {
    
    
                /*有客户端连接上来了*/
                asize = sizeof(caddr);  
                client = accept(server, (struct sockaddr*)&caddr, &asize);
                FD_SET(client, &allrets);//将连接上来的客户端设置进去
                if(maxfd < client)
                {
    
    
                    /*更新最大文件描述符*/
                    maxfd = client;
                }
            }
            else if(ret == -1)
            {
    
    
                for(i = server + 1; i <= maxfd; i++)
                {
    
    
                    if(FD_ISSET(i, &rets))//判断是哪一个客户端有信息
                    {
    
    
                        len = read(i, buf, 1024);
                        if(len == 0)//客户端断开了连接
                        {
    
    
                            FD_CLR(i, &allrets);//将断开连接的客户端清除出去
                            close(i);//关闭客户端
                        }
                        else
                        {
    
    
                            printf("read len : %d read buf : %s\n", len, buf);
                            write(i, buf, len);
                        }
                    }
                }
            }
        }
        else
        {
    
    
            printf("select is err\n");
        }                

    }
    
    close(server);

    return 0;
}

注意点:

Cuando se utiliza la función select(), los parámetros como los readfds pasados ​​y salientes pueden ser diferentes. Esto se debe a que la función select() modifica el conjunto de descriptores de archivos pasados ​​para indicar qué descriptores de archivos están listos.

Por lo tanto, cuando utilice select, primero debe hacer una copia de seguridad de los readfds para evitar perder algunos descriptores de archivos.

Cuarto, las desventajas de la función de selección.

1. Ineficiencia: la función select() tiene algunas limitaciones funcionales. Utiliza un escaneo lineal para recorrer el conjunto de descriptores de archivos monitoreados, por lo que es menos eficiente en el caso de una gran cantidad de descriptores de archivos. Cada llamada a select() requiere pasar el conjunto completo de descriptores de archivos al kernel y, al regresar, se vuelve a verificar todo el conjunto para determinar qué descriptores de archivos están listos. La sobrecarga de este escaneo lineal aumenta con la cantidad de descriptores de archivos que se monitorean.

2. Limitación en la cantidad de descriptores de archivos: existe un límite en la cantidad de descriptores de archivos que la función select() puede monitorear. En algunos sistemas, este límite puede ser relativamente pequeño, como 1024. Por lo tanto, si la cantidad de descriptores de archivos a monitorear excede el límite, se requerirán otros métodos.

3. Modo de bloqueo: la función select () es una llamada de bloqueo, es decir, cuando no hay ningún descriptor de archivo listo, siempre bloqueará y esperará. Esto evita que el programa realice otras operaciones. Aunque este problema se puede resolver estableciendo un tiempo de espera, un tiempo de espera demasiado corto puede provocar tiempos de espera falsos, mientras que un tiempo de espera demasiado largo afectará la capacidad de respuesta del programa.

4. Tipos de eventos restringidos: la función select() solo puede monitorear la legibilidad, escritura y excepciones del descriptor de archivo. Si necesita monitorear otros tipos de eventos, como eventos de temporizador o eventos de señal, no puede usar la función select().

5. Reinicialice cada llamada: antes de cada llamada a la función select(), la colección de descriptores de archivos debe reinicializarse y los descriptores de archivos que se van a monitorear se agregan nuevamente a la colección. Un proceso de inicialización de este tipo es relativamente engorroso, especialmente cuando el conjunto de descriptores de archivos cambia y el conjunto debe actualizarse manualmente.

Resumir

Este artículo explica principalmente cómo utilizar la función de selección para realizar IO multicanal y realizar la escritura de programas de servidor concurrentes.

Supongo que te gusta

Origin blog.csdn.net/m0_49476241/article/details/132351251
Recomendado
Clasificación