Multithreading | Multiprocesso | Programação de rede de alta simultaneidade

Insira a descrição da imagem aqui

1. Servidor simultâneo multiprocesso

Servidor simultâneo de vários processos é uma arquitetura de servidor clássica que atinge a capacidade de lidar com várias solicitações de clientes simultaneamente, criando vários subprocessos para lidar com conexões de clientes.

conceito:

  1. Quando o servidor é iniciado, o processo principal é criado e a porta de escuta é vinculada.
  2. Quando há uma solicitação de conexão do cliente, o processo principal aceita a conexão e cria um processo filho para tratar da conexão do cliente.
  3. O processo filho se comunica com o cliente, processando solicitações e enviando respostas.
  4. O processo principal continua a escutar novas solicitações de conexão.
  5. Depois que o processo filho conclui a tarefa, ele pode optar por encerrar ou continuar processando outras conexões e escolher se deseja repetir o ciclo de acordo com as necessidades.

vantagem:

  1. Altas capacidades de processamento simultâneo: Cada processo filho pode lidar com uma conexão de cliente de forma independente, aproveitando os processadores multi-core para alcançar altas capacidades de processamento simultâneo e melhorar o rendimento do servidor.
  2. Estabilidade: Cada processo filho é independente. Problemas com um processo não afetarão outros processos, o que melhora a estabilidade e a tolerância a falhas do servidor.
  3. Simples e intuitivo: É relativamente simples implementar servidores simultâneos usando o modelo multiprocesso, e o código é altamente legível e fácil de entender e manter.
  4. Plataforma cruzada: O conceito de servidor simultâneo multiprocessos é aplicável a vários sistemas operacionais, não se limitando apenas às plataformas Linux.

deficiência:

  1. Alto consumo de recursos: Cada processo filho requer recursos independentes, incluindo memória, descritores de arquivos, etc. Quando o número de conexões simultâneas for alto, uma grande quantidade de recursos do sistema será consumida.
  2. Comunicação entre processos: A comunicação entre subprocessos requer mecanismos adicionais, como pipes, memória compartilhada, etc., aumentando a complexidade.
  3. Sobrecarga de alternância de contexto: A sobrecarga de alternância entre processos é relativamente grande e afetará o desempenho.
  4. Dificuldades de depuração: como cada processo filho é executado de forma independente, a depuração e a localização de problemas podem ser complicadas.

Resumindo, o servidor simultâneo multiprocesso pode atender às necessidades de alto processamento simultâneo e tem as vantagens de estabilidade e simplicidade. No entanto, também apresenta deficiências como alto consumo de recursos e dificuldade de depuração.É importante escolher uma arquitetura de servidor adequada com base nas necessidades reais e no cenário de aplicação.

Caso

Usando multiprocessos, baseados na comunicação TCP, o servidor pode se conectar a vários clientes ao mesmo tempo. Implementar conversão de caso.

serviço.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>

void handler(int sig)
{
    if (sig == SIGCHLD)
    {
        while(waitpid(0,NULL,WNOHANG)>0);
    }
}
int main(int argc, char const *argv[])
{
    //1.建立socket套接字
    int socked = socket(AF_INET, SOCK_STREAM, 0);
    if (socked < 0)
    {
        perror("socket is err");
        return -1;
    }

    //2.bind绑定服务器ip和端口号
    struct sockaddr_in saddr, caddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(atoi(argv[1]));
    saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
    int len = sizeof(caddr);
    int rev = bind(socked, (struct sockaddr *)(&saddr), sizeof(saddr));
    if (rev < 0)
    {
        perror("bind is err");
        return 0;
    }

    //3.listen设置最大同时链接数量
    rev = listen(socked, 32);
    if (rev < 0)
    {
        perror("rev is err");
        return 0;
    }

    //4.建立子进程负责服务器给客户端发送消息,并且主进程不结束,此进程不结束
    char buf[1024] = {0};
    int size=0;
    while (1)
    {
        int acceptfd = accept(socked, (struct sockaddr *)(&caddr), &len);
        if (acceptfd < 0)
        {
            perror("accept is err");
            return 0;
        }
        pid_t pid = fork();
        if (pid < 0)
        {
            perror("pid is err");
            return 0;
        }
        else if (pid == 0)
        {
            //子进程,每个子进程维护一个客户端
            //不许要监听,直接关掉
            close(socked);
            while (1)
            {

                int flage = recv(acceptfd, buf, sizeof(buf), 0);
                if (flage < 0)
                {
                    perror("recv is err");
                }
                else if (flage == 0)
                {
                    printf("ip:%s is close\n", inet_ntoa(caddr.sin_addr));
                    close(acceptfd);
                    break;
                }
                else
                {
                    size = strlen(buf);

                    for (int i = 0; i < size; ++i)
                    {
                        if (buf[i] >= 'a' && buf[i] <= 'z')
                            buf[i] = buf[i] + ('A' - 'a');
                        else
                            buf[i] = buf[i] + ('a' - 'A');
                    }
                    printf("%d %s\n",getpid(),buf);
                    send(acceptfd, buf, sizeof(buf), 0);
                }
            }
        }
        else
        {
            //主进程回收子线程资源
            close(acceptfd);
            //异步节约资源
            signal(SIGCHLD, handler);
        }
    }
    return 0;
}

