• 高级I/O函数(2)-splice函数


    splice函数:

        功能描述:用于在两个文件描述符之间移动数据,也是零拷贝操作。函数定义如下:

    1 #include <fcntl.h>
    3   ssize_t splice(int fd_in,loff_t* off_t,int fd_out,loff_t* off_out,size_t len,unsigned int flags);

    参数描述:

        fd_in:待输入数据的文件描述符.

        off_t:如果fd_in是一个管道文件描述符,那么off_t参数必须是NULL,表示从数据流的当前偏移位置读入;如果fd_in不是一个管道文件描述符(例如socket),则它将指出具体的偏移位置.

        len:指定移动数据的长度.

        flags:则控制数据如何移动,它可以被设置为下表中值的按位异或.

              表  splice的flags参数的常用取值及其含义

                 常用值 含义
    SPLICE_F_MOVE 如果合适的话,按整页内存移动数据.
    SPLICE_F_NONBLOCK 非阻塞的splice操作,但实际效果还是会受文件描述符本身的阻塞状态的影响.
    SPLICE_F_MORE 给内核一个提示:后续的splice调用将读取更多的数据
    SPLICE_F_GIFT 对splice没有效果.

     注意:

        使用splice函数时,fd_in和fd_out必须至少有一个管道文件描述符.调用成功后返回移动字节的数量.它可能返回0,这发生从管道中读取数据时而该管道没有被写入任何数据.错误返回-1并设置errno.

     例子:利用splice函数来实现一个零拷贝的回射服务器模型。

     1 #include <sys/socket.h>
     2 #include <netinet/in.h>
     3 #include <arpa/inet.h>
     4 #include <unistd.h>
     5 #include <stdlib.h>
     6 #include <string.h>
     7 #include <fcntl.h>
     8 
     9 int main(int argc,const char* argv[]){
    10       if(argc!=2){
    11           printf("usage:%s ip_address port_number
    ",argv[0]);
    12           return -1;
    13       }'
    14       
    15       const char* ip=argv[1];
    16       int port=atoi(argv[2]);
    17       
    18       int ret;
    19       struct sockaddr_in address;
    20       bzero(&address,sizeof(address));
    21       address.sin_family=AF_INET;
    22       inet_pton(AF_INET,ip,&address.sin_addr);
    23       address.sin_port=htons(port);
    24       
    25       int sockfd=socket(AF_INET,SOCK_STREAM,0);
    26       assert(sockfd!=-1);
    27       
    28       ret=bind(sockfd,(struct sockaddr*)&address,sizeof(address));
    29       assert(ret!=-1);
    30       
    31       ret=listen(sockfd,5);
    32       assert(ret!=-1);
    33       
    34       while(1){
    35           struct sockaddr_in peer;
    36           bzero(&peer,sizeof(peer));
    37           socklen_t len=sizeof(peer);
    38           
    39           int connfd=accept(sockfd,(struct sockaddr*)&peer,len);
    40           if(connfd<0){
    41               printf("errno is:%d
    ",errno);
    42               break;
    43           }
    44           else{
    45               int pipefd[2];
    46               ret=pipe(pipefd);
    47               assert(ret!=-1);
    48               
    49               /*将connfd上流入的客户端数据定向到管道中*/
    50               ret=splice(connfd,NULL,pipefd[1],NULL,32768,SPLICE_F_MORE|
    51                                           SPLICE_F_MOVE);
    52               assert(ret!=-1);
    53               /*将管道中的数据定向到connfd的客户端文件描述符上*/
    54               splice(pipefd[0],NULL,connfd,NULL,32768,SPLICE_F_MORE|
    55                                           SPLICE_F_MOVE);
    56               assert(ret!=-1);
    57            }          
    58       }
    59       
    60       close(connfd);
    61       close(sockfd);
    62       return 0;
    63 }

    我们通过splice函数将从客户端的内容读入到pipefd[1]中,然后再使用splice函数从pipefd[0]中读出该内容到客户端。从而实现了简单高效的回射服务。整个过程为执行recv/send操作,因此未涉及用户空间和内核空间之间的数据拷贝。

       

        

         

  • 相关阅读:
    Mysql 从库的备份中恢复一张表
    my.cnf 配置文件参数解释
    利用mvn deploy命令上传包(转)
    IntelliJ IDEA 项目文件旁边都有0%classes,0% lines covered
    idea启动java Maven项目,出现" java: 程序包xxxx不存在"
    org/apache/poi/POIXMLTypeLoader或者java.lang.NoSuchFieldError: RETURN_NULL_AND_BLANK
    elasticsearch,kibana,logstash.下载
    idea显示 RunDashboard ,多个启动项时列表显示
    写for循环快捷生成方式
    Could not transfer artifact xxx from/to xxx解决方案
  • 原文地址:https://www.cnblogs.com/sixue/p/3973343.html
Copyright © 2020-2023  润新知