线程池

一、线程池:
因为父线程和子线程公用文件描述符,所以线程池并不像进程池一样,需要socketpair、sendmsg、recvmsg来传递描述符。
当有连接请求时,父进程产生一个new_fd,放到一个队列中。而子线程从队列中往外取new_fd,进行处理。

需要的数据结构:
一个共同的路径信息。保存现在用户所在的当前路径。

父线程流程:
1.socket();
2.bind();
3.listen() 监听客户端的请求
4.队列的初始化。
5.产生num个子线程。每个子线程有自己不同的事。所以用num个条件变量。
5.accept(),收到请求后,产生一个new_fd中。主线程与客户端进行通话,看客户端需要什么操作。根据不同的操作,signal通知不同的子线程进行处理。

子线程:
1.子线程等待被唤醒,去做自己的事。如果为空就wait();
2.

客户端:
1.socket();
2.connect();
3.接收数据

细节一:头文件如何防止重复引用 
在一个大的软件工程里面,可能会有多个文件同时包含一个头文件,当这些文件编译链接成一个可执行文件时,就会出现大量重定义的错误。在头文件中实用#ifndef #define #endif能避免头文件的重定义。

方法:例如要编写头文件test.h
#ifndef _TEST_H       //在头文件开头写上两行。一般是文件名的大写,这样便于看。
#define _TEST_H
...       
#endif                        //头文件结尾写上一行. 这样一个工程文件里同时包含两个test.h时,就不会出现重定义的错误了。

分析:当第一次包含test.h时,由于没有定义_TEST_H,条件为真,这样就会包含(执行)#ifndef _TEST_H和#endif之间的代码,当第二次包含test.h时前面一次已经定义了_TEST_H,条件为假,#ifndef _TEST_H和#endif之间的代码也就不会再次被包含,这样就避免了重定义了。主要用于防止重复定义宏和重复包含头文件。


线程池:服务器端       
/******head.h******/
#ifndef __FUNC_H__
#define __FUNC_H__
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#endif

/*****work_que.h*****/
#ifndef __WOEK_QUE_H__
#define __WOEK_QUE_H__
#include "head.h"
typedef struct node{                               //队列的节点
        int new_fd;                                    //主线程接收到新的请求产生一个new_fd,放到队列中
        struct node* pnext;                      
}Node,*pNode
typedef struct{
        pNode phead,ptail;                         //队列的头尾指针
         pthread_mutex_t mutex;                  //队列包含一个锁,要求互斥地访问队列
        int size;                                         //当前队列中元素个数
}que_t,*pque_t;

void que_init(pque_t);
void que_insert(pque_t,int);
void que_get(pque_t,int*);
#endif

/******factory.h*******/
#ifndef __FACTORY_H__
#define __FACTORY_H__
#include "head.h"
#include "work_que.h"

#define FILENAME "file"
typedef void* (* pfunc)(void*);                 //定义一个pfunc类型。这是一个函数指针类型。  

typedef struct{                                        //工厂结构体 
        pthread_t*  thread;                           //num个线程ID的首地址 
        int  th_num;                                     //要创建多少个线程 
        pthread_cond_t  cond;                      //条件变量,用于线程之间的同步 
        int  capacity;                                    //最多同时有多少个请求 
        que_t  que;                                      //工厂有一个队列 
        pfunc  func_sendfile;                        //线程函数指针 
        int  flag;                                          //标记,避免工厂启动多次 
}factory,*pf;

typedef struct{                                        //小火车数据类型 
        int len;
        char buf[1000];
}data,*pdata;

void factory_init(pf,int,int,pfunc);             //工厂数据结构初始化 
void factory_start(pf);                              //工厂启动 
void* child_thread(void*);

#endif

/*****send_file.c*****/
#include "head.h"
void  send_n(int new_fd,char* buf,int len){
        int ret;
        int total=0;
        while(total
                ret=send(new_fd,buf+total,len-total,0);
                total=total+ret;
        }
}

void  recv_n(int new_fd,char* buf,int len){
        int ret;
        int total=0;
        while(total
                ret=recv(new_fd,buf+total,len-total,0);
                total=total+ret;
        }
}

void  send_file(int fdw){                  
        data d;
        memset(&d,0,sizeof(d));
        d.len=strlen(FILENAME);
        strcpy(d.buf,FILENAME);
        int ret;
        ret=send(fdw,&d,4+d.len,0);                                     //发送文件名
        if(-1==ret){
                perror("send");
                exit(-1);
        }
        int fd=open(FILENAME,O_RDONLY);
        if(-1==fd){
                perror("open");
                exit(-1);
        }
        while(memset(&d,0,sizeof(d)),(d.len=read(fd,d.buf,sizeof(d.buf)))>0){
                send_n(fdw,&d,4+d.len);
        }
        ret=0;
        send_n(fdw,&ret,sizeof(int));                                    //发送文件结束标志
        close(fdw);
}

work_que.c
#include "work_que.h"

void  que_init(pque_t pq){                                                      //初始化队列 
        pq->phead=NULL;
        pq->ptail=NULL;
         pthread_mutex_init(&pq->mutex,NULL);                       //初始化队列的锁 
        pq->size=0;
}

void  que_insert(pque_t pq,int fd){                                          //把新产生的fd放到队列中 
        pNode pnew=(pNode)calloc(1,sizeof(Node));                  //新建一个节点,存放fd
        pnew->new_fd=fd;
         pthread_mutex_lock(&pq->mutex);                               //互斥地访问队列 
        if(pq->phead==NULL){                                                //采用尾插法,插入新的节点
                pq->phead=pnew;
                pq->ptail=pnew;
        }else{
                pq->ptail->pnext=pnew;
                pq->ptail=pnew;
        }
        (pq->size)++;
         pthread_mutex_unlock(&pq->mutex);
}
void  que_get(pque_t pq,int* fd){                                             //取出一个new_fd 
        pNode pcur;
        pcur=pq->phead;
        *fd=pcur->new_fd;                                                        //把队首的元素取出来
        pq->phead=pcur->pnext;
        free(pcur);                                                                     //尽量把malloc和free放到锁外面
        if(pq->phead == NULL){
                pq->ptail=NULL;
        }
        (pq->size)--;
         pcur=NULL;                                                                   //防止产生野指针
}

