一.多进程并发服务器

多进程并发服务器是一种经典的服务器架构,它经过创立多个子进程来处理客户端衔接,然后完成并发处理多个客户端恳求的能力。

概念:

  1. 服务器启动时,创立主进程,并绑定监听端口。
  2. 当有客户端衔接恳求时,主进程承受衔接,并创立一个子进程来处理该客户端衔接。
  3. 子进程与客户端进行通讯,处理恳求和发送呼应。
  4. 主进程持续监听新的衔接恳求。
  5. 子进程完成任务后,能够挑选停止或持续处理其他衔接,依据需求挑选是否重复循环。

长处:

  1. 高并发处理能力:每个子进程都能够独立地处理一个客户端衔接,运用多核处理器的优势,完成高并发处理能力,进步服务器的吞吐量
  2. 稳定性:每个子进程都是独立的,一个进程出现问题不会影响其他进程,进步了服务器的稳定性和容错能力。
  3. 简略直观:采用多进程模型完成并发服务器相对简略,代码可读性高,易于理解和维护。
  4. 跨渠道:多进程并发服务器概念适用于各种操作体系,不只限于Linux渠道。

缺陷:

  1. 高资源耗费:每个子进程都需求独立的资源,包括内存、文件描述符等,当并发衔接数较高时,会耗费很多的体系资源。
  2. 进程间通讯:各个子进程之间的通讯需求额定的机制,例如管道、同享内存等,增加了复杂性。
  3. 上下文切换开支:进程间切换的开支相对较大,会影响性能。
  4. 调试困难:因为每个子进程独立履行,调试和定位问题或许会变得复杂。

综上所述,多进程并发服务器能够满足高并发处理需求,并具有稳定性和简略性等长处。然而,它也存在资源耗费较高和调试困难等缺陷,依据实际需求和使用场景挑选合适的服务器架构是很重要的。

事例

运用多进程,在TCP通讯的基础上,完成服务器能够一起衔接多个客户端。完成大小写转化。

service.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;
}

client.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;
}

二.多线程并发服务器

多线程并发服务器是一种服务器架构,经过创立多个线程来处理客户端衔接,完成并发处理多个客户端恳求的能力。下面是多线程并发服务器的概念和一些长处:

概念:

  1. 服务器启动时,创立主线程,并绑定监听端口。
  2. 当有客户端衔接恳求时,主线程承受衔接,并创立一个或多个线程来处理该客户端衔接。
  3. 线程与客户端进行通讯,处理恳求和发送呼应。
  4. 主线程持续监听新的衔接恳求。
  5. 线程完成任务后,挑选停止或持续处理其他衔接,依据需求挑选是否重复循环。

长处:

  1. 资源功率:线程相比进程,创立和毁掉的开支较小,占用的资源较少,特别是同享的资源,如文件描述符,能够在多个衔接之间同享,进步了资源的运用功率。
  2. 呼应速度:线程之间的切换开支较小,内存空间同享,能够快速呼应客户端恳求,减少了衔接等待时间。
  3. 简略直观:与多进程相比,多线程完成并发服务器更简略直观,代码可读性高,易于理解和维护。
  4. 可扩展性:线程可轻松地增加和删除,习惯不断变化的衔接数需求,供给更好的可扩展性。
  5. 同享数据简洁:线程之间同享的数据结构能够直接访问,不需求像进程间通讯一样运用额定的机制。

缺陷:

  1. 数据同步问题:多线程同享数据或许导致竞态条件和数据一致性问题,需求运用锁或其他同步机制来确保线程安全。
  2. 调试困难:因为线程之间同享同一进程的内存空间,调试和定位问题或许会变得复杂。
  3. 并行性限制:在某些情况下,多线程服务器的并行性或许受限于体系中可用的CPU中心数量。
  4. 需求考虑线程安全:编写线程安全的代码或许需求更多的开发工作和考虑,防止数据竞赛和死锁等问题。

综上所述,多线程并发服务器能够满足高并发处理需求,并具有资源功率、呼应速度、简略性和可扩展性等长处。然而,它也存在数据同步问题和调试困难等缺陷,依据实际需求和使用场景挑选合适的服务器架构是很重要的。

事例

运用多线程,在TCP通讯的基础上,完成服务器能够一起衔接多个客户端。完成大小写转化。

service.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;
}

client.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;
}