源代码如下:
server端
head.h
#ifndef __HEAD_H__
#define __HEAD_H__
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <time.h>
#include <pwd.h>
#include <grp.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/select.h>
#include <sys/time.h>
#include <strings.h>
#include <syslog.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/msg.h>
#include <errno.h>
#include <signal.h>
#include <pthread.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/epoll.h>
#include <sys/uio.h>
#include <sys/sendfile.h>
#define ARGS_CHECK(argc,val) {if(argc!=val) {printf("error args\n");return -1;}}
#define ERROR_CHECK(ret,retVal,funcName) {if(ret==retVal) {perror(funcName);return -1;}}
#define THREAD_ERROR_CHECK(ret,funcName) {if(ret!=0) {printf("%s:%s\n",funcName,strerror(ret));return -1;}}
#define CHILD_THREAD_ERROR_CHECK(ret,funcName) {if(ret!=0) {printf("%s:%s\n",funcName,strerror(ret));return (void*)-1;}}
#endif
tcp_init.c
#include "head.h"
int tcpInit(int *sfd,char* ip,char* port)
{
int socketFd=socket(AF_INET,SOCK_STREAM,0);
ERROR_CHECK(socketFd,-1,"socket");
struct sockaddr_in serAddr;
bzero(&serAddr,sizeof(serAddr));
serAddr.sin_family=AF_INET;
serAddr.sin_port=htons(atoi(port));
serAddr.sin_addr.s_addr=inet_addr(ip);
int reuse=1;
int ret;
ret=setsockopt(socketFd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(int));
ERROR_CHECK(ret,-1,"setsockopt");
ret=bind(socketFd,(struct sockaddr*)&serAddr,sizeof(serAddr));
ERROR_CHECK(ret,-1,"bind");
listen(socketFd,10);
*sfd=socketFd;
return 0;
}
work_que.h
#ifndef __WORK_QUE_H__
#define __WORK_QUE_H__
#include "head.h"
typedef struct tag_node{
int newFd;
struct tag_node* pNext;
}Node_t,*pNode_t;
typedef struct{
pNode_t queHead,queTail;
int queCapacity;//队列最大长度
int queSize;//队列当前长度
pthread_mutex_t mutex;//队列锁
}Que_t,*pQue_t;
void queInit(pQue_t,int);
void queInsert(pQue_t,pNode_t);
int queGet(pQue_t,pNode_t*);
#endif
work_que.c
#include "work_que.h"
//初始化队列
//设置参数,初始化锁
void queInit(pQue_t pq,int capacity)
{
bzero(pq,sizeof(Que_t));
pq->queCapacity=capacity;
pthread_mutex_init(&pq->mutex,NULL);
return;
}
//插入到队列
void queInsert(pQue_t pq,pNode_t pNew)
{
if(NULL==pq->queHead)
{
pq->queHead=pNew;
pq->queTail=pNew;
}else{
pq->queTail->pNext=pNew;
pq->queTail=pNew;
}
pq->queSize++;
}
//从队列中取任务出来
int queGet(pQue_t pq,pNode_t *pDel)
{
if(NULL==pq->queHead)
{
return -1;
}
*pDel=pq->queHead;
pq->queHead=pq->queHead->pNext;
if(NULL==pq->queHead)
{
pq->queTail=NULL;
}
pq->queSize--;
return 0;
}
factory.h
#ifndef __FACTORY_H__
#define __FACTORY_H__
#include "head.h"
#include "work_que.h"
typedef struct{
Que_t que; //保存任务的队列
pthread_cond_t cond;//条件变量,用来唤醒等待的子线程
pthread_t *pthid;//存储线程ID的起始地址
int threadNum;//线程数目
short startFlag;//线程池是否启动
}Factory_t,*pFactory_t;
int factoryInit(pFactory_t,int,int);
int factoryStart(pFactory_t);
int tcpInit(int *sfd,char* ip,char* port);
int tranFile(int newFd);
#endif
factory.c
#include "factory.h"
void cleanupFunc(void* pArg)
{
pQue_t pq=(pQue_t)pArg;
printf("thread unlock\n");
pthread_mutex_unlock(&pq->mutex);
}
//线程函数
void* threadFunc(void* p)
{
pFactory_t pThreadInfo=(pFactory_t)p;
//任务队列
pQue_t pq=&pThreadInfo->que;
pthread_cleanup_push(cleanupFunc,pq);
pNode_t pGet;
int getTaskSuccess;
//每次去队列中拿任务,先加锁,如果队列为空,在条件变量上等待
//如果不为空,就拿一个任务,解锁,传输文件
while(1)
{
//退出方式,当startFlag为0时,退出
if(pThreadInfo->startFlag == 0)
{
printf("thread exit\n");
pthread_exit(NULL);
}
pthread_mutex_lock(&pq->mutex);
if(!pq->queSize)
{
pthread_cond_wait(&pThreadInfo->cond,&pq->mutex);
}
getTaskSuccess=queGet(pq,&pGet);//拿任务
pthread_mutex_unlock(&pq->mutex);
if(!getTaskSuccess)
{
tranFile(pGet->newFd);
free(pGet);
pGet=NULL;
}
}
pthread_cleanup_pop(1);
}
//初始化线程池,初始化队列,初始化条件变量和锁
//设置线程数量和启动状态。
int factoryInit(pFactory_t p,int threadNum,int capacity)
{
queInit(&p->que,capacity);
pthread_cond_init(&p->cond,NULL);
p->pthid=(pthread_t*)calloc(threadNum,sizeof(pthread_t));
p->threadNum=threadNum;
p->startFlag=0;
return 0;
}
//启动线程池
//按照线程数量创建线程,同时标记为启动状态
int factoryStart(pFactory_t p)
{
if(!p->startFlag)
{
int i;
p->startFlag=1;
for(i=0;i<p->threadNum;i++)
{
pthread_create(p->pthid+i,NULL,threadFunc,p);
}
}
return 0;
}
tran_file.c
#include "head.h"
typedef struct{
int dataLen;
char buf[1000];
}train_t;
#define FILENAME "file"
int tranFile(int newFd)
{
train_t train;
int ret;
train.dataLen=strlen(FILENAME);//发送文件名
strcpy(train.buf,FILENAME);
send(newFd,&train,4+train.dataLen,0);
//发送文件大小给客户端
struct stat buf;
int fd=open(FILENAME,O_RDWR);
fstat(fd,&buf);
train.dataLen=sizeof(buf.st_size);
memcpy(train.buf,&buf.st_size,train.dataLen);
send(newFd,&train,4+train.dataLen,0);
//发送文件内容
ret=sendfile(newFd,fd,NULL,buf.st_size);
/* printf("sendfile ret=%d\n",ret); */
ERROR_CHECK(ret,-1,"sendfile");
return 0;
}
main.c
#include "factory.h"
int exitFds[2];
void sigFunc(int signum)
{
printf("ready to exit\n");
write(exitFds[1],&signum,1);
}
// 传参 ip 端口 线程数量 能力
int main(int argc,char* argv[])
{
if(argc!=5)
{
printf("./thread_pool_server IP PORT THREAD_NUM CAPACITY\n");
return -1;
}
pipe(exitFds);
//创建子进程,子进程实现线程池
if(fork())
{
//父进程,注释信号处理函数,收到信号后,通知子进程退出
signal(SIGUSR1,sigFunc);
wait(NULL);
printf("thread pool exit\n");
exit(0);
}
//子进程实现线程池
// 初始化线程池结构体
Factory_t threadInfo;
int threadNum=atoi(argv[3]);
int capacity=atoi(argv[4]);
//初始化线程池
factoryInit(&threadInfo,threadNum,capacity);
//启动线程池
factoryStart(&threadInfo);
int socketFd;
tcpInit(&socketFd,argv[1],argv[2]);
int newFd;
pQue_t pq=&threadInfo.que;
pNode_t pNew;
int epfd = epoll_create(1);
struct epoll_event event,evs[2];
event.data.fd = socketFd;
event.events = EPOLLIN;
int ret = epoll_ctl(epfd,EPOLL_CTL_ADD,socketFd,&event);
ERROR_CHECK(ret,-1,"epoll_ctl");
event.data.fd = exitFds[0];
event.events = EPOLLIN;
ret = epoll_ctl(epfd,EPOLL_CTL_ADD,exitFds[0],&event);
ERROR_CHECK(ret,-1,"epoll_ctl");
int readyFdNum = 0;
while(1)
{
readyFdNum = epoll_wait(epfd,evs,2,-1);
if(readyFdNum>0)
{
for(int i=0;i<readyFdNum;i++)
{
if(evs[i].data.fd == socketFd)
{
//接收客户端连接,放到新的节点里,添加到队列中
newFd=accept(socketFd,NULL,NULL);
pNew=(pNode_t)calloc(1,sizeof(Node_t));
pNew->newFd=newFd;
//加锁,再去放任务
pthread_mutex_lock(&pq->mutex);
queInsert(pq,pNew);//放任务
pthread_cond_signal(&threadInfo.cond);//唤醒子线程
pthread_mutex_unlock(&pq->mutex);
}
else if(evs[i].data.fd == exitFds[0])
{
//管道读端可读,表示要退出了
//第一种退出方式,轮流给子线程发pthread_cancel信号
//子线程收到cancel信号,运行到calcel点,退出,
//清理函数中释放锁
/* for(int j=0;j<threadInfo.threadNum;j++) */
/* { */
/* pthread_cancel(threadInfo.pthid[j]); */
/* } */
//第二种退出方式,把启动状态标记为0,
//子线程完成任务后来判断启动状态,
//如果是0,表示要退出了,pthread_exit退出
threadInfo.startFlag=0;
//把所有等待在条件变量上的子线程唤醒
pthread_cond_broadcast(&threadInfo.cond);
for(int j=0;j<threadInfo.threadNum;j++)
{
pthread_join(threadInfo.pthid[j],NULL);
}
printf("main thread exit\n");
exit(0);
}
}
}
}
return 0;
}
Makefile
SRCS:=$(wildcard *.c)
OBJS:=$(patsubst %.c,%.o,$(SRCS))
ELF:=thread_pool_server
CC:=gcc
CFLAGS:=-Wall
$(ELF):$(OBJS)
gcc -o $@ $^ -pthread
clean:
rm -rf $(OBJS) $(ELF)