Programación de red Linux (servidor de alta concurrencia)


prefacio

Este artículo le enseñará cómo utilizar servidores de alta concurrencia en la programación de redes Linux. Primero debemos comprender qué es un servidor de alta concurrencia y luego aprender cómo escribir un servidor de alta concurrencia.

1. ¿Qué es un servidor de alta concurrencia?

Un servidor de alta concurrencia se refiere a un sistema de servidor que puede manejar una gran cantidad de solicitudes simultáneas al mismo tiempo. En las aplicaciones de red, cuando varios usuarios o clientes solicitan el servidor al mismo tiempo, el servidor debe poder manejar estas solicitudes de manera eficiente y mantener un buen rendimiento y estabilidad.

高并发服务器的设计和实现需要考虑以下几个关键因素:

1. Procesamiento multiproceso o multiproceso: el uso de métodos multiproceso o multiproceso puede permitir que el servidor procese múltiples solicitudes al mismo tiempo. Cada hilo o proceso es responsable de procesar una solicitud, mejorando así las capacidades de procesamiento concurrente del servidor.

2. E/S asincrónicas sin bloqueo: el uso del modelo de programación de E/S asincrónicas sin bloqueo puede evitar el bloqueo de subprocesos o procesos durante el procesamiento de solicitudes, aprovechar al máximo los recursos del sistema y mejorar el rendimiento del procesamiento concurrente del servidor. Una forma común es utilizar bibliotecas o marcos de programación controlados por eventos, como la E/S controlada por eventos de Node.js, el modelo controlado por eventos de Nginx, etc.

3. Equilibrio de carga: al introducir un equilibrador de carga en el clúster de servidores y distribuir las solicitudes a múltiples nodos de servidor, se puede mejorar aún más la capacidad de procesamiento simultáneo de todo el sistema. El equilibrador de carga puede distribuir solicitudes a diferentes servidores de acuerdo con ciertas políticas (como sondeo, peso, etc.) para que cada servidor pueda manejar una cantidad adecuada de solicitudes.

4. Almacenamiento en caché y distribuido: el uso adecuado del caché y el almacenamiento distribuido puede reducir la carga en el servidor y mejorar la velocidad de respuesta. Almacene los datos a los que se accede con frecuencia en caché para reducir la presión sobre los sistemas de almacenamiento back-end.

5. Expansión horizontal: amplíe la capacidad de procesamiento del sistema aumentando la cantidad de servidores, como agregando nodos de servidor o utilizando la función de escalamiento elástico de los proveedores de servicios de computación en la nube. El escalado horizontal permite que el sistema maneje más solicitudes simultáneas.

2. La idea de utilizar subprocesos y procesos múltiples para implementar servidores de alta concurrencia

En la comunicación TCP, después de que el cliente se conecta al servidor, el servidor creará un nuevo cliente para comunicarse con el cliente conectado en lugar de comunicarse directamente. En este caso, tenemos una idea. Necesitamos mantener el servidor en aceptación esperando la conexión, esperando que se conecte un nuevo cliente. Cuando un cliente se conecta, se creará un nuevo cliente para comunicarse con él, luego necesitaremos para crear un hilo o proceso para este cliente, de modo que no afecte la capacidad del servidor para recibir solicitudes de conexión de nuevos clientes.

Insertar descripción de la imagen aquí

3. Escribir código de servidor multiproceso

Después de que el cliente se conecta exitosamente al servidor, usa la función fork para crear un proceso hijo para comunicarse con el cliente, y el proceso padre usa señales para reciclar el proceso hijo.

#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 <stdlib.h>
#include <signal.h>
#include <sys/wait.h>
#include <errno.h>

/*回收子进程*/
void catch_child(int sig) 
{
    
    
    pid_t pid;
    int status;
    while ((pid = waitpid(-1, &status, WNOHANG)) > 0) 
    {
    
    
        // 处理子进程的退出状态
    }
}

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 r = 0;

    pid_t pid;

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

    while( 1 )
    {
    
    
        asize = sizeof(caddr);      
        client = accept(server, (struct sockaddr*)&caddr, &asize);

        if( client == -1 )
        {
    
    
            if (errno == EINTR)
            {
    
    
                // 信号中断,重新调用accept
                client = accept(server, (struct sockaddr*)&caddr, &asize);
            }
            else
            {
    
    
                perror("accept");
                printf("client accept error\n");
                return -1;
            }
        }   

        pid = fork();//创建子进程与客户端进行通信

        if(pid == 0)
        {
    
    
            close(server);
            while (1)
            {
    
    
                /*子进程*/
                len = read(client, buf, 1024);
                if(len == 0)
                {
    
    
                    printf("child exit\n");
                    close(client);
                    exit(1);//退出子进程
                }
                write(client, buf, len);
                printf("recv_buf : %s len : %d\n", buf, len);
                printf("child pid : %d\n", getpid());
            }                       
        }
        else if(pid > 0)
        {
    
    
            /*父进程*/
            struct sigaction act;
            sigemptyset(&act.sa_mask);
            act.sa_handler = catch_child;            
            act.sa_flags = 0;

            sigaction(SIGCHLD, &act, NULL);

            close(client);                    
        }
    }
    
    close(server);

    return 0;
}

4. Escribir código de servidor multiproceso

El método de subprocesos múltiples es más simple que el método de múltiples procesos. Aquí usamos la función pthread_detach para separar los subprocesos. En este caso, no necesitamos reciclar manualmente los subprocesos. Todo lo que tenemos que hacer es comunicarnos con los conectados. cliente en la función de hilo.Simplemente comuníquese.

#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 <stdlib.h>
#include <errno.h>
#include <pthread.h>

void* client_work(void* arg)
{
    
    
    int client = (int)arg;
    int len = 0;
    char buf[1024];

    while (1)
    {
    
    
        len = read(client, buf, 1024);
        if(len == 0)
        {
    
    
            printf("client is close\n");
            return NULL;
        }
        printf("read buf : %s\n", buf);
        write(client, buf, len);
    }    

    close(client);
    return NULL;
}

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 r = 0;

    pid_t pid;

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

    while( 1 )
    {
    
    
        pthread_t tid;
        asize = sizeof(caddr);      
        client = accept(server, (struct sockaddr*)&caddr, &asize);

        if( client == -1 )
        {
    
    
            if (errno == EINTR)
            {
    
    
                // 信号中断,重新调用accept
                client = accept(server, (struct sockaddr*)&caddr, &asize);
            }
            else
            {
    
    
                perror("accept");
                printf("client accept error\n");
                return -1;
            }
        }

        pthread_create(&tid, NULL, client_work, (void*)client);   
        pthread_detach(tid);
    }
    
    close(server);

    return 0;
}

Resumir

Este artículo lo explicará aquí.

Supongo que te gusta

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