可以用来注册网站域名的入口是,汽车美容网站模板,百度搜索资源平台提交,口碑好的广州做网站文件服务器1.0文件服务器2.0文件服务器4.0 概述
使用了 tcp epoll 线程池 生产者消费者模型#xff0c;实现文件服务器 有两个进程#xff0c;主进程负责接收退出信号用来退出整个程序#xff1b;子进程负责管理线程池、客户端连接以及线程池的退出
子进程中的主线程生…
文件服务器1.0文件服务器2.0文件服务器4.0 概述
使用了 tcp epoll 线程池 生产者消费者模型实现文件服务器 有两个进程主进程负责接收退出信号用来退出整个程序子进程负责管理线程池、客户端连接以及线程池的退出
子进程中的主线程生产任务其他子线程消费任务 功能 主要功能客户端连接服务器然后自动下载文件 注意
实际传输速度主要取决于网络传输速度而不是本地传输速度。网速不高的情况下零拷贝接口并没有优势。 服务器搭建 创建管道用于给子进程传递退出消息创建子进程主进程注册SIGUSR1信号等待回收子进程的资源退出程序子进程 从配置信息中取出服务器的ip地址port端口号线程数量根据线程数量创建线程池初始化线程池启动线程池建立tcp监听将服务器套接字和退出管道加入epoll管理等待客户端的连接 如果有新客户端将其加入线程池的任务队列中通知线程池中的线程执行任务如果退出管道就绪取消并回收所有子线程退出进程 客户端搭建 从配置文件中取出服务器的ip地址port端口号连接服务器下载文件 启动 启动服务器
1、在bin目录下生成可执行文件
wUbuntu20:bin $ gcc ../src/*.c -o server -lpthread2、启动服务器
wUbuntu20:bin $ ./server ../conf/server.conf启动客户端
1、在客户端的目录下生成可执行文件
wUbuntu20:client $ gcc *.c -o client2、启动客户端
wUbuntu20:client $ ./client client.conf目录设计
服务器
bin存放二进制文件conf存放配置文件include存放头文件src存放源文件
wUbuntu20:src $ tree ..
..
├── bin
│ └── server
├── conf
│ └── server.conf
├── include
│ ├── head.h
│ ├── task_queue.h
│ └── thread_pool.h
├── resource
│ └── file
└── src├── interact_cli.c├── main_server.c├── send_file_truck.c├── send_file_mmap.c├── send_file_splice.c├── task_queue.c├── tcp_init.c└── thread_pool.c 客户端
wUbuntu20:client $ tree
.
├── client
├── client.conf
├── main_client.c
├── recv_file_mmap.c
├── recv_file_splice.c
└── recv_file_truck.c配置文件 服务器配置文件 server.conf
存放服务器ip地址服务器port端口线程池中的线程数量
根据实际情况自行更改
192.168.160.129
2000
10客户端配置文件 client.conf
存放服务器ip地址服务器port端口
根据实际情况自行更改
192.168.160.129
2000线程池退出方式
方式一本文采用
给主进程发送退出信号
主进程收到信号后通知子进程退出
子进程收到退出信号后取消线程池并回收线程池资源。 方式二
给主进程发送退出信号
主进程收到信号后通知子进程退出
如果是非忙碌的子进程直接退出如果是忙碌的子进程就忙完了再退出
在方式一的基础上给任务队列中新增一个退出标志位每一个线程处理任务前先看标志位决定是否退出线程 传输文件方式
方式一使用自定义协议传输先发送本次数据长度再发送数据内容 方式二使用零拷贝的方式传输
mmap和sendfile支持的最大发送字节数是2G2G以上的文件无法用mmap和sendfile
splice没有发送字节数限制每次最大发送65535字节管道的最大承载量 mmap零拷贝接口
NAMEmmap, munmap - map or unmap files or devices into memorySYNOPSIS#include sys/mman.hvoid *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);int munmap(void *addr, size_t length);注意服务器用mmap发送的文件客户端也需要用mmap接收因为不再是先接数据长度再接数据内容的形式了
//服务器用mmap建立文件映射将文件直接映射到内核发送缓冲区
char *pMap (char*)mmap(NULL, file_info.st_size, PROT_READ, MAP_SHARED, file_fd, 0);
ERROR_CHECK(pMap, (char*)-1, mmap);send(socket_fd, pMap, file_info.st_size, 0);
munmap(pMap, file_info.st_size);//客户端也需要用mmap接收或者用whilerecv接收直接接收数据不用先接收数据长度
ftruncate(fd, filesize);//先固定文件大小
char *pMap (char*)mmap(NULL, filesize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
ERROR_CHECK(pMap, (char*)-1, mmap);recv(sfd, pMap, filesize, MSG_WAITALL); //要用MSG_WAITALL参数避免没有全部接收到
munmap(pMap, filesize);sendfile零拷贝接口
在两个文件描述符之间直接发送文件将in_fd发给out_fd
NAMEsendfile - transfer data between file descriptorsSYNOPSIS#include sys/sendfile.hssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);splice零拷贝接口
splice传输必须要借助管道
将数据写入管道再从管道传给发送缓冲区是不经过用户态空间的
管道最多可以存储65535个字节如果文件较大则需要循环传输
splice没有字节数限制比上面两个好用一点可以控制发送的快慢第5个参数决定每次传输的字节数量
NAMEsplice - splice data to/from a pipeSYNOPSIS#define _GNU_SOURCE /* See feature_test_macros(7) */#include fcntl.hssize_t splice(int fd_in, loff_t *off_in, int fd_out,loff_t *off_out, size_t len, unsigned int flags);代码 服务器代码 head.h
#ifndef __HEAD_H__
#define __HEAD_H__#include stdio.h
#include stdlib.h
#include string.h
#include unistd.h
#include wait.h
#include sys/socket.h
#include sys/types.h
#include sys/epoll.h
#include pthread.h//检查命令行参数个数
#define ARGS_CHECK(argc, num) { if (argc ! num) {\fprintf(stderr, Args error!\n);\return -1;} }//检查系统调用的返回值
#define ERROR_CHECK(ret, num, msg) { if (ret num) {\perror(msg);\return -1;} }int tcp_init(char *ip, int port);#endif task_queue.h
#ifndef __TASK_QUEUE_H__
#define __TASK_QUEUE_H__#include head.h//任务队列节点
typedef struct TaskNode {int _clifd; //客户端fdstruct TaskNode *_pNext;
}TaskNode_t, *pTaskNode_t;//任务队列
typedef struct TaskQueue {int _size; //队列大小pTaskNode_t _pHead; //队头pTaskNode_t _pTail; //队尾pthread_cond_t _cond; //条件变量pthread_mutex_t _mutex; //互斥锁
}TaskQueue_t, *pTaskQueue_t;//初始化任务队列
int init_TaskQueue(pTaskQueue_t pQueue);
//入队
int push_TaskQueue(pTaskQueue_t pQueue, pTaskNode_t pNew);
//得到队头元素
int get_TaskNode(pTaskQueue_t pQueue, pTaskNode_t *ppGet);#endif thread_pool.h
#ifndef __THREAD_POOL_H__
#define __THREAD_POOL_H__#include head.h
#include task_queue.h//线程池
typedef struct {int _thread_num; //子线程数量pthread_t *_pthid; //子线程数组TaskQueue_t _que; //任务队列
}ThreadPool_t, *pThreadPool_t;//初始化线程池
int init_ThreadPool(pThreadPool_t pPool, int thread_num);//启动线程池
int boot_ThreadPool(pThreadPool_t pPool);//功能服务器主线程将新连接的客户端加入线程池中的任务队列然后通知子线程处理
int interact_cli(int sfd, pThreadPool_t pPool, int exitpipe);//使用私有协议传输数据给另一个进程传输文件
int send_file_truck(int socket_fd, char *filename);//使用sendfile给另一个进程传输文件
int send_file_sendfile(int socket_fd, char *filename);//使用mmap接口给另一个进程传输文件
int send_file_mmap(int socket_fd, char *filename);//使用splice接口给另一个进程传输文件
int send_file_splice(int socket_fd, char *filename);#endifmain_server.c
#include ../include/head.h
#include ../include/thread_pool.h//发送信号实现异步退出线程池
int exitpipe[2];
void sig_func(int sigNum)
{printf(sig %d is coming!\n, sigNum);write(exitpipe[1], a, 1);
}int main(int argc, char *argv[])
{//配置文件ARGS_CHECK(argc, 2);//父子传递退出标记的管道pipe(exitpipe);//父进程用来接收退出信号if (fork()) {close(exitpipe[0]);signal(SIGUSR1, sig_func);//回收子进程的资源wait(NULL);printf(thread_pool exit!\n);exit(0);}//子进程用来管理线程池close(exitpipe[1]);//从配置文件中取出服务器ip地址、port端口号、子线程数量FILE *fp fopen(argv[1], r);char ip[128] {0};int port 0;int thread_num 0;fscanf(fp, %s%d%d, ip, port, thread_num);fclose(fp);//创建线程池并初始化ThreadPool_t pool;init_ThreadPool(pool, thread_num);//启动线程池boot_ThreadPool(pool);//创建一个正在监听的tcp套接字int sfd tcp_init(ip, port);//处理客户端的请求和退出请求//有新请求就新建任务节点放入任务队列//子线程等待任务有任务立刻处理if (-1 ! sfd) {interact_cli(sfd, pool, exitpipe[0]);}return 0;
}tcp_init.c
#include stdio.h
#include stdlib.h
#include string.h#include arpa/inet.h#define ERROR_CHECK(ret, num, msg) { if (ret num) {\perror(msg); return -1;} }//输入服务器的ip地址端口号
//输出绑定了服务器ip和端口的正在监听的套接字
int tcp_init(char *ip, int port)
{//生成一个tcp类型的套接字int sfd socket(AF_INET, SOCK_STREAM, 0);ERROR_CHECK(sfd, -1, ser_socket);//将端口号设置为可重用, 不用再等待重启时的TIME_WAIT时间int reuse 1;setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, reuse, sizeof(reuse));//给套接字绑定服务端ip和portstruct sockaddr_in serverAddr;memset(serverAddr, 0, sizeof(struct sockaddr_in));serverAddr.sin_family AF_INET;serverAddr.sin_addr.s_addr inet_addr(ip);serverAddr.sin_port htons(port);int ret bind(sfd, (struct sockaddr*)serverAddr, sizeof(serverAddr));ERROR_CHECK(ret, -1, ser_bind);//将套接字设为监听模式并指定最大监听数全连接队列的大小ret listen(sfd, 10); ERROR_CHECK(ret, -1, ser_listen);/* printf([ip:%s, port:%d] is listening...\n, ip, port); */return sfd;
}thread_pool.c
#include ../include/head.h
#include ../include/thread_pool.h
#include ../include/task_queue.h//线程清理函数
void clean_func(void *p)
{pTaskQueue_t pQue (pTaskQueue_t)p;pthread_mutex_unlock(pQue-_mutex);
}//线程函数
void *thread_func(void *p)
{//拿到任务队列地址pTaskQueue_t pQue (pTaskQueue_t)p;pTaskNode_t pCur NULL;while (1) {//取任务pthread_mutex_lock(pQue-_mutex);pthread_cleanup_push(clean_func, pQue);if (0 pQue-_size) {pthread_cond_wait(pQue-_cond, pQue-_mutex);}get_TaskNode(pQue, pCur);pthread_mutex_unlock(pQue-_mutex);//执行任务发送文件char filename[128] file;send_file_truck(pCur-_clifd, filename); printf(----------------------------send finish!\n);//任务完成销毁任务节点free(pCur);pCur NULL;pthread_cleanup_pop(1);}pthread_exit(NULL);
}//初始化线程池
int init_ThreadPool(pThreadPool_t pPool, int thread_num)
{pPool-_thread_num thread_num;pPool-_pthid (pthread_t*)calloc(thread_num, sizeof(pthread_t));init_TaskQueue(pPool-_que);return 0;
}//启动线程池
int boot_ThreadPool(pThreadPool_t pPool)
{printf(thread_num: %d\n, pPool-_thread_num);for (int i 0; i pPool-_thread_num; i) {pthread_create(pPool-_pthid[i], NULL, thread_func, pPool-_que);}return 0;
}task_queue.c
#include ../include/head.h
#include ../include/task_queue.h//初始化任务队列
int init_TaskQueue(pTaskQueue_t pQueue)
{pQueue-_size 0;pQueue-_pHead pQueue-_pTail NULL;pthread_cond_init(pQueue-_cond, NULL);pthread_mutex_init(pQueue-_mutex, NULL);return 0;
}//入队
int push_TaskQueue(pTaskQueue_t pQueue, pTaskNode_t pNew)
{if (NULL pQueue-_pHead) {pQueue-_pHead pQueue-_pTail pNew;}else {pQueue-_pTail-_pNext pNew;pQueue-_pTail pNew;}pQueue-_size;return 0;
}//得到队头元素
int get_TaskNode(pTaskQueue_t pQueue, pTaskNode_t *ppGet)
{//没有元素if (0 pQueue-_size) {return -1;}//有元素取出更新队头*ppGet pQueue-_pHead;pQueue-_pHead pQueue-_pHead-_pNext;//只有一个元素更新队尾if (1 pQueue-_size) {pQueue-_pTail NULL;}//减小队列长度--pQueue-_size;return 0;
} interact_cli.c
#include ../include/head.h
#include ../include/task_queue.h
#include ../include/thread_pool.h//将fd加入epfd
int epollAddFd(int fd, int epfd)
{struct epoll_event event;memset(event, 0, sizeof(event));event.events EPOLLIN;event.data.fd fd;int ret epoll_ctl(epfd, EPOLL_CTL_ADD, fd, event);ERROR_CHECK(ret, -1, EPOLL_CTL_ADD);return 0;
}//将fd从epfd中移除
int epollDelFd(int fd, int epfd)
{struct epoll_event event;memset(event, 0, sizeof(event));event.events EPOLLIN;event.data.fd fd;int ret epoll_ctl(epfd, EPOLL_CTL_DEL, fd, event);ERROR_CHECK(ret, -1, EPOLL_CTL_DEL);return 0;
}//功能服务器主线程将新连接的用户加入线程池中的任务队列然后通知子线程处理
//参数服务器套接字线程池地址退出管道
int interact_cli(int sfd, pThreadPool_t pPool, int exitpipe)
{//使用epoll管理所有文件描述符int epfd epoll_create(1);//将sfd添加进epfdepollAddFd(sfd, epfd);//将用来退出的管道加入epfdepollAddFd(exitpipe, epfd);int readyFdNum 0;//就绪的文件描述符数量struct epoll_event evs[2]; //epoll_wait等待数组的大小int newfd 0;//客户端的套接字//epoll等待就绪的文件描述符while (1) {readyFdNum epoll_wait(epfd, evs, 2, -1);int i;for (i 0; i readyFdNum; i) {//服务端套接字就绪有新用户连接if (evs[i].data.fd sfd) {//接收用户端newfd accept(sfd, NULL, NULL);//新建任务节点pTaskNode_t pNew (pTaskNode_t)calloc(1, sizeof(TaskNode_t));pNew-_clifd newfd;pthread_mutex_lock(pPool-_que._mutex);//加锁push_TaskQueue(pPool-_que, pNew); //将新节点放入任务队列pthread_cond_signal(pPool-_que._cond);//通知子线程pthread_mutex_unlock(pPool-_que._mutex);//解锁}//退出管道就绪退出线程池if (evs[i].data.fd exitpipe) {//取消线程池for (int j 0; j pPool-_thread_num; j) {pthread_cancel(pPool-_pthid[j]);}//回收线程池的资源for (int j 0; j pPool-_thread_num; j) {pthread_join(pPool-_pthid[j], NULL);}//退出子进程exit(0);}}}return 0;
}send_file_truck.c
#include stdio.h
#include string.h
#include unistd.h//open
#include sys/types.h
#include sys/stat.h
#include fcntl.h
//send
#include sys/socket.h#define ERROR_CHECK(ret, num, msg) { if (ret num) {\perror(msg); return -1;} }//传输文件协议小货车
typedef struct {int _data_len;//货车头表示数据长度char _data[1000];//火车车厢表示数据
}Truck_t;//使用私有协议传输数据给另一个进程传输文件
int send_file_truck(int socket_fd, char *filename)
{int ret -1;//定义一个小货车用来传输文件Truck_t truck;memset(truck, 0, sizeof(Truck_t));//将文件名扩展为文件路径char filepath[128] {0};sprintf(filepath, ../resource/%s, filename);//根据文件路径打开传输文件int file_fd open(filepath, O_RDONLY);ERROR_CHECK(file_fd, -1, open);//先发文件名truck._data_len strlen(filename);strcpy(truck._data, filename);ret send(socket_fd, truck, sizeof(int) truck._data_len, 0);ERROR_CHECK(ret, -1, send_title);//再发文件大小struct stat file_info;memset(file_info, 0, sizeof(file_info));fstat(file_fd, file_info);truck._data_len sizeof(file_info.st_size);memcpy(truck._data, file_info.st_size, truck._data_len);ret send(socket_fd, truck, sizeof(int) truck._data_len, 0);ERROR_CHECK(ret, -1, send_filesize);//再发文件内容while (1) {memset(truck._data, 0, sizeof(truck._data));truck._data_len read(file_fd, truck._data, sizeof(truck._data));ERROR_CHECK(truck._data_len, -1, read);if (0 truck._data_len) {//传输完成通知客户端然后退出循环ret send(socket_fd, truck._data_len, 4, 0);ERROR_CHECK(ret, -1, send);break;}ret send(socket_fd, truck, sizeof(int) truck._data_len, 0);if (-1 ret) {//客户端异常断开退出循环printf(client already break!\n);break;}}//关闭传输文件close(file_fd);return 0;
}send_file_mmap.c
#include stdio.h
#include string.h
#include unistd.h//open
#include sys/types.h
#include sys/stat.h
#include fcntl.h
//send
#include sys/socket.h
//mmap
#include sys/mman.h#define ERROR_CHECK(ret, num, msg) { if (ret num) {\perror(msg); return -1;} }//传输文件协议小货车
typedef struct {int _data_len;//货车头表示数据长度char _data[1000];//火车车厢表示数据
}Truck_t;//使用mmap给另一个进程传输文件
int send_file_mmap(int socket_fd, char *filename)
{int ret -1;//定义一个小货车用来传输数据Truck_t truck;memset(truck, 0, sizeof(Truck_t));//将文件名扩展为文件路径char filepath[128] {0};sprintf(filepath, ../resource/%s, filename);//根据文件路径打开传输文件int file_fd open(filepath, O_RDONLY);ERROR_CHECK(file_fd, -1, open);//先发文件名truck._data_len strlen(filename);strcpy(truck._data, filename);ret send(socket_fd, truck, sizeof(int) truck._data_len, 0);ERROR_CHECK(ret, -1, send_title);//再发文件大小struct stat file_info;memset(file_info, 0, sizeof(file_info));fstat(file_fd, file_info);truck._data_len sizeof(file_info.st_size);memcpy(truck._data, file_info.st_size, truck._data_len);ret send(socket_fd, truck, sizeof(int) truck._data_len, 0);ERROR_CHECK(ret, -1, send_filesize);printf(filesize %ld\n, file_info.st_size);//再发文件内容//用mmap建立文件映射将文件直接映射到内核发送缓冲区char *pMap (char*)mmap(NULL, file_info.st_size, PROT_READ, MAP_SHARED, file_fd, 0);ERROR_CHECK(pMap, (char*)-1, mmap);send(socket_fd, pMap, file_info.st_size, 0);munmap(pMap, file_info.st_size);//关闭传输文件close(file_fd);return 0;
} send_file_sendfile.c
#include stdio.h
#include string.h
#include unistd.h//open
#include sys/types.h
#include sys/stat.h
#include fcntl.h
//send
#include sys/socket.h
//sendfile
#include sys/sendfile.h#define ERROR_CHECK(ret, num, msg) { if (ret num) {\perror(msg); return -1;} }//传输文件协议小货车
typedef struct {int _data_len;//货车头表示数据长度char _data[1000];//火车车厢表示数据
}Truck_t;//使用sendfile给另一个进程传输文件
int send_file_sendfile(int socket_fd, char *filename)
{int ret -1;//定义一个小货车Truck_t truck;memset(truck, 0, sizeof(Truck_t));//将文件名扩展为文件路径char filepath[128] {0};sprintf(filepath, ../resource/%s, filename);//根据文件路径打开传输文件int file_fd open(filepath, O_RDONLY);ERROR_CHECK(file_fd, -1, open);//先发文件名truck._data_len strlen(filename);strcpy(truck._data, filename);ret send(socket_fd, truck, sizeof(int) truck._data_len, 0);ERROR_CHECK(ret, -1, send_title);//再发文件大小struct stat file_info;memset(file_info, 0, sizeof(file_info));fstat(file_fd, file_info);truck._data_len sizeof(file_info.st_size);memcpy(truck._data, file_info.st_size, truck._data_len);ret send(socket_fd, truck, sizeof(int) truck._data_len, 0);ERROR_CHECK(ret, -1, send_filesize);printf(filesize %ld\n, file_info.st_size);//再发文件内容//用sendfile接口整体传输sendfile(socket_fd, file_fd, 0, file_info.st_size);//关闭传输文件close(file_fd);return 0;
}send_file_splice.c
#define _GNU_SOURCE
#include stdio.h
#include string.h
#include unistd.h//open
#include sys/types.h
#include sys/stat.h
#include fcntl.h
//send
#include sys/socket.h#define ERROR_CHECK(ret, num, msg) { if (ret num) {\perror(msg); return -1;} }//传输文件协议小货车
typedef struct {int _data_len;//货车头表示数据长度char _data[1000];//火车车厢表示数据
}Truck_t;//使用splice给另一个进程传输文件
int send_file_splice(int socket_fd, char *filename)
{int ret -1;//定义一个小货车Truck_t truck;memset(truck, 0, sizeof(Truck_t));//将文件名扩展为文件路径char filepath[128] {0};sprintf(filepath, ../resource/%s, filename);//根据文件路径打开传输文件int file_fd open(filepath, O_RDONLY);ERROR_CHECK(file_fd, -1, open);//先发文件名truck._data_len strlen(filename);strcpy(truck._data, filename);ret send(socket_fd, truck, sizeof(int) truck._data_len, 0);ERROR_CHECK(ret, -1, send_title);//再发文件大小struct stat file_info;memset(file_info, 0, sizeof(file_info));fstat(file_fd, file_info);truck._data_len sizeof(file_info.st_size);memcpy(truck._data, file_info.st_size, truck._data_len);ret send(socket_fd, truck, sizeof(int) truck._data_len, 0);ERROR_CHECK(ret, -1, send_filesize);printf(filesize %ld\n, file_info.st_size);//再发文件内容//用splice接口需要借助管道int fds[2];pipe(fds);int cur_len 0; //已经发送的字节数/* int maxsize 4096; //每次最多发送的字节数修改这个参数来控制传输的速度 */int maxsize 1280; //每次最多发送的字节数修改这个参数来控制传输的速度//先读文件到管道再从管道放入发送缓冲区while (cur_len file_info.st_size) {ret splice(file_fd, 0, fds[1], 0, maxsize, 0);ret splice(fds[0], 0, socket_fd, 0, ret, 0);cur_len ret;}//关闭传输文件close(file_fd);return 0;
}客户端代码 main_client.c
#include stdio.h
#include stdlib.h
#include string.h
#include unistd.h#include sys/types.h
#include sys/socket.h
#include fcntl.h
#include arpa/inet.h
#include sys/mman.h
#include sys/time.h//检查命令行参数个数
#define ARGS_CHECK(argc, num) { if (argc ! num) {\fprintf(stderr, Argc error!\n);\return -1;}}//检查系统调用返回值
#define ERROR_CHECK(ret, num, msg) { if (ret num) {\perror(msg);\return -1;}}//从服务器下载文件用whilerecv小货车的方式
int recv_file_truck(int sfd);//从服务器下载文件用mmap的方式
int recv_file_mmap(int sfd);//从服务器下载文件用splice的方式
int recv_file_splice(int sfd);int main(int argc, char *argv[])
{ARGS_CHECK(argc, 2);//从配置文件中拿到服务器的ip和portFILE *fp fopen(argv[1], r);char ip[128] {0};int port 0;fscanf(fp, %s%d, ip, port);fclose(fp);//生成一个tcp类型的套接字并连接上了服务器int sfd 0;/* tcp_connect(ip, port, sfd); */sfd socket(AF_INET, SOCK_STREAM, 0);//连接服务器struct sockaddr_in serAddr;memset(serAddr, 0, sizeof(serAddr));serAddr.sin_family AF_INET;serAddr.sin_addr.s_addr inet_addr(ip);serAddr.sin_port htons(port);int ret -1;ret connect(sfd, (struct sockaddr*)serAddr, sizeof(serAddr));ERROR_CHECK(ret, -1, connect);printf(connect server!\n);//计算下载时间用gettimeofday接口struct timeval begin;struct timeval end;gettimeofday(begin, NULL);//下载文件recv_file_truck(sfd);/* recv_file_mmap(sfd); *//* recv_file_splice(sfd); */gettimeofday(end, NULL);printf(cost time : %ld us\n, (end.tv_sec - begin.tv_sec) * 1000000 end.tv_usec - begin.tv_usec);//关闭服务器套接字close(sfd);return 0;
}recv_file_truck.c
#include stdio.h
#include string.h
#include unistd.h#include sys/types.h
#include sys/socket.h
#include fcntl.h//检查系统调用返回值
#define ERROR_CHECK(ret, num, msg) { if (ret num) {\perror(msg);\return -1;}}//接收协议:小货车
typedef struct {int _data_len;//车头先接数据长度char _data[1000];//车厢再接数据内容
}Truck_t;//先接数据长度再根据数据长度接收数据内容
int recv_file_truck(int sfd)
{int ret -1;//接收文件Truck_t truck;memset(truck, 0, sizeof(truck));//先接收文件名打开一个新文件recv(sfd, truck._data_len, sizeof(int), 0);recv(sfd, truck._data, truck._data_len, 0);int file_fd open(truck._data, O_WRONLY|O_CREAT, 0666);ERROR_CHECK(file_fd, -1, open);printf(filename: %s\n, truck._data);//再接收文件大小用来打印进度条int total_size 0;//文件总大小recv(sfd, truck._data_len, sizeof(int), 0);recv(sfd, total_size, truck._data_len, 0);printf(filesize: %d\n, total_size);float rate 0;//当前接收百分比int cur_size 0;//文件已接收大小//循环接收文件内容while (cur_size total_size) {//重置小货车memset(truck, 0, sizeof(truck));//先接数据长度recv(sfd, truck._data_len, sizeof(int), 0);if (0 truck._data_len) {//传输完毕printf(Transfer Finish!\n);break;}//根据数据长度接收数据内容//防止发送方发的慢导致接收缓冲区将车厢当成车头设置recv参数为MSG_WAITALLret recv(sfd, truck._data, truck._data_len, MSG_WAITALL);if (0 ret) {printf(Download finish!\n);break;}//将接收数据写入文件write(file_fd, truck._data, ret);cur_size ret;//打印进度条rate (float)cur_size / total_size;printf(--------------------------%5.2f%%\r, rate * 100);fflush(stdout);//防止光标抖动}//关闭文件close(file_fd);return 0;
}recv_file_mmap.c
#include stdio.h
#include unistd.h
#include sys/types.h
#include sys/socket.h
#include sys/stat.h
#include fcntl.h
#include sys/mman.h//检查系统调用返回值
#define ERROR_CHECK(ret, num, msg) { if (ret num) {\perror(msg);\return -1;}}//从服务器下载文件用mmap的方式
int recv_file_mmap(int sfd)
{//先接收文件名字int data_len 0;char filename[1000] {0};recv(sfd, data_len, sizeof(int), 0);recv(sfd, filename, data_len, 0);//创建一个新文件int file_fd open(filename, O_RDWR | O_CREAT, 0600);ERROR_CHECK(file_fd, -1, open);//再接收文件大小off_t filesize 0;//此处类型必须为off_t否则ftrucate会失败recv(sfd, data_len, sizeof(int), 0);recv(sfd, filesize, data_len, 0);printf(filesize: %ld\n, filesize);//用mmap接收文件int ret ftruncate(file_fd, filesize);ERROR_CHECK(ret, -1, ftruncate);printf(recv start...\n);char *pMap (char*)mmap(NULL, filesize, PROT_READ|PROT_WRITE, MAP_SHARED, file_fd, 0);ERROR_CHECK(pMap, (char*)-1, mmap);recv(sfd, pMap, filesize, MSG_WAITALL);munmap(pMap, filesize);printf(recv finish!\n);//关闭文件close(file_fd);return 0;
}recv_file_splice.c
#define _GNU_SOURCE
#include stdio.h
#include unistd.h
#include sys/types.h
#include sys/socket.h
#include sys/stat.h
#include fcntl.h//检查系统调用返回值
#define ERROR_CHECK(ret, num, msg) { if (ret num) {\perror(msg);\return -1;}}//从服务器下载文件用splice的方式
int recv_file_splice(int sfd)
{int ret -1;//先接收文件名字int data_len 0;char filename[1000] {0};recv(sfd, data_len, sizeof(int), 0);recv(sfd, filename, data_len, 0);//创建一个新文件int file_fd open(filename, O_RDWR | O_CREAT, 0600);ERROR_CHECK(file_fd, -1, open);//再接收文件大小off_t filesize 0;//此处类型必须为off_t否则ftrucate会失败recv(sfd, data_len, sizeof(int), 0);recv(sfd, filesize, data_len, 0);printf(filesize: %ld\n, filesize);//用splice接收文件int fds[2];pipe(fds);int cur_len 0; //当前接收长度int maxsize 4096;//每次传输的最大字节数printf(recv start...\n);while (cur_len filesize) {ret splice(sfd, 0, fds[1], 0, maxsize, 0);ret splice(fds[0], 0, file_fd, 0, ret, 0);cur_len ret;}printf(recv finish!\n);//关闭文件close(file_fd);return 0;
}总结
一个练习tcpepoll和线程池的小项目 文章转载自: http://www.morning.xsklp.cn.gov.cn.xsklp.cn http://www.morning.ggnjq.cn.gov.cn.ggnjq.cn http://www.morning.lpmjr.cn.gov.cn.lpmjr.cn http://www.morning.zdhxm.com.gov.cn.zdhxm.com http://www.morning.ctxt.cn.gov.cn.ctxt.cn http://www.morning.rysmn.cn.gov.cn.rysmn.cn http://www.morning.kxyqy.cn.gov.cn.kxyqy.cn http://www.morning.rtkz.cn.gov.cn.rtkz.cn http://www.morning.nspzy.cn.gov.cn.nspzy.cn http://www.morning.tqsgt.cn.gov.cn.tqsgt.cn http://www.morning.zsrdp.cn.gov.cn.zsrdp.cn http://www.morning.hongjp.com.gov.cn.hongjp.com http://www.morning.nmyrg.cn.gov.cn.nmyrg.cn http://www.morning.dhdzz.cn.gov.cn.dhdzz.cn http://www.morning.ncqzb.cn.gov.cn.ncqzb.cn http://www.morning.ftnhr.cn.gov.cn.ftnhr.cn http://www.morning.bpmnh.cn.gov.cn.bpmnh.cn http://www.morning.fhrgk.cn.gov.cn.fhrgk.cn http://www.morning.hdscx.cn.gov.cn.hdscx.cn http://www.morning.dwmtk.cn.gov.cn.dwmtk.cn http://www.morning.wnnfh.cn.gov.cn.wnnfh.cn http://www.morning.jlschmy.com.gov.cn.jlschmy.com http://www.morning.gmyhq.cn.gov.cn.gmyhq.cn http://www.morning.zcfsq.cn.gov.cn.zcfsq.cn http://www.morning.hcwjls.com.gov.cn.hcwjls.com http://www.morning.lqjlg.cn.gov.cn.lqjlg.cn http://www.morning.xpqyf.cn.gov.cn.xpqyf.cn http://www.morning.hpjpy.cn.gov.cn.hpjpy.cn http://www.morning.mcpdn.cn.gov.cn.mcpdn.cn http://www.morning.gyjld.cn.gov.cn.gyjld.cn http://www.morning.rmyqj.cn.gov.cn.rmyqj.cn http://www.morning.yqzyp.cn.gov.cn.yqzyp.cn http://www.morning.knczz.cn.gov.cn.knczz.cn http://www.morning.wdnkp.cn.gov.cn.wdnkp.cn http://www.morning.ghryk.cn.gov.cn.ghryk.cn http://www.morning.jphxt.cn.gov.cn.jphxt.cn http://www.morning.xdwcg.cn.gov.cn.xdwcg.cn http://www.morning.gqfbl.cn.gov.cn.gqfbl.cn http://www.morning.rhmt.cn.gov.cn.rhmt.cn http://www.morning.bmncq.cn.gov.cn.bmncq.cn http://www.morning.mtqqx.cn.gov.cn.mtqqx.cn http://www.morning.gthwz.cn.gov.cn.gthwz.cn http://www.morning.dtgjt.cn.gov.cn.dtgjt.cn http://www.morning.qinhuangdjy.cn.gov.cn.qinhuangdjy.cn http://www.morning.lwdzt.cn.gov.cn.lwdzt.cn http://www.morning.qbwmz.cn.gov.cn.qbwmz.cn http://www.morning.kzrg.cn.gov.cn.kzrg.cn http://www.morning.cmrfl.cn.gov.cn.cmrfl.cn http://www.morning.xjqrn.cn.gov.cn.xjqrn.cn http://www.morning.wdwfm.cn.gov.cn.wdwfm.cn http://www.morning.jwfqq.cn.gov.cn.jwfqq.cn http://www.morning.rxxdk.cn.gov.cn.rxxdk.cn http://www.morning.pangucheng.cn.gov.cn.pangucheng.cn http://www.morning.gwsll.cn.gov.cn.gwsll.cn http://www.morning.mrpqg.cn.gov.cn.mrpqg.cn http://www.morning.nrrzw.cn.gov.cn.nrrzw.cn http://www.morning.xjnjb.cn.gov.cn.xjnjb.cn http://www.morning.tnthd.cn.gov.cn.tnthd.cn http://www.morning.gxtbn.cn.gov.cn.gxtbn.cn http://www.morning.ynryz.cn.gov.cn.ynryz.cn http://www.morning.slnz.cn.gov.cn.slnz.cn http://www.morning.nlrxh.cn.gov.cn.nlrxh.cn http://www.morning.pwdgy.cn.gov.cn.pwdgy.cn http://www.morning.rdkqt.cn.gov.cn.rdkqt.cn http://www.morning.3dcb8231.cn.gov.cn.3dcb8231.cn http://www.morning.lzttq.cn.gov.cn.lzttq.cn http://www.morning.xpgwz.cn.gov.cn.xpgwz.cn http://www.morning.tjjkn.cn.gov.cn.tjjkn.cn http://www.morning.bypfj.cn.gov.cn.bypfj.cn http://www.morning.fylsz.cn.gov.cn.fylsz.cn http://www.morning.zrgsg.cn.gov.cn.zrgsg.cn http://www.morning.ymwrs.cn.gov.cn.ymwrs.cn http://www.morning.qxlhj.cn.gov.cn.qxlhj.cn http://www.morning.fkfyn.cn.gov.cn.fkfyn.cn http://www.morning.yksf.cn.gov.cn.yksf.cn http://www.morning.xsszn.cn.gov.cn.xsszn.cn http://www.morning.dbnrl.cn.gov.cn.dbnrl.cn http://www.morning.lfpdc.cn.gov.cn.lfpdc.cn http://www.morning.bwqcx.cn.gov.cn.bwqcx.cn http://www.morning.yknsr.cn.gov.cn.yknsr.cn