Linux C 多进程下载文件demo

1、使用多进程,实现多个客户端同时下载文件

目录:
目录

目录1

头文件

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <string.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define FILENAME "file"
typedef struct childdata{
    pid_t pid;
    int fdw;
    short busy;//标示子进程是否忙碌
}cdata,*pcdata;

typedef struct tdata{
    int len;
    char buf[1000];
}td,*ptd;
void make_child(pcdata,int);
void set_init(int,char*,char*);
void send_file(int);

main.c

#include "func.h"
int main(int argc,char* argv[])
{
    if(argc!=4)
    {
        printf("error args\n");
        return -1;
    }
    // 需要创建的进程数
    int count=atoi(argv[3]);
    // 开辟进程总数所消耗的内存
    pcdata p=(pcdata)calloc(count,sizeof(cdata));
    // 创建父进程和子进程
    make_child(p,count);
    int sfd;
    sfd=socket(AF_INET,SOCK_STREAM,0);
    if(-1==sfd)
    {
        perror("socket");
        return -1;
    }
    // call set_init  and bind
    set_init(sfd,argv[1],argv[2]);
    listen(sfd,count);

    // 进程之间的触发响应机制 
    int epfd;
    epfd=epoll_create(1);
    struct epoll_event event,*evs;
    evs=(struct epoll_event*)calloc(count+1,sizeof(event));
    memset(&event,0,sizeof(event));
    // 水平触发管理进程
    event.events=EPOLLIN;
    event.data.fd=sfd;
    int ret;
    ret=epoll_ctl(epfd,EPOLL_CTL_ADD,sfd,&event);
    if(-1==ret)
    {
        perror("epoll_ctl");
        return -1;
    }
    int i;
    // 管理每个进程的状态
    for(i=0;i<count;i++)
    {
        event.events=EPOLLIN;
        event.data.fd=p[i].fdw;
        epoll_ctl(epfd,EPOLL_CTL_ADD,p[i].fdw,&event);          
    }
    int j,ret1,new_fd;
    short flag;
while(1)
{
    memset(evs,0,(count+1)*sizeof(event));
    // 等待进程消息
    ret=epoll_wait(epfd,evs,count+1,-1);
    if(ret>0)
    {
        for(i=0;i<ret;i++)
        {
            // 进程之间有消息通信到来
            if(evs[i].data.fd==sfd) 
            {
               // 接收消息  
                new_fd=accept(sfd,NULL,NULL);
                // 遍历一下当前的进程数
                for(j=0;j<count;j++)
                {
                    // 非忙碌
                    if(p[j].busy==0) 
                    {
                    // 让该进程发送消息
                    send_fd(p[j].fdw,new_fd);
                    p[j].busy=1;        
                    break;
                    }               
                }
                close(new_fd);
            }

        for(j=0;j<count;j++)
        {
             // 有写入,说明可以读管道
            if(evs[i].data.fd==p[j].fdw)
            {
                read(p[j].fdw,&flag,sizeof(flag));
                p[j].busy=0;//把子进程置为非忙碌状态
                printf("run ok\n");                             
            }
            }
        }
    }       
}   
    close(sfd);
    close(epfd);
    return 0;
}

make_child。c

#include "func.h"
//实现子进程处理事件描述符
void handle_request(int fdr)
{
    int new_fd;
    short flag=1;
    while(1)
    {
        // 子进程之间接收消息
        recv_fd(fdr,&new_fd);  
        printf("new_fd=%d\n",new_fd);
        // 处理发送数据
        send_file(new_fd);
        // 改变子进程的状态
        write(fdr,&flag,sizeof(flag));      
    }   
}

// 创建子进程数
void make_child(pcdata p,int count)
{
    int fds[2];
    int i;
    pid_t pid;
    for(i=0;i<count;i++)
    {
        // 全双工tcp通信
        socketpair(AF_LOCAL,SOCK_STREAM,0,fds);
        pid=fork();
        if(0==pid)
        {
            // 关闭写端
            close(fds[1]);
            //子进程处理函数 
            handle_request(fds[0]);
        }
        // 父进程传递每个进程的状态
        p[i].pid=pid;
        // 父进程写端打开
        p[i].fdw=fds[1];
        p[i].busy=0;
        // 关闭读端
        close(fds[0]);
    }   
}

pool_n.c

#include "func.h"

void send_n(int fd,char *buf,int len)
{
    int total=0;
    int pos;
    while(total<len)
    {
        pos=send(fd,buf+total,len-total,0);
        total=total+pos;    
    }
}

void recv_n(int fd,char *buf,int len)
{

    int total=0;
    int pos;
    while(total<len)
    {
        pos=recv(fd,buf+total,len-total,0);
        total=total+pos;
    }
}

send_fd.c

