【C语言】多进程/多线程

【C语言】多进程/多线程

参考链接

c 中文网
菜鸟 c

多进程/多线程服务器

多进程和多线程是常用的并发编程技术。它们都允许程序同时执行多个任务,提高了系统的资源利用率和程序的运行效率。

1. 多进程服务器

多进程是指在操作系统中同时运行多个独立的进程。每个进程都有自己独立的地址空间和资源,进程间的通信通过操作系统提供的进程间通信机制进行。多进程可以充分利用多核处理器的优势,提高系统的整体性能。然而,进程间的切换会引入较大的开销,并且需要较高的内存开销。

服务器使用 fork 创建子进程来和客户端进行通信,父进程负责取出连接请求。

c 复制代码
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <strings.h>
#include <string.h>
#include <ctype.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <arpa/inet.h>

// 信号处理函数
void waitchild(int signo)
{
    pid_t wpid;
    while (1)
    {
        wpid = waitpid(-1, NULL, WNOHANG);
        if (wpid > 0)
        {
            printf("child exit, wpid==[%d]\n", wpid);
        }
        else if (wpid == 0 || wpid == -1)
        {
            break;
        }
    }
}

int main()
{
    // 阻塞SIGCHLD信号
    sigset_t mask;
    sigemptyset(&mask);
    sigaddset(&mask, SIGCHLD);
    sigprocmask(SIG_BLOCK, &mask, NULL);
    int sigbol = 1;

    int sfd = socket(AF_INET, SOCK_STREAM, 0);

    // 设置端口复用
    int opt = 1;
    setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));

    struct sockaddr_in soaddr;
    bzero(&soaddr, sizeof(soaddr));

    soaddr.sin_family = AF_INET;
    soaddr.sin_port = htons(9999);
    soaddr.sin_addr.s_addr = htonl(INADDR_ANY);

    bind(sfd, (struct sockaddr *)&soaddr, sizeof(soaddr));

    //监听-listen
    listen(sfd, 128);

    struct sockaddr_in clientsocket;
    socklen_t clilen;

    char sIP[16];

    while (1)
    {
        clilen = sizeof(clientsocket);
        bzero(&clientsocket, clilen);

        int cfd = accept(sfd, (struct sockaddr *)&clientsocket, &clilen);

        /* */
        int pid = fork();
        if (pid == 0)
        {
            // 子进程
            close(sfd);
            char buff[64];
            printf("current pid is [%d],father is [%d]\n", getpid(), getppid());
            while (1)
            {
                memset(buff, 0x00, sizeof(buff));
                int n = read(cfd, buff, sizeof(buff));
                if (n == 0)
                {
                    return 0;
                }
                else if (n < 0)
                {
                    perror("child read error");
                    return -1;
                }
                printf("child [%d] recv data from [%s:%d]:[%s]\n", getpid(), inet_ntop(AF_INET, &clientsocket.sin_addr.s_addr, sIP, sizeof(sIP)), ntohs(clientsocket.sin_port), buff);
                for (int i = 0; i < n; i++)
                {
                    buff[i] = toupper(buff[i]);
                }
                n = Write(cfd, buff, n);
                if (n <= 0)
                {
                    perror("child write error");
                    return -1;
                }
            }
        }
        else if (pid > 0)
        {
            // 父进程
            close(cfd);

            //假如是初次fork子进程,那么才注册信号处理函数
            if (sigbol == 1)
            {
                sigbol = 0;
                // 注册SIGCHLD信号处理函数
                struct sigaction act;
                act.sa_handler = waitchild;
                act.sa_flags = 0;
                sigemptyset(&act.sa_mask);
                sigaction(SIGCHLD, &act, NULL);

                // 解除对SIGCHLD信号的阻塞
                sigprocmask(SIG_UNBLOCK, &mask, NULL);
            }
            //循环等待下一个连接请求的到来
            continue;
        }
        else
        {
            perror("fork error");
            close(sfd);
            return -1;
        }


    }

    return 0;
}

2. 多线程服务器

多线程是指在同一个进程中同时运行多个独立的线程。与进程不同,线程共享同一个地址空间和资源,可以通过共享内存等方式进行线程间的通信。多线程可以减少线程间的切换开销和内存开销,提高系统的响应速度和资源利用率。然而,多线程编程需要考虑线程安全问题,需要使用线程同步技术来保证共享资源的正确访问。

主线程创建子线程,用子进程和客户端通信。

c 复制代码
#include <arpa/inet.h>
#include <pthread.h>
#include <strings.h>
#include <string.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <unistd.h>

typedef struct info
{
    int cfd; // 若为-1表示可用, 大于0表示已被占用
    int idx;
    pthread_t thread;          // 由pthread_create 返回
    struct sockaddr_in client; // 由accept 返回
} INFO;

INFO thInfo[1024];

void initThreadArr()
{
    for (int i = 0; i < 1024; i++)
    {
        bzero(&thInfo[i],sizeof(thInfo[i]));
        thInfo[i].cfd = -1;
    }
}

