#pragma once
#include <fun.h> //包含各种需要用到的标准库和Linux库和一个检查返回值的宏定义,省略
#define FILENAME "test.txt"
//子进程状态信息数据结构
typedef struct{
int pid;//进程号
int fd; //通信套接字
int busy;//进程状态,0代表阻塞,1代表运行,-1代表死亡
}pro_data;
// 用来传输文件的数据结构
typedef struct{
int len;//控制信息,提示接下来的字节长度
char p[1024];//真正的数据信息
}Train_t;
//创建num子进程,传出参数p保存各个子进程的状态信息队列
int fork_child(int num,pro_data * p);
//向传入的套接字传输文件
int transp(int);
//子进程工作函数,传入参数是其与父进程通信的套接字接口
int work(int);
//传送文件描述符,第一个参数是目的描述符,第二个参数是要传送的文件描述符
int sendFD(int,int);
//接收文件描述符,第一个参数是接收信息的套接字描述符,第二个参数是传出参数,传出接收的文件描述符
int recvFD(int,int *);
客户端
#include <fun.h>
#include "pool.h"intmain(int args,char*argv[]){
struct winsize wsize;ioctl(STDOUT_FILENO, TIOCGWINSZ,&wsize);ARG_CHECK(args,3);int sfd =tcp_connect(argv[1],atoi(argv[2]));RET_CHECK(sfd,-1,"client tcp connetc");printf("successfully connetc server.\n");
Train_t train;int ret;//get filename and create fileint len;
ret =recv(sfd,&len,4,0);RET_CHECK(ret,-1,"服务器已满负荷,请稍候再试");
ret =recv(sfd,train.p,len,0);RET_CHECK(ret,-1,"getfilename");
train.p[ret]='\0';int fd =open(train.p,O_RDWR|O_CREAT,0666);if(fd==-1){printf("文件创建失败,可能存在同名文件!client exit.\n");close(sfd);return0;}//get size of file int size;
ret =recv(sfd,&size,sizeof(int),0);RET_CHECK(fd,-1,"recv");printf("the size of file is %d\n",size);printf("start downloading file %s\n",train.p);int getsize =0;while(1){
ret =recv(sfd,&len,4,MSG_WAITALL);RET_CHECK(ret,-1,"getlen");if(len ==0&& ret ==4){printf("\r");printf(">进度:%5.2f%s 已下载:%d",(double)getsize/size*100,"%",getsize);for(int i =0;i<(getsize/size)*(wsize.ws_row-35);i++){printf("-");}printf(">\ndownload is finished\n");close(fd);close(sfd);return0;}
ret =recv(sfd,&train.p,len,MSG_WAITALL);RET_CHECK(ret,-1,"recv");if(ret!=len){printf("download error!\n");close(fd);close(sfd);return0;}
getsize += ret;write(fd,train.p,len);printf("\r");printf(">进度:%5.2f%s 已下载:%d",(double)getsize/size*100,"%",getsize);for(int i =0;i<(double)getsize/size*(wsize.ws_col-35);i++){printf("-");}printf(">");}}
服务端父进程
intmain(int args,char*argv[]){signal(2,clean);ARG_CHECK(args,4);int process_number =atoi(argv[3]);
num =&process_number;
pro_data *p =(pro_data *)calloc(process_number,sizeof(pro_data));//创建子进程,保存子进程信息fork_child(process_number,p);
tp = p;//创建tcp端口等待客户端连接int sfd =tcp_init(argv[1],atoi(argv[2]));//自己封装的,需要自行实现,相当于socket,bind,listen三连//create epoll instance
efd =epoll_create(process_number+1);
struct epoll_event event,evs[11];
event.events = EPOLLIN;
event.data.fd = sfd;//监听服务端的listen端口int ret =epoll_ctl(efd,EPOLL_CTL_ADD,sfd,&event);RET_CHECK(ret,-1,"epoll_ctl");//添加子进程监听socketfor(int i =0;i<process_number;i++){
event.data.fd = p[i].fd;
ret =epoll_ctl(efd,EPOLL_CTL_ADD,p[i].fd,&event);RET_CHECK(ret,-1,"epoll_ctl")}//begin taskint newfd;int count;int i,j;int flag;printf("server is ready for download task\n");while(1){
count =epoll_wait(efd,evs,process_number+1,-1);//epoll实例状态改变,子进程退出删除监控事件导致的错误码,忽略处理if(count ==-1&&errno ==4){continue;}RET_CHECK(count,-1,"epoll_wait");for(i=0;i<count;i++){//sfd is ready, a new client is connectingif(evs[i].data.fd == sfd){
flag =0;
newfd =tcp_accept(sfd);RET_CHECK(newfd,-1,"accept");//find free child 查找空闲进程for(j=0;j<process_number;j++){if(!p[j].busy){
flag =1;
p[j].busy =1;printf("child process %d get the task.\n",j+1);sendFD(p[j].fd,newfd);close(newfd);break;}}//no child is free 没有空闲进程if(!flag){close(newfd);printf("no child is free,the task is rejected.\n");}}//check if a child is free 查看是否有子进程完成任务for(j =0;j < process_number; j++){if(evs[i].data.fd == p[j].fd&&p[j].busy ==1){read(p[j].fd,&ret,1);
p[j].busy =0;printf("child process %d completed his task.\n",j+1);break;}}}}return0;}
子进程创建和工作
//create children processes,创建子进程,子进程循环等待父进程唤醒intfork_child(int num,pro_data* p){int fds[2];int pid;int ret;for(int i=0;i<num;i++){//apply a pair of anonymous sockets 申请两个套接字,第一个用于子进程读写,第二个用于父进程读写
ret =socketpair(AF_LOCAL,SOCK_STREAM,0,fds);RET_CHECK(ret,-1,"socketpair");
pid =fork();//子进程if(0== pid){
#ifdef debug
printf("子进程%d创建成功\n",i);
#endif
close(fds[1]);//进入工作函数进行循环work(fds[0]);}//父进程
p[i].pid = pid;
p[i].busy =0;
p[i].fd = fds[1];close(fds[0]);}return0;}intwork(int fd){int sfd=0;//和客户端通信while(1){recvFD(fd,&sfd);
#ifdef debug
printf("pid= %d,sfd=%d\n",getpid(),sfd);
#endif
printf("pid =%d begin working.\n",getpid());transp(sfd);close(sfd);printf("pid =%d stop workinng.\n",getpid());write(fd,&sfd,1);}}
文件传输函数
inttransp(int sfd){
Train_t train;memset(&train,0,sizeof(train));int fd =open(FILENAME,O_RDONLY);RET_CHECK(fd,-1,"open");//transfer filename
train.len =strlen(FILENAME);strcpy(train.p,FILENAME);int ret =send(sfd,&train,4+train.len,0);if(-1==ret){printf("client is closed\n");close(sfd);close(fd);return0;}//transfer the size of file
struct stat statbuf;fstat(fd,&statbuf);
train.len=statbuf.st_size;
ret =send(sfd,&train.len,sizeof(int),0);if(-1==ret){printf("client is closed\n");close(sfd);close(fd);return0;}printf("pid = %d begin to transfer file %s\n",getpid(),FILENAME);while((train.len =read(fd,train.p,sizeof(train.p)))>0){
ret =send(sfd,&train,4+train.len,0);if(-1==ret){printf("client is closed\n");close(sfd);close(fd);return0;}}
train.len =0;send(sfd,&train,4,0);printf("pid = %d completed transfer of file %s\n",getpid(),FILENAME);close(fd);close(sfd);return0;}
子进程退出的信号处理函数
//全局变量
pro_data *tp;//指向进程信息队列int*num;//指向子进程总数目int efd;//指向epoll实例voidclean(int sig){//标志量,记录是否关闭了一个进程int no =0;//静态变量,记录关闭的总进程数目 staticint flag =0;
struct epoll_event event;
event.events = EPOLLIN;//关闭的数目等于总数if(flag ==*num){printf("all child processes are exit!\n");}//循环查找是否有空闲进程for(int i =0;i<*num;i++){if(tp[i].busy!=1&& tp[i].busy !=-1){
tp[i].busy =-1;
event.data.fd = tp[i].fd;epoll_ctl(efd,EPOLL_CTL_DEL,tp[i].fd,&event);close(tp[i].fd);kill(tp[i].pid,9);printf("child process pid= %d is exit.\n",tp[i].pid);
flag ++;
no =1;break;}}//没有找到空闲进程if(!no)printf("no free child process!\n");}