Liunx C 線程池模型:實現多線程併發傳輸文件服務 1.0

源代碼如下:
服務器端:
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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章