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