C进程池实现文件的传输

工作流程图
在这里插入图片描述
头文件声明
prool.h

#include <stdio.h>
#include<sys/sendfile.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <dirent.h>
#include <time.h>
#include <pwd.h>
#include <grp.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/mman.h>
#include <sys/select.h>
#include <sys/time.h>
#include <pthread.h>
#include <setjmp.h>
#include <sys/time.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/msg.h>
#include <sys/uio.h>
#include <arpa/inet.h>
#include <sys/shm.h>
#include <sys/epoll.h>
#define ARGS_CHECK(argc, val) \
    if(argc != val) { \
        printf("error args\n"); \
        return -1; \
    } 


#define ERROR_CHECK(ret, retVal, funcName) { \
    if(ret == retVal) { \
        perror(funcName); \
        return -1; \
    } \
}

#define THREAD_ERROR_CHECK(ret, funcName) \
    do { \
        if (0 != ret) { \
            printf("%s : %s\n", funcName, strerror(ret)); \
        } \
    }while(0)


//父进程管理子进程的数据结构
typedef  struct {
    short flag;
    int pipefd;
    pid_t pid;
}process_data_t, *pProcess_data_t;
typedef  struct
{
    int len;
    char buf[1000];
}Train_t,*pTrain_t;
int makeChild(int processNum, pProcess_data_t pData);
int childFunc(int pipefd);
int tcpInit(char *ip, char *port, int *sockFd);
int epollAddFd(int fd, int epfd);
int sendFd(int pipeFd, int fd,char flag);
int recvFd(int pipeFd, int *fd,char *flag);

int recvCycle(int sockfd,void *buf,int totalLen);
int transFile(int clienFd);

TCP套接字初始化
tcpInit.c

#include "process_pool.h"

int tcpInit(char *ip, char *port, int *sockFd)
{
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    ERROR_CHECK(sfd, -1, "socket");

    struct sockaddr_in serAddr;
    memset(&serAddr, 0, sizeof(serAddr));
    serAddr.sin_family = AF_INET;
    serAddr.sin_addr.s_addr = inet_addr(ip);
    serAddr.sin_port = htons(atoi(port));

    //设置套接口地址可重用
    int reuse = 1; // 变量大于0,设置的选项生效
    setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));

    int ret = 0;
    ret = bind(sfd, (struct sockaddr*)&serAddr, sizeof(serAddr));
    ERROR_CHECK(ret, -1, "bind");

    ret = listen(sfd, 10);
    ERROR_CHECK(ret, -1, "listen");

    *sockFd = sfd;

    return 0;

}

主进程下,创建子进程,利用子进程创建进程池child.c

#include "process_pool.h"

int makeChild(int processNum, pProcess_data_t pData)
{
    int sfd[2];
    pid_t childPid = 0;
    for(int i = 0; i < processNum; ++i){
        socketpair(AF_LOCAL, SOCK_STREAM, 0, sfd);
        
        childPid = fork();
        if(0 == childPid){
            close(sfd[1]);
            childFunc(sfd[0]);
            exit(0);
        }

        close(sfd[0]);
        pData[i].flag = 0;
        pData[i].pid = childPid;
        pData[i].pipefd = sfd[1];
    }
    return 0;
}

int childFunc(int pipefd)
{
    int clienFd = 0;
    while(1){
        //接收父进程传来的newFd
        recvFd(pipefd, &clienFd,0);
        //写管道,使管道就绪,让父进程把自己的状态置为非忙碌的
        transFile(clienFd);        
        write(pipefd, "a", 1);
    }
    return 0;
}

epoll监听的封装epollAddFd.c

#include "process_pool.h"

int epollAddFd(int fd, int epfd)
{   
    struct epoll_event event;
    memset(&event, 0, sizeof(event));

    event.events = EPOLLIN;
    event.data.fd = fd;
    epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);

    return 0;
}

子进程下传递fd给进程池
sendFd

#include "process_pool.h"

