APUE编程:32---高级IO之(数据零拷贝:sendfile()函数)

博客介绍了sendfile函数,它能在两个文件描述符间直接传递数据,实现零拷贝,效率高。说明了函数参数及注意事项,如in_fd须指向真实文件,out_fd须是socket。还给出服务端设计演示案例,展示将服务器文件传送给客户端的结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、函数介绍

#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文件的内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

董哥的黑板报

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值