• 多线程|多进程|高并发网络编程


    在这里插入图片描述

    一.多进程并发服务器

    多进程并发服务器是一种经典的服务器架构,它通过创建多个子进程来处理客户端连接,从而实现并发处理多个客户端请求的能力。

    概念:

    1. 服务器启动时,创建主进程,并绑定监听端口。
    2. 当有客户端连接请求时,主进程接受连接,并创建一个子进程来处理该客户端连接。
    3. 子进程与客户端进行通信,处理请求和发送响应。
    4. 主进程继续监听新的连接请求。
    5. 子进程完成任务后,可以选择终止或继续处理其他连接,根据需求选择是否重复循环。

    优点:

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

    缺点:

    1. 高资源消耗:每个子进程都需要独立的资源,包括内存、文件描述符等,当并发连接数较高时,会消耗大量的系统资源。
    2. 进程间通信:各个子进程之间的通信需要额外的机制,例如管道、共享内存等,增加了复杂性。
    3. 上下文切换开销:进程间切换的开销相对较大,会影响性能。
    4. 调试困难:由于每个子进程独立执行,调试和定位问题可能会变得复杂。

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

    案例

    利用多进程,在TCP通信的基础上,实现服务器可以同时连接多个客户端。实现大小写转换。

    service.c

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112

    client.c

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    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
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    二.多线程并发服务器

    多线程并发服务器是一种服务器架构,通过创建多个线程来处理客户端连接,实现并发处理多个客户端请求的能力。下面是多线程并发服务器的概念和一些优点:

    概念:

    1. 服务器启动时,创建主线程,并绑定监听端口。
    2. 当有客户端连接请求时,主线程接受连接,并创建一个或多个线程来处理该客户端连接。
    3. 线程与客户端进行通信,处理请求和发送响应。
    4. 主线程继续监听新的连接请求。
    5. 线程完成任务后,选择终止或继续处理其他连接,根据需求选择是否重复循环。

    优点:

    1. 资源效率:线程相比进程,创建和销毁的开销较小,占用的资源较少,特别是共享的资源,如文件描述符,可以在多个连接之间共享,提高了资源的利用效率。
    2. 响应速度:线程之间的切换开销较小,内存空间共享,可以快速响应客户端请求,减少了连接等待时间。
    3. 简单直观:与多进程相比,多线程实现并发服务器更简单直观,代码可读性高,易于理解和维护。
    4. 可扩展性:线程可轻松地添加和删除,适应不断变化的连接数需求,提供更好的可扩展性。
    5. 共享数据简便:线程之间共享的数据结构可以直接访问,不需要像进程间通信一样使用额外的机制。

    缺点:

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

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

    案例

    利用多线程,在TCP通信的基础上,实现服务器可以同时连接多个客户端。实现大小写转换。

    service.c

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111

    client.c

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    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
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
  • 相关阅读:
    手机怎么把照片转JPG格式?这三种手机小技巧需要知道
    【论文阅读】Graph Fusion Network for Text Classification
    《精通嵌入式Linux编程》——解锁嵌入式Linux开发的无限可能
    基于YOLOv8模型的120类狗狗目标检测系统(PyTorch+Pyside6+YOLOv8模型)
    网络参考资料搬运(3)
    MAX3072EESA+T RS-485/RS-422半双工收发器
    Git安装、原理、常用命令、版本控制、如何上传普通文件到仓库以及如何修改IDEA中Terminal为git窗口
    vulnhub——The Planets: Earth
    Spire.Office for NET 全家桶最新版7.7.6
    经典CAN2.0 DBC快速切换为CANFD DBC
  • 原文地址:https://blog.csdn.net/m0_73731708/article/details/132910150