int sendFd(int pipeFd, int fd)
{
    struct msghdr msg;
    memset(&msg, 0, sizeof(msg));

    struct iovec iov;
    memset(&iov, 0, sizeof(iov));
    char buf[8] = {0};
    strcpy(buf, "hi");

    iov.iov_base = buf;
    iov.iov_len = strlen(buf);

    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    int len = CMSG_LEN(sizeof(int));
    struct cmsghdr *cmsg = (struct cmsghdr*)calloc(1, len);

    cmsg->cmsg_len = len;
    cmsg->cmsg_level = SOL_SOCKET;
    cmsg->cmsg_type = SCM_RIGHTS;

    *(int*)CMSG_DATA(cmsg) = fd;
    
    msg.msg_control = cmsg;
    msg.msg_controllen = len;

    sendmsg(pipeFd, &msg, 0);
    return 0;
}

进程池接收文件描述符和信息

#include "process_pool.h"

int recvFd(int pipeFd, int *fd)
{
    struct msghdr msg;
    memset(&msg, 0, sizeof(msg));

    struct iovec iov;
    memset(&iov, 0, sizeof(iov));
    char buf[8] = {0};
    strcpy(buf, "hi");

    iov.iov_base = buf;
    iov.iov_len = strlen(buf);

    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    int len = CMSG_LEN(sizeof(int));
    struct cmsghdr *cmsg = (struct cmsghdr*)calloc(1, len);

    cmsg->cmsg_len = len;
    cmsg->cmsg_level = SOL_SOCKET;
    cmsg->cmsg_type = SCM_RIGHTS;
    
    msg.msg_control = cmsg;
    msg.msg_controllen = len;

    recvmsg(pipeFd, &msg, 0);
    *fd = *(int*)CMSG_DATA(cmsg);

    return 0;
}

进程池中的进程负责传输文件

#include "process_pool.h"

void sigfunc(int signum)
{
    printf("sig is comming\n");
}

int transFile(int clienFd)
{

    signal(SIGPIPE, sigfunc);
    //使用私有协议发送数据,人为的规定发送数据的边界
    Train_t train;
    memset(&train, 0, sizeof(train));
    int fd = open("file", O_RDWR);

    //存储文件名长度
    train.len = 4;
    strcpy(train.buf, "file");

    //发送文件名
    send(clienFd, &train, 4 + train.len, 0);


    while(1){
        int ret = read(fd, train.buf, sizeof(train.buf));
        train.len = ret;

        //send以一个已经断开的连接,会返回-1, 在次send的时候,会收到SIGPIPE信号
        //终止子进程
        int ret1 = send(clienFd, &train, 4 + train.len, 0);
        if(0 == ret){
            break;
        }
        if(-1 == ret1){
            printf("client exit\n");
            break;
        }

    }

    return 0;

}

主函数