int findIndex()
{
    int i;
    for (i = 0; i < 1024; i++)
    {
        if (thInfo[i].cfd == -1)
        {
            return i;
        }
    }
    //if (i == 1024)
    //{
    //    return -1;
    //}
    return -1;
}

void *threadFunc(void *arg)
{
    INFO *curthread = (INFO *)arg;
    char sIP[16];
    printf("current thread id [%ld],arr index is [%d],cfd is [%d],client ip is [%s:%d]\n", pthread_self(), curthread->idx, curthread->cfd, inet_ntop(AF_INET, &curthread->client.sin_addr.s_addr, sIP, sizeof(sIP)), ntohs(curthread->client.sin_port));
    char buff[64];
    while (1)
    {
        memset(buff, 0x00, sizeof(buff));
        int n = read(curthread->cfd, buff, sizeof(buff));
        if (n == 0)
        {
            bzero(&thInfo[curthread->idx],sizeof(thInfo[curthread->idx]));
            thInfo[thInfo->idx].cfd = -1;
            return 0;
        }
        else if (n < 0)
        {
            bzero(&thInfo[curthread->idx],sizeof(thInfo[curthread->idx]));
            thInfo[thInfo->idx].cfd = -1;
            perror("child read error");
            return 0;
        }

        printf("child thread [%ld] recv data from [%s:%d]:[%s]\n", pthread_self(), inet_ntop(AF_INET, &curthread->client.sin_addr.s_addr, sIP, sizeof(sIP)), ntohs(curthread->client.sin_port), buff);
        for (int i = 0; i < n; i++)
        {
            buff[i] = toupper(buff[i]);
        }
        n = write(curthread->cfd, buff, n);
        if (n <= 0)
        {
            bzero(&thInfo[curthread->idx],sizeof(thInfo[curthread->idx]));
            thInfo[thInfo->idx].cfd = -1;
            perror("child write error");
            return 0;
        }
    }
}

int main()
{

    initThreadArr();

    int sfd = socket(AF_INET, SOCK_STREAM, 0);

    // 设置端口复用
    int opt = 1;
    setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(int));

    struct sockaddr_in soaddr;
    bzero(&soaddr, sizeof(soaddr));

    soaddr.sin_family = AF_INET;
    soaddr.sin_port = htons(9999);
    soaddr.sin_addr.s_addr = htonl(INADDR_ANY);

    bind(sfd, (struct sockaddr *)&soaddr, sizeof(soaddr));

    // 监听-listen
    listen(sfd, 128);

    struct sockaddr_in clientsocket;
    socklen_t clilen;

    int cfd;
    int index;
    int ret;

    while (1)
    {
        index = -1;
        clilen = sizeof(clientsocket);
        bzero(&clientsocket, clilen);

        cfd = accept(sfd, (struct sockaddr *)&clientsocket, &clilen);
        // 从线程数组中找一个可以用的
        index = findIndex();
        thInfo[index].idx = index;
        thInfo[index].client = clientsocket;
        thInfo[index].cfd = cfd;

        // 创建线程
        ret = pthread_create(&thInfo[index].thread, NULL, threadFunc, &thInfo[index]);

        if (ret != 0)
        {
            printf("create thread error:[%s]\n", strerror(ret));
            exit(-1);
        }

        // 设置子线程为分离属性
        pthread_detach(thInfo[index].thread);
    }

    Close(sfd);

    return 0;
}

结语

多进程和多线程的选择取决于具体的应用场景。如果任务之间需要较高的隔离度,或者需要充分利用多核处理器的优势,可以选择多进程。如果任务之间需要较低的切换开销和内存开销,或者需要提高系统的响应速度和资源利用率,可以选择多线程。

参考链接

c 中文网
菜鸟 c

相关推荐
喜欢理工科1 小时前
18 C语言标准头文件
c语言·python·算法·c语言标准头文件
sakabu3 小时前
Linux安装MySQL数据库并使用C语言进行数据库开发
linux·c语言·数据库·笔记·mysql·数据库开发
Peter_Deng.4 小时前
单片机 - 位运算详解(`&`、`|`、`~`、`^`、`>>`、`<<`)
c语言·单片机·嵌入式硬件
阿巴~阿巴~5 小时前
2024年3月全国计算机等级考试真题(二级C语言)
c语言
王RuaRua6 小时前
[数据结构]1.时间复杂度和空间复杂度
c语言·数据结构·算法
wuqingshun3141597 小时前
蓝桥杯 R格式
c语言·c++·算法·职场和发展·蓝桥杯·r语言
努力努力再努力wz7 小时前
【c++入门系列】:引用以及内联函数详解
java·运维·服务器·c语言·开发语言·c++
三体世界8 小时前
C++ STL 序列式容器之(三)-- List
java·c语言·开发语言·c++·windows·list·visual studio
慕洋男孩9 小时前
LeetCode(977):有序数组的平方
c语言·leetcode
大锦终10 小时前
详解vector容器
c语言·开发语言·数据结构·c++