Linux C 多进程下载文件demo

2/22/2017来源:ASP.NET技巧人气:1345

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

#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); }

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

进程数 这里写图片描述