#include "process_pool.h"
int pipe1[2];
void sigFunc(int sig)
{
    printf("sig \n");
    write(pipe1[1],&sig,4);
    //把、写入管道
}
int main(int argc, char** argv)
{
    //ip,port, processNum;
    int processNum = atoi(argv[3]);
    pProcess_data_t pData  = (pProcess_data_t)calloc(processNum,sizeof(process_data_t)); 
    makeChild(processNum,pData);
    //循环创建子进程
    pipe(pipe1);
    signal(SIGUSR1,sigFunc);
    
    int sfd;
    tcpInit(argv[1],argv[2],&sfd);
    //将sfd传出
    int epfd = epoll_create(1);

    epollAddFd(sfd,epfd);
    //将文件描述符纳入监听
    epollAddFd(pipe1[0],epfd);
    //把管道的读端纳入监听,如果是发送信号了,读端肯定就有数据了,证明来信号了

   
    for(int i = 0 ;i< processNum; i++)
    {
        //循环将管道的读端纳入监听
        epollAddFd(pData[i].pipefd,epfd);
    }
    int newfd;
    struct epoll_event evs[2];
    int num;
    while(1)
    {
        num = epoll_wait(epfd,evs,2,-1);
        for(int i = 0 ; i<num; i++)
        {
            if(evs[i].data.fd == sfd)
            {
                newfd = accept(sfd,NULL,NULL);
                for(int j = 0 ;j< processNum ; j++)
                {
                    if(pData[j].flag == 0)
                    {
                        sendFd(pData[j].pipefd, newfd,0);
                        break;
                    }
                }
                close(newfd);
            }
            if(evs[i].data.fd == pipe1[0])
            {
                //退出1,暴力
                /*
                for(int j=0 ;j<processNum; j++)
                {
                    kill(pData[j].pid,SIGUSR1);
                }
                */   
                for(int j=0 ;j<processNum; j++)
                {
                    //循环遍历所有子进程
                    sendFd(pData[i].pipefd,0,1);
                    //发送标志位1过去,证明要关闭了
                }
                for(int j = 0;j<processNum; j++)
                {
                    int s;
                    wait(&s);//捕获退出状态
                    if(WIFEXITED(s))
                    {
                        printf("num =%d \n",WEXITSTATUS(s));
                    }
               }
                    exit(0);//主进程也跟着完蛋了
            }
            else
            {

                for(int j = 0 ;j< processNum ; j++)
                {
                    if(pData[j].pipefd == evs[i].data.fd)
                    {
                        char buf[4] = {0};
                        read(pData[j].pipefd,buf,sizeof(buf));
                        pData[j].flag = 0;
                        break;
                    }
                }
            }
        }
    }
} 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个使用C语言实现的简单进程的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> #define MAX_PROCESSES 10 #define MAX_TASKS 20 typedef struct { void (*task)(void *); void *arg; } task_t; typedef struct { pid_t pid; int busy; int pipefd[2]; } worker_t; typedef struct { int count; worker_t workers[MAX_PROCESSES]; } process_pool_t; void worker_process(int pipefd[2]) { while (1) { task_t task; int n = read(pipefd[0], &task, sizeof(task)); if (n == 0) { break; } task.task(task.arg); } close(pipefd[0]); close(pipefd[1]); exit(0); } void process_pool_init(process_pool_t *pool) { pool->count = 0; } void process_pool_add_worker(process_pool_t *pool) { worker_t *worker = &pool->workers[pool->count++]; pipe(worker->pipefd); worker->pid = fork(); if (worker->pid == -1) { perror("fork"); exit(1); } else if (worker->pid == 0) { close(worker->pipefd[1]); worker_process(worker->pipefd); } else { close(worker->pipefd[0]); worker->busy = 0; } } void process_pool_submit_task(process_pool_t *pool, task_t task) { int i; for (i = 0; i < pool->count; i++) { if (!pool->workers[i].busy) { write(pool->workers[i].pipefd[1], &task, sizeof(task)); pool->workers[i].busy = 1; return; } } wait(NULL); process_pool_submit_task(pool, task); } void print_task(void *arg) { int *value = (int *)arg; printf("Task: %d\n", *value); sleep(1); } int main() { process_pool_t pool; process_pool_init(&pool); int i; for (i = 0; i < MAX_PROCESSES; i++) { process_pool_add_worker(&pool); } for (i = 0; i < MAX_TASKS; i++) { int *value = malloc(sizeof(int)); *value = i; task_t task = {print_task, value}; process_pool_submit_task(&pool, task); } for (i = 0; i < pool.count; i++) { write(pool.workers[i].pipefd[1], NULL, 0); } for (i = 0; i < pool.count; i++) { waitpid(pool.workers[i].pid, NULL, 0); } return 0; } ``` 在这个例子中,我们使用了一个进程来管理多个工作进程,执行任务。每个工作进程都通过一个管道来接收任务,当有任务需要执行时,进程会选择一个空闲的工作进程,将任务发送给它执行。当所有任务都执行完毕后,进程会向每个工作进程发送一个空消息,通知它们退出。在主函数中,我们创建了20个任务,然后将它们提交给进程来执行。在任务函数中,我们简单地打印出任务的编号并休眠一秒钟,模拟任务的执行过程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值