#include "func.h"
// 发送描述符
void send_fd(int fdw,int fd)
{
    // 进程同时可以进行读和写,通信消息机制
    struct msghdr msg;
    // 接收消息
    memset(&msg,0,sizeof(msg));
    char buf[10]="hello";
    char buf1[10]=" world";
    struct iovec iov[2];
    iov[0].iov_base=buf;
    iov[0].iov_len=5;
    iov[1].iov_base=buf1;
    iov[1].iov_len=6;
    msg.msg_iov=iov;
    msg.msg_iovlen=2;
    // 处理消息
    struct cmsghdr *cmsg;
    int len=CMSG_LEN(sizeof(int));
    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;
    int ret;
    // 发送数据
    ret=sendmsg(fdw,&msg,0);
    if(-1==ret)
    {
        perror("sendmsg");
        return;
    }
}       

void recv_fd(int fdr,int* fd)
{
    struct msghdr msg;
    memset(&msg,0,sizeof(msg));
    char buf[10]="hello";
    char buf1[10]=" world";
    struct iovec iov[2];
    iov[0].iov_base=buf;
    iov[0].iov_len=5;
    iov[1].iov_base=buf1;
    iov[1].iov_len=6;
    msg.msg_iov=iov;
    msg.msg_iovlen=2;
    struct cmsghdr *cmsg;
    int len=CMSG_LEN(sizeof(int));
    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;
    int ret;
    ret=recvmsg(fdr,&msg,0);
    if(-1==ret)
    {
        perror("sendmsg");
        return;
    }
    *fd=*(int*)CMSG_DATA(cmsg);
}       

send_file.c

#include "func.h"

void send_file(int new_fd)
{   
    td t;
    memset(&t,0,sizeof(t));
    t.len=strlen(FILENAME);
    strcpy(t.buf,FILENAME);
    send_n(new_fd,&t,4+t.len);
    int fd;
    fd=open(FILENAME,O_RDONLY);
    if(-1==fd)
    {
        perror("open");
        return;
    }
    while(memset(&t,0,sizeof(t)),(t.len=read(fd,t.buf,sizeof(t.buf)))>0)
    {
        send_n(new_fd,&t,4+t.len);
    }
    t.len=0;
    send_n(new_fd,&t.len,4);//发送结束符给客户端
    close(new_fd);
}

send_init.c

#include "func.h"
// 初始化网络ip和端口绑定进程
void set_init(int sfd,char* ip,char* port)
{
    struct sockaddr_in ser;
    memset(&ser,0,sizeof(ser));
    ser.sin_family=AF_INET;
    ser.sin_port=htons(atoi(port));//port转网络字节序
    ser.sin_addr.s_addr=inet_addr(ip);//IP地址转网络字节序
    int ret;
    ret=bind(sfd,(struct sockaddr*)&ser,sizeof(ser));
    if(-1==ret)
    {
        perror("bind");
        return; 
    }
}

client

pool_n.c

#include "func.h"

void send_n(int fd,char *buf,int len)
{
    int total=0;
    int pos;
    while(total<len)
    {
        pos=send(fd,buf+total,len-total,0);
        total=total+pos;    
    }
}

void recv_n(int fd,char *buf,int len)
{

    int total=0;
    int pos;
    while(total<len)
    {
        pos=recv(fd,buf+total,len-total,0);
        total=total+pos;
    }
}

pro_pool_client.c

#include "func.h"
int main(int argc,char* argv[])
{
    if(argc!=3)
    {
        printf("error args\n");
        return -1;
    }
    int sfd;
    sfd=socket(AF_INET,SOCK_STREAM,0);
    if(-1==sfd)
    {
        perror("socket");
        return -1;
    }
    struct sockaddr_in ser;
    memset(&ser,0,sizeof(ser));
    ser.sin_family=AF_INET;
    ser.sin_port=htons(atoi(argv[2]));
    ser.sin_addr.s_addr=inet_addr(argv[1]);
    int ret;
    // connect
    ret=connect(sfd,(struct sockaddr*)&ser,sizeof(ser));
    if(-1==ret)
    {
        perror("connect");
        return -1;
    }
    char buf[1000]={0};
    int len;
    // client recv 文件描述符
    recv_n(sfd,(char*)&len,4);
    printf("len=%d\n",len);
    // 接收数据
    recv_n(sfd,buf,len);
    int fd;
    //本地创建文件
    fd=open(buf,O_RDWR|O_CREAT,0666);
    if(-1==fd)
    {
        perror("open");
        return -1;
    }
    while(1)
    {

        recv_n(sfd,(char*)&len,4);
        memset(buf,0,sizeof(buf));
        if(len >0) // 接收数据,并将buf写入
        {
            recv_n(sfd,buf,len);
            write(fd,buf,len);
        }else{
            break;
        }
    }
    close(fd);
    close(sfd);
}

服务器端:
这里写图片描述
客服端:
这里写图片描述

进程数
这里写图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值