cliente.c

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

int main(int argc, char const *argv[])
{
    //1.socket建立文件描述符
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0)
    {
        perror("socket is err");
    }

    //2.connect连接服务器
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(atoi(argv[2]));
    saddr.sin_addr.s_addr = inet_addr(argv[1]);
    int flage = connect(fd, (struct sockaddr *)(&saddr), sizeof(saddr));
    if (flage < 0)
    {
        perror("connect is err");
    }

    //3.服务器端不断发送数据,接受服务器转化后的数据
    char buf[1024] = {0};
    while (1)
    {
        //memset(buf,0,sizeof(buf));
        fgets(buf, sizeof(buf), stdin);
        if (strncmp(buf,"quit#",5)==0)
        {
            break;
        }
        
        if (buf[strlen(buf) - 1] == '\n')
            buf[strlen(buf) - 1] = '\0';
        send(fd, buf, sizeof(buf), 0);
        flage = recv(fd, buf, sizeof(buf), 0);
        if (flage < 0)
        {
            perror("recv is err");
        }
        else
        {
            fprintf(stdout, "%s\n", buf);
        }
    }
    close(fd);
    return 0;
}

2. Servidor simultâneo multithread

Servidor simultâneo multithread é uma arquitetura de servidor que consegue lidar com várias solicitações de clientes simultaneamente, criando vários threads para lidar com conexões de clientes. Aqui estão os conceitos e algumas vantagens dos servidores simultâneos multithread:

conceito:

  1. Quando o servidor é iniciado, o thread principal é criado e a porta de escuta é vinculada.
  2. Quando há uma solicitação de conexão do cliente, o thread principal aceita a conexão e cria um ou mais threads para tratar a conexão do cliente.
  3. Threads se comunicam com clientes, tratam de solicitações e enviam respostas.
  4. O thread principal continua escutando novas solicitações de conexão.
  5. Depois que o thread conclui sua tarefa, ele opta por encerrar ou continuar processando outras conexões e escolhe se deseja repetir o ciclo de acordo com a necessidade.

vantagem:

  1. Eficiência de recursos: Em comparação com os processos, os threads têm menos sobrecarga de criação e destruição e ocupam menos recursos.Em particular, recursos compartilhados, como descritores de arquivos, podem ser compartilhados entre múltiplas conexões, melhorando a eficiência da utilização de recursos.
  2. Velocidade de resposta: A sobrecarga de comutação entre threads é pequena, o espaço de memória é compartilhado, a solicitação do cliente pode ser respondida rapidamente e o tempo de espera da conexão é reduzido.
  3. Simples e intuitivo: Comparado com o servidor simultâneo multiprocesso e multithread, é mais simples e intuitivo, o código é altamente legível e fácil de entender e manter.
  4. Escalabilidade: Threads podem ser facilmente adicionados e removidos para se adaptar às mudanças nos requisitos de conexão e fornecer melhor escalabilidade.
  5. Compartilhar dados é fácil: as estruturas de dados compartilhadas entre threads podem ser acessadas diretamente sem o uso de mecanismos adicionais, como comunicação entre processos.

deficiência:

  1. Problemas de sincronização de dados: O compartilhamento de dados entre vários threads pode levar a condições de corrida e problemas de consistência de dados, exigindo o uso de bloqueios ou outros mecanismos de sincronização para garantir a segurança do thread.
  2. Dificuldades de depuração: A depuração e localização de problemas podem ser complicadas pelo fato de que o espaço de memória do mesmo processo é compartilhado entre threads.
  3. Limitações de paralelismo: Em alguns casos, o paralelismo de um servidor multithread pode ser limitado pelo número de núcleos de CPU disponíveis no sistema.
  4. A segurança do thread precisa ser considerada: escrever código thread-safe pode exigir mais esforço e consideração de desenvolvimento para evitar problemas como corridas de dados e impasses.

