Linux network programming (high concurrency server)


Preface

This article takes you to learn about high-concurrency servers in Linux network programming. First of all, we need to understand what a high-concurrency server is, and then learn how to write a high-concurrency server.

1. What is a high-concurrency server?

A high-concurrency server refers to a server system that can handle a large number of concurrent requests at the same time. In network applications, when multiple users or clients request the server at the same time, the server needs to be able to handle these requests efficiently and maintain good performance and stability.

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

1. Multi-thread or multi-process processing: The use of multi-thread or multi-process can enable the server to process multiple requests at the same time. Each thread or process is responsible for processing a request, thereby increasing the concurrent processing capability of the server.

2. Asynchronous non-blocking I/O: Using the asynchronous non-blocking I/O programming model can avoid blocking threads or processes during request processing, make full use of system resources, and improve the concurrent processing performance of the server. A common way is to use an event-driven programming framework or library, such as Node.js' Event-driven I/O, Nginx's event-driven model, etc.

3. Load balancing: By introducing a load balancer in the server cluster and distributing requests to multiple server nodes, the concurrent processing capability of the entire system can be further improved. The load balancer can distribute requests to different servers according to certain policies (such as polling, weight, etc.) so that each server can handle an appropriate amount of requests.

4. Cache and distributed storage: Reasonable use of cache and distributed storage can reduce server load and improve response speed. Store frequently accessed data in the cache to reduce the pressure on the back-end storage system.

5. Horizontal expansion: Expand the system's processing capabilities by increasing the number of servers, such as adding server nodes or using the elastic scaling function of a cloud computing service provider. Horizontal scaling enables the system to handle more concurrent requests.

2. The idea of ​​using multi-thread and multi-process to realize high concurrent server

In TCP communication, after the client connects to the server, the server will create a new client to communicate with the connected client instead of communicating directly. In this case, we have an idea. We need to keep the server in accpet waiting for connection, waiting for a new client to connect. When a client connects, a new client will be created to communicate with it, then we will A thread or process needs to be created for this client, so that it will not affect the server's ability to receive new client connection requests.

Insert image description here

3. Writing multi-process server code

After the client successfully connects to the server, it uses the fork function to create a child process to communicate with the client. The parent process uses signals to recycle the child process.

#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. Writing multi-threaded server code

The multi-threading method is simpler than the multi-process method. Here we use the pthread_detach function to separate the threads. In this case, we do not need to manually recycle the threads. All we have to do is to communicate with the connected client in the thread function. Just communicate.

#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;
}

Summarize

This article will explain it here.

Guess you like

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