/*****factory.c*****/  
#include "factory.h"

void  factory_init(pf p,int  num,int cap,pfunc  func_sendfile){    //初始化工厂:num为要创建的线程数,cap是同时请求的最大数目,func_sendfile                                                                                              是一个函数指针类型的变量。所以func_sendfile等于child_thread,指向子线程 
        p->thread=(pthread_t*) calloc(num,sizeof(pthread_t));      //num个线程ID的首地址.pthread_t pth_id[num].因为num变化,所以calloc。 
        p->th_num=num;                                                          //要创建的线程数
         pthread_cond_init(&p->cond,NULL);                           //条件变量初始化
        p->capacity=cap;                                                          //最多同时有多少个请求
         que_init(&p->que);                                                       //初始化队列。因为要改变队列的值,所以传地址
        p->func_sendfile=func_sendfile;                                    //p->func_sendfile也是函数指针类型。所以p->func_sendfile等于child_thread 
        p->flag=0;                     
}

void  factory_start(pf p){                                                        //工厂启动。p是factory * 类型 
        int i;
        if( p->flag ==0){                                                            //如果工厂未被启动 
                for(i=0;ith_num;i++){
                         pthread_create(& p->thread[i],NULL, p->func_sendfile,(void*)p);    //创建p->th_num个子线程。并把p作为参数传递给子线程
                }
        }
        p->flag=1;                                                                    //标记工厂已经被启动 
}

/*****main.c*******/
#include "factory.h"

void* child_thread(void* p){                                                 
        pf p1=(pf)p;
        int new_fd;
        while(1){
                 pthread_mutex_lock(&p1->que.mutex);                   //互斥地访问队列  
                if(p1->que.size==0){                                               //如果进程中没有new_fd,就等待被唤醒 
                        pthread_cond_wait(&p1->cond,&p1->que.mutex);
                }
                 que_get(&p1->que,&new_fd);                                 //取出一个new_fd,返回给new_fd 
                 pthread_mutex_unlock(&p1->que.mutex);       
                 send_file(new_fd);                                                  //发送文件。同进程池的send_file函数 
        }
}

int main(int argc,char* argv[]){                                      //参数:IP地址  端口  创建的线程数  capacity
        if(argc!=5){
                printf("error args\n");
                return -1;
        }
        int  num=atoi(argv[3]);                                          //要创建的线程数
        int  cap=atoi(argv[4]);                                           //容量,最多多少个线程连接
        factory f;
         factory_init(&f,num,cap,child_thread);                   //初始化工厂。child_thread是函数的地址(函数指针类型)。   
         factory_start(&f);                                                  //工厂启动:创建num个子线程。
        int 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]));//一定要用htons
        ser.sin_addr.s_addr=inet_addr(argv[1]);
        int ret;
        ret= bind(sfd,(struct sockaddr*)&ser,sizeof(struct sockaddr));
        if(-1==ret){
                perror("bind");
                return -1;
        }
         listen(sfd, cap);                                                     //最大可接受的请求的数量,即队列中最多有多少个new_fd
        int new_fd;
        while(1){
                new_fd= accept(sfd,NULL,NULL);
                if(-1==new_fd){
                        perror("accept");
                        return -1;
                }
                 que_insert(&f.que,new_fd);                           //把新产生的new_fd放到队列中 
                 pthread_cond_signal(&f.cond);                      //唤醒一个子进程 
        }
}       

//客户端:与进程池客户端完全相同       
#include "func.h"
void send_n(int new_fd,char* buf,int len){                     
        int ret;
        int total=0;
        while(total
                ret=send(new_fd,buf+total,len-total,0);
                total=total+ret;
        }
}

void recv_n(int new_fd,char* buf,int len){
        int ret;
        int total=0;
        while(total
                ret=recv(new_fd,buf+total,len-total,0);
                total=total+ret;
        }
}

int main(int argc,char** argv){                               //传入参数:IP地址、端口 
        if(argc !=3){
                printf("error args\n");
                return -1;
        }
        int 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;
        ret= connect(sfd,(struct sockaddr*)&ser,sizeof(struct sockaddr));    
        if(-1==ret){
                perror("connect");
                return -1;
        }
         data d;                                                          //定义小火车结构体 
        memset(&d,0,sizeof(d));
        recv_n(sfd,&d.len,sizeof(int));                        //读取要接收文件名字的长度
        recv_n(sfd,d.buf,d.len);                                  //读取文件名
        int fd;
        fd=open(d.buf,O_RDWR|O_CREAT,0666);    //在本地创建文件 
        if(-1==fd){
                perror("open");
                return -1;
        }
        while(1){
                memset(&d,0,sizeof(d));
                recv_n(sfd,&d.len,sizeof(int));                //接收小火车头
                if(d.len >0){                                          //如果后面有数据
                         recv_n(sfd,d.buf,d.len);                  //读出buf的长度。一定要保证读出len的长度,而不能读少了。
                        write(fd,d.buf,d.len);                      //读完之后写到文件中。
                }else{                                                   //如果只有火车头,后面没有数据,说明文件已经下载完了
                        break;
                }
        }
        close(sfd);
        close(fd);             
        return 0;
}










你可能感兴趣的:(4,linux,c开发)