Resumindo, os servidores simultâneos multithread podem atender às altas necessidades de processamento simultâneo e ter as vantagens de eficiência de recursos, velocidade de resposta, simplicidade e escalabilidade. No entanto, também apresenta deficiências, como problemas de sincronização de dados e dificuldades de depuração.É importante escolher uma arquitetura de servidor apropriada com base nas necessidades reais e no cenário de aplicação.

Caso

Usando multithreading, baseado na comunicação TCP, o servidor pode se conectar a vários clientes ao mesmo tempo. Implementar conversão de caso.

serviço.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <pthread.h>

struct client
{
    int acceptfd;
    struct sockaddr_in caddr;
};

void *my_pthread(void *p)
{
    char buf[1024] = {0};
    int size = 0;
    struct client *cl = (struct client *)p;
    int acceptfd = cl->acceptfd;
    struct sockaddr_in caddr = cl->caddr;
    while (1)
    {
        int flage = recv(acceptfd, buf, sizeof(buf), 0);
        if (flage < 0)
        {
            perror("recv is err");
        }
        else if (flage == 0)
        {
            printf("ip:%s is close\n", inet_ntoa(caddr.sin_addr));
            close(acceptfd);
            break;
        }
        else
        {
            size = strlen(buf);

            for (int i = 0; i < size; ++i)
            {
                if (buf[i] >= 'a' && buf[i] <= 'z')
                    buf[i] = buf[i] + ('A' - 'a');
                else
                    buf[i] = buf[i] + ('a' - 'A');
            }
            send(acceptfd, buf, sizeof(buf), 0);
        }
    }

    close(acceptfd);
    return 0;
}

int main(int argc, char const *argv[])
{

    //1.建立socket套接字
    int socked = socket(AF_INET, SOCK_STREAM, 0);
    if (socked < 0)
    {
        perror("socket is err");
        return -1;
    }

    //2.bind绑定服务器ip和端口号
    struct sockaddr_in saddr, caddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(atoi(argv[1]));
    saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
    int len = sizeof(caddr);
    int rev = bind(socked, (struct sockaddr *)(&saddr), sizeof(saddr));
    if (rev < 0)
    {
        perror("bind is err");
        return 0;
    }

    //3.listen设置最大同时链接数量
    rev = listen(socked, 32);
    if (rev < 0)
    {
        perror("rev is err");
        return 0;
    }

    //4.建立子进程负责服务器给客户端发送消息,并且主进程不结束,此进程不结束

    int i = 0;
    pthread_t tid;
    struct client ti[1024] = {0};
    while (1)
    {
        int acceptfd = accept(socked, (struct sockaddr *)(&caddr), &len);
        if (acceptfd < 0)
        {
            perror("accept is err");
            return 0;
        }
        ti[i].acceptfd = acceptfd;
        ti[i].caddr = caddr;
        pthread_create(&tid, NULL, my_pthread, (void *)&ti[i]);
        pthread_detach(tid);
        ++i;
    }
    return 0;
}

cliente.c

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

int main(int argc, char const *argv[])
{
    //1.socket建立文件描述符
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0)
    {
        perror("socket is err");
    }

    //2.connect连接服务器
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(atoi(argv[2]));
    saddr.sin_addr.s_addr = inet_addr(argv[1]);
    int flage = connect(fd, (struct sockaddr *)(&saddr), sizeof(saddr));
    if (flage < 0)
    {
        perror("connect is err");
    }

    //3.服务器端不断发送数据,接受服务器转化后的数据
    char buf[1024] = {0};
    while (1)
    {
        //memset(buf,0,sizeof(buf));
        fgets(buf, sizeof(buf), stdin);
        if (strncmp(buf,"quit#",5)==0)
        {
            break;
        }
        
        if (buf[strlen(buf) - 1] == '\n')
            buf[strlen(buf) - 1] = '\0';
        send(fd, buf, sizeof(buf), 0);
        flage = recv(fd, buf, sizeof(buf), 0);
        if (flage < 0)
        {
            perror("recv is err");
        }
        else
        {
            fprintf(stdout, "%s\n", buf);
        }
    }
    close(fd);
    return 0;
}

Acho que você gosta

Origin blog.csdn.net/m0_73731708/article/details/132910150
Recomendado
Clasificación