简 易 版 的 进 程 池 模 型 学 习
生活随笔
收集整理的這篇文章主要介紹了
简 易 版 的 进 程 池 模 型 学 习
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
簡 易 ?版 的 進 程 池 模 ?型 學 習
1、進程池流程
父進程流程
第一步: make_child 初始化子進程 循環創建子進程,并初始化父進程的子進程管理結構體數組 child, 通過 socket_pair 將 socket描述符一端放入數組
子進程流程 recv_fd 等待父進程發送任務 send_file 發送文件數據 Write 向父進程發送完成任務
第二步: 父進程 epoll 監控 fd_listen 描述符。 父進程 epoll 監控 parr 結構體數組的 socket 描述符
第三步: While 1 啟動 epoll_wait, 等待是否有客戶端連接 有客戶端連接后, accept 獲得描述符, 循環找到非忙碌的子進程, 并發送給子進程, 標記對 應子進程忙碌。 當子進程完成任務后, 父進程一旦監控 socket 描述符可讀, 代表子進程非忙碌, 然后標記子 進程非忙碌。
2、頭文件及相關數據結構
#include<sys/stat.h> #include <sys/stat.h> #include<errno.h> #include <fcntl.h> #include <signal.h> #include <sys/select.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <netdb.h> #include <string.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <sys/epoll.h> #include<sys/wait.h>#define FILENAME "file"typedef struct{ //維護子進程的數據結構pid_t pid;int tfds;short busy; }child,*pchild; typedef struct{ //網絡數據發送的數據結構int len;char buf[1000]; }train,*ptrain;void send_fd(int fdw,int fd); void recv_fd(int fdr,int* fd); void make_child(pchild p,int len); void child_handle(int fdr); void send_data(int ); int send_n(int ,char *,int); int recv_n(int,char *,int);
3、父進程源代碼
#include"func.h"int exitfds[2]; //退出拉起管道 int exit_num=0; //用于退出時判斷機制void set_status(int fd) {int status=fcntl(fd,F_GETFL);status=status|O_NONBLOCK;fcntl(fd,F_SETFL,status); }void sighandle(int signum) {write(exitfds[1],"over",4); }int main(int argc,char **argv) { pipe(exitfds);signal(SIGINT,sighandle);if(4!=argc){perror("error argcs!\nplease enter IP SOCKET proccess_num\n");return -1;}int child_len=atoi(argv[3]);//首先為每個子進程動態申請空間,用指針實現數組效果pchild p=(pchild)calloc(child_len,sizeof(child)); //創建子進程,并初始化數據結構,//封裝成一個Init函數make_child(p,child_len);int sfd;sfd=socket(AF_INET,SOCK_STREAM,0);if(sfd==-1){perror("socket");return -1;}struct sockaddr_in ser;ser.sin_addr.s_addr=inet_addr(argv[1]);ser.sin_port=htons(atoi(argv[2]));ser.sin_family=AF_INET;int ret;ret=bind(sfd,(struct sockaddr *)&ser,sizeof(ser));if(-1==ret){perror("bind");return -1;}int epfd=epoll_create(1); //創造一個epfd的句柄struct epoll_event event,*evs;evs=(struct epoll_event *)calloc(child_len+1,sizeof(struct epoll_event));memset(&event,0,sizeof(event));event.data.fd=sfd;event.events=EPOLLIN;epoll_ctl(epfd,EPOLL_CTL_ADD,sfd,&event);int i;for(i=0;i<child_len;i++) //監管注冊每個子進程的描述符管道{event.data.fd=p[i].tfds;event.events=EPOLLIN;epoll_ctl(epfd,EPOLL_CTL_ADD,p[i].tfds,&event);}listen(sfd,child_len);int j,ret1,newfd;char flag[6];static int once=0;set_status(exitfds[0]);while(1){ret=read(exitfds[0],flag,sizeof(flag));if(ret>0){if(0==exit_num){printf("exerting quit action--please wait····\n");event.data.fd=sfd; //不在接受連接event.events=EPOLLIN;epoll_ctl(epfd,EPOLL_CTL_DEL,sfd,&event);for(i=0;i<child_len;i++){printf("正在處理子進程p[i].pid=%d ",p[i].pid);printf("\n");kill(p[i].pid,SIGKILL);}wait(NULL);printf("you have quited\n");return(0);}else{printf("請等待,有客戶正在交易···\n"); }}ret1=epoll_wait(epfd,evs,child_len+1,-1);for(i=0;i<ret1;i++){ //媽的,又他媽犯這種錯誤了if(evs[i].data.fd==sfd) //網絡請求到達{newfd=accept(sfd,NULL,NULL);//找到一個空閑的子進程,并且將newfd發送給它for(j=0;j<child_len;j++){if(p[j].busy==0){//通過tcp管道發送newfd給子進程send_fd(p[j].tfds,newfd); exit_num++;p[j].busy=1;printf("find a not busy proccess,send newfd success!\n");break;}}close(newfd); //關閉父進程對newfd的引用計數}//監聽子進程的tcp管道的發送完成信息for(j=0;j<child_len;j++) {if(evs[i].data.fd==p[j].tfds){//read(p[j].tfds,&flag,sizeof(flag));char buf[15]={0};read(p[j].tfds,buf,sizeof(buf));printf("%s\n",buf);//read(p[j].tfds,&flag,sizeof(flag));p[j].busy=0;exit_num--;printf("child proccess %d is not busy!\n",j);if(0==exit_num){printf("Are you stall want to finish this deal,please push Ctrl+C\n");}}}}} }
4、子進程流程代碼
#include"func.h"/*首先,要創建len個子進程,就會用到fork( )函數,于是,必須把父進程和子進程分別開來,一個父進程,len個子進程。 其次,初始化是針對定義的數據結構初始化,有: 1、每個子進程在fork創建之后需要根據返回值保存自己的pid. 2、對于全雙工的TCP管道,需要用socketpair( )函數初始化 3、剛開始時,將其每個子進程的busy位置為0,表示空閑 對于子進程,必須讓它在循環里面持續發送,從父進程接收任務,父進程將到發送數據 ,再到發送完,通知父進程,可以通過寫一個flag,然后父進程將其busy改為0, 表示空閑。 */ void child_handle(int fdr) {int newfd;char flag[6]={0};strcpy(flag,"over");while(1){recv_fd(fdr,&newfd);send_data(newfd);printf("I am child,send success!\n");write(fdr,&flag,sizeof(flag));//write(fdr,"hello",5);} }void make_child(pchild p,int len) {int fds[2],i,ret;pid_t pid;for(i=0;i<len;i++){ret=socketpair(AF_LOCAL,SOCK_STREAM,0,fds);pid=fork();if(0==pid){close(fds[1]);child_handle(fds[0]);//p[i].tfds=fds[0]; 將fds[0]端留給子進程} //讓其子進程在里面循環,不出來close(fds[0]);p[i].pid=pid; //獲取子進程的pidp[i].busy=0; //置為空閑p[i].tfds=fds[1]; //將管道的一端傳遞給主進程} }
5、父進程向子進程發送套接字文件描述符(內核信息)
#include "func.h"void send_fd(int fdw,int fd) {struct msghdr msg;memset(&msg,0,sizeof(msg));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;char buf1[10]="hello";char buf2[10]="world";struct iovec iov[2];iov[0].iov_base=buf1;iov[0].iov_len=5;iov[1].iov_base=buf2;iov[1].iov_len=5;msg.msg_iov=iov;msg.msg_iovlen=2;int 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));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;char buf1[10]="hello";char buf2[10]="world";struct iovec iov[2];iov[0].iov_base=buf1;iov[0].iov_len=5;iov[1].iov_base=buf2;iov[1].iov_len=5;msg.msg_iov=iov;msg.msg_iovlen=2;int ret=recvmsg(fdr,&msg,0);if(-1==ret){perror("sendmsg");return;}*fd=*(int*)CMSG_DATA(cmsg); }
6、子進程的主要邏輯業務----發送文件 #include"func.h"void send_data(int newfd) {train t;memset(&t,0,sizeof(t));strcpy(t.buf,FILENAME);t.len=strlen(FILENAME);printf("the strcpy t.buf is %s, len =%d\n",t.buf,t.len);//先發文件名int ret;ret=send_n(newfd,(char *)&t,t.len+4);if(ret==-1){perror("send");return;}int fd=open(FILENAME,O_RDONLY);if(-1==fd){perror("open");return;}struct stat filestat;fstat(fd,&filestat);t.len=sizeof(long);memcpy(t.buf,&filestat.st_size,sizeof(filestat.st_size));send_n(newfd,(char*)&t,4+t.len);while(memset(t.buf,0,sizeof(t.buf)),(t.len=read(fd,t.buf,sizeof(t.buf)))>0){send_n(newfd,(char *)&t,t.len+4);}t.len=0;send_n(newfd,(char *)&t,4+t.len);close(newfd);close(fd); }
7、為了匹配網絡兩端的發送速度,以及發送大文件時,可能的緩沖區大小的瓶頸,造成 ? ? ? ? ?? ?????的數據丟失,故需要精確控制要發送和接受的字節數,需要循環發送。
#include"func.h"int send_n(int sfd,char *p,int len) {int total=0,ret;while(total<len){ret=send(sfd,p+total,len-total,0);//取出每一次發送了多少個total=total+ret;}return 0; }int recv_n(int sfd,char *p,int len ) {int total=0,ret;while(total<len){ret=recv(sfd,p+total,len-total,0);total=total+ret;}return 0; }
8、客戶器端源代碼
#include<time.h> #include <sys/stat.h> #include<errno.h> #include <fcntl.h> #include <signal.h> #include <sys/select.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <netdb.h> #include <string.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <sys/epoll.h> #include<sys/wait.h>typedef struct{pid_t pid;int tfds;short busy; }child,*pchild;void send_fd(int fdw,int fd); void recv_fd(int fdr,int* fd); void make_child(pchild p,int len); void child_handle(int fdr); int recv_n(int,char *,int);int send_n(int sfd,char *p,int len) {int total=0,ret;while(total<len){ret=send(sfd,p+total,len-total,0);//取出每一次發送了多少個total=total+ret;}return 0; }int recv_n(int sfd,char *p,int len ) {int total=0,ret;while(total<len){ret=recv(sfd,p+total,len-total,0);total=total+ret;}return 0; }int main(int argc,char **argv) {if(3!=argc){printf("error argcs!\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));int ret,ret1;ser.sin_family=AF_INET;ser.sin_port=htons(atoi(argv[2]));ser.sin_addr.s_addr=inet_addr(argv[1]);ret=connect(sfd,(struct sockaddr*)&ser,sizeof(ser));int len;char buf[1000]={0};recv_n(sfd,(char *)&len,sizeof(len)); //先接受文件名recv_n(sfd,buf,len);int fd=open(buf,O_WRONLY|O_CREAT,0666);if(-1==fd){perror("open");return -1;}recv_n(sfd,(char *)&len,sizeof(len)); //在發送文件大小long file_len;recv_n(sfd,(char*)&file_len,len);long l=0;time_t now,check;time(&now);check=now;while(1){recv_n(sfd,(char *)&len,sizeof(len));if(len>0){l=l+len;memset(buf,0,sizeof(buf));recv_n(sfd,buf,len);//接受完后就要寫到襲擊者邊新建的文件里write(fd,buf,len);//接受多少就寫多少time(&now);if(now>check+1){printf("Now Data is %5.2f%s\r",100*(double)l/file_len,"%");fflush(stdout);check=now;}}else{printf("Down load successful!\n");break;}}close(fd);close(sfd);return 0; }
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎
總結
以上是生活随笔為你收集整理的简 易 版 的 进 程 池 模 型 学 习的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 那些年我们追过的网络库(PartI)
- 下一篇: input禁止鼠标滚轮改变数值