源代碼如下:
服務器端:
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 <signal.h>
#include <sys/msg.h>
#include <errno.h>
#include <pthread.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/epoll.h>
#include <sys/uio.h>
#include <sys/sendfile.h>
#include <fcntl.h>
#include <assert.h>
#include <libgen.h>
#include <stdbool.h>
#define CHILD_THREAD_ERROR_CHECK(ret,funcName){if(ret!=0){printf("%s:%s\n",funcName,strerror(ret));return (void*)-1;}}
#define THREAD_ERROR_CHECK(ret,funcName){if(ret!=0){printf("%s:%s\n",funcName,strerror(ret));return -1;}}
#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;}}
#endif
work_que.h
#ifndef _WORK_QUE_H_
#define _WORK_QUE_H_
#include "head.h"
typedef struct tag_node{ //客戶端文件描述符struct
int newFd;
struct tag_node* pNext;
}Node_t,*pNode_t;
typedef struct{ //等待隊列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* threadFunc(void* p)
{
pFactory_t pThreadInfo=(pFactory_t)p;
pQue_t pq=&pThreadInfo->que;
pNode_t pGet;
int getTaskSuccess;
while(1)
{
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;
}
}
}
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;
}
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;
}
tran_file.c
#include "head.h"
void sigFunc(int signum)
{
printf("%d is coming\n",signum);
}
typedef struct{
int dataLen;
char buf[1000];
}train_t;
#define FILENAME "file"
int tranFile(int newFd)
{
signal(SIGPIPE,sigFunc);
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");
close(fd);
close(newFd);
return 0;
}
main.c
#include "factory.h"
int main(int argc,char* argv[])
{
if(argc!=5)
{
printf("./thread_pool_server IP PORT THREAD_NUM CAPACITY\n");
return -1;
}
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;
while(1)
{
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_mutex_unlock(&pq->mutex);
pthread_cond_signal(&threadInfo.cond); //喚醒子線程
}
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)
客戶端:
#include "head.h"
int recvCycle(int sfd,void* buf,int recvLen) //socketfd
{
char *p=(char*)buf;
int total=0,ret;
while(total<recvLen) //recv返回接收的字節大小
{
ret=recv(sfd,p+total,recvLen-total,0); //下次再次接收數據的時候放在buf的p+total的位置
if(0==ret)
{
return -1;
}
total+=ret; //累加到total上
}
return 0;
}
tcp_client.c
#define _GNU_SOURCE
#include <func.h>
int recvCycle(int,void*,int);
int main(int argc,char* argv[])
{
ARGS_CHECK(argc,3);
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(argv[2]));
serAddr.sin_addr.s_addr=inet_addr(argv[1]);
int ret;
ret=connect(socketFd,(struct sockaddr*)&serAddr,sizeof(serAddr));
ERROR_CHECK(ret,-1,"connect");
int fd;
int dataLen;
char buf[1000]={0};
recvCycle(socketFd,&dataLen,4); //接收文件名的大小,存入dataLen(佔多少字節)
recvCycle(socketFd,buf,dataLen); //接收文件名
fd=open(buf,O_CREAT|O_RDWR,0666);//創建文件名,加權限
ERROR_CHECK(fd,-1,"open");
//接收文件大小
off_t fileSize,downLoadSize=0,slice,lastLoadSize=0;
recvCycle(socketFd,&dataLen,4); //長度存入dataLen,比如 400000字節,佔4個字節空間
recvCycle(socketFd,&fileSize,dataLen); //實際fileSize的值爲40000字節
printf("fileSize=%ld\n",fileSize);
//time_t lastTime, nowTime;
//lastTime=nowTime=time(NULL);
struct timeval start,end;
int fds[2];
pipe(fds);
gettimeofday(&start,NULL);
slice=fileSize/1000;
while(downLoadSize<fileSize)
{
ret=splice(socketFd,NULL,fds[1],NULL,65535,SPLICE_F_MOVE|SPLICE_F_MORE);
ERROR_CHECK(ret,-1,"splice");
splice(fds[0],NULL,fd,NULL,ret,SPLICE_F_MOVE|SPLICE_F_MORE);
downLoadSize+=ret;
if(downLoadSize-lastLoadSize>slice)
{
printf("%5.2f%s\r",(float)downLoadSize/fileSize*100,"%");
fflush(stdout); //每次打印後,情況輸出緩衝區
lastLoadSize=downLoadSize;
}
//通過splice函數將socket緩衝區的內容寫入到fds[1]中,再從管道的讀端fds[0]讀出數據寫入到用戶
//打開的文件描述符。整個過程未執行recv/send操作,因此也未涉及用戶空間和內核空間的數據拷貝
}
gettimeofday(&end,NULL);
printf("100.00%%\n");
printf("use time=%ld\n",(end.tv_sec-start.tv_sec)*1000000+end.tv_usec-start.tv_usec);
close(fd);
close(socketFd);
return 0;
}