一、函数介绍
#include <sys/sendfile.h>
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
//返回值:成功返回传输的字节数;失败返回-1并设置errno
- 功能:此函数在两个文件描述符之间直接传递数据(完全在内核中),从而避免了内核缓冲区和用户缓冲区之间数据拷贝,效率很高,这称为零拷贝
- 数据从in_fd传送到out_fd
参数
- out_fd:待写入内容的文件描述符
- in_fd:待读取内容的文件描述符
- offset:指定从读入文件流的那个位置开始读
- count:指定在文件描述符之间传输的字节数
参数的注意事项
- in_fd必须是一个支持类似mmap函数的文件描述符,即必须指向真实的文件,不能使socket和管道。out_fd必须是一个socket
- 由此可以看出,sendfile是专门为在网络上传输文件而设计的
二、演示案例
服务端设计
//sendfile_serv.c
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc,char *argv[])
{
if(argc!=4){
printf("usage:%s ip port filename\n",argv[0]);
exit(EXIT_FAILURE);
}
int serv_fd,cli_fd,file_fd;
pid_t pid;
struct stat file_stat;
struct sockaddr_in serv_addr;
if((file_fd=open(argv[3],O_RDONLY))==-1){
perror("open:");
exit(EXIT_FAILURE);
}
if(fstat(file_fd,&file_stat)==-1){
perror("listen:");
exit(EXIT_FAILURE);
}
bzero(&serv_addr,sizeof(serv_addr));
serv_addr.sin_family=AF_INET;
serv_addr.sin_port=htons(atoi(argv[2]));
if(inet_pton(AF_INET,argv[1],&serv_addr.sin_addr)==-1){
perror("inet_pton:");
exit(EXIT_FAILURE);
}
if((serv_fd=socket(AF_INET,SOCK_STREAM,0))==-1){
perror("inet_pton:");
exit(EXIT_FAILURE);
}
if(bind(serv_fd,(struct sockaddr*)&serv_addr,sizeof(serv_addr))==-1){
perror("bind:");
exit(EXIT_FAILURE);
}
if(listen(serv_fd,10)==-1){
perror("listen:");
exit(EXIT_FAILURE);
}
while(1){
if((cli_fd=accept(serv_fd,NULL,NULL))==-1){
continue;
}
if((pid=fork())==0){
close(serv_fd);
if(sendfile(cli_fd,file_fd,NULL,file_stat.st_size)==-1){
perror("sendfile:");
exit(EXIT_FAILURE);
}
printf("sendfile success\n");
close(cli_fd);
exit(EXIT_SUCCESS);
}
close(cli_fd);
}
exit(EXIT_SUCCESS);
}
- 演示结果:左侧开启我们的sendfile程序,右侧使用telnet连接,可以看到读取到了Hello.txt文件的内容

