简 易 版 的 进 程 池 模 型 学 习
来源:互联网 发布:软件开发的岗位 编辑:程序博客网 时间:2024/05/29 18:12
简 易 版 的 进 程 池 模 型 学 习
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; //获取子进程的pid p[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;}
阅读全文
0 0
- 简 易 版 的 进 程 池 模 型 学 习
- 简 易 版 线 程 池 模 型 学 习
- 黑马程序员:JavaSE的 课 程 学 习
- 学 习 中 的 思 考
- [转]学计算机不能不学的语言
- 要学的东西,快点学
- 学语言就要学有对象的!
- 学外语的忌讳
- 学语言的困惑...
- 学外语的忌讳
- 学真正的linux
- 要学的很多
- 学数据结构的意义
- 学编程的感悟!
- 学软件的体会
- 学的很迷糊~
- 学汇编的方法
- 学软件的道路
- ongoDB——第一天 基础入门
- c
- 使用Python Flask框架快速实现简单API
- 图论500题——POJ_P1308 Is_It_A _tree?
- 复杂链表的赋值
- 简 易 版 的 进 程 池 模 型 学 习
- MyBatis的懒加载
- 多线程编程10例问题(6)
- xs128编程中遇到的两个warning
- 《C专家编程》总结
- C# xsd验证xml格式
- JDBC驱动的四种类型
- 线上讲座复盘,讲述背后不为人知的故事!
- PLSQL:oracle记录类型