初探sendfile「建议收藏」

初探sendfile「建议收藏」很早就知道sendfile这个专门用来传输大文件的函数,也称为零拷贝,但一直没测试过,今天用了宿舍的网和小组内的网测试了下,发现结果和我预想的不一样。为什么效率高,网上说的也很多了,看下man手册中的内容sendfile()copiesdatabetweenonefiledescriptorandanother.Becausethiscopyingisdo

大家好,又见面了,我是你们的朋友全栈君。

很早就知道sendfile这个专门用来传输大文件的函数,也称为零拷贝,但一直没测试过,今天用了宿舍的网和小组内的网测试了下,发现结果和我预想的不一样。


为什么效率高,网上说的也很多了,看下man手册中的内容
sendfile() copies data between one file descriptor and another. Because this copying is done within the kernel, sendfile() is more efficient than the combination of read(2) and write(2), which would require transferring data to and from user space.
普通的read和write先会将数据拷贝到用户空间,然后再拷贝到内核空间,然后从tcp缓冲区发送出去。sendfile避免了多余的拷贝。


测试过程:

通过hdparm -Tt /dev/sda
测试了我的磁盘IO大约是110MB/sec

通过bmon
宿舍网大概150k/s,小组内网大概4.5~5.5MB/s。
只是测试了个大概。参考而已

服务端跑在学长借我的服务器上测试。


数据有限,我测试的分别为7.5M,90M,2G的数据。
起初宿舍网络有点慢,read,write版本和sendfile版本运行出来的时间都几乎差不多。然后我在小组测试网速4.5~5.5MB/s,跑出来的数据竟然也差不多,有时read,write版本竟然比sendfile版本的时间短
很奇怪,最后一想,磁盘IO的性能依然是远大于网络IO的性能,我测试的两种网络带宽远远不够。


测试结论:

最后询问了学长,得知4.5mb/s-5.5mb/s也就是带宽为36-44mib/s的网络IO是远远不够的,用ifconfig查看本机ip来测试,本机千兆网卡测试出来传送2.2G数据sendfile效率的确高于read,write。
那么像我们普通这种网络带宽根本不能满足且突出sendfile的性能。


测试代码:客户端

客户端两个版本,一个为read,write,一个为sendfile,修改注释即可

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/sendfile.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

int main(int argc, char *argv[])
{
    if(argc < 3){
        printf("argument error\n");
        exit(1);
    }
    char *ip = argv[1];
    int port = atoi(argv[2]);

    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(port);
    inet_pton(AF_INET, ip, &server.sin_addr);

    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if(fd < 0){
        printf("create socket error\n");
        exit(1);
    }
    int con_ret = connect(fd, (struct sockaddr*)&server, sizeof(server));
    if(con_ret == -1){
        printf("connect error\n");
        exit(1);
    }
    char *name = argv[3];
    int r_fd = open(name, O_RDONLY);
    if(r_fd == -1){
        printf("open file error\n");
        exit(1);
    }
    struct stat st;
    long int size = stat(name, &st);
    if(size < 0){
        printf("file stat error\n");
        exit(1);
    }
    printf("file size:%ld\n", st.st_size);
    off_t pos = lseek(r_fd, 0, SEEK_SET);
    if(pos < 0){
        printf("obtain fileP error\n");
        exit(1);
    }
    char *cname = "newfile";
    int sn = send(fd, cname, sizeof(argv[3]), 0);
    struct timeval startTime, endTime;
    double timeuse;
    gettimeofday(&startTime, NULL);
    //sendfile测试版本
    //int n = sendfile(fd, r_fd, &pos, st.st_size);
    long int ssize = 0;
    char buffer[2048];
    long int n = 0;
    //read,write测试版本
    while(1){
        bzero(buffer, 2048);
        int rn = read(r_fd, buffer, 2048);
        int wn = write(fd, buffer, 2048);
        if(n >= st.st_size){
            printf("size:%ld\n", n);
            printf("size:%ld\n", st.st_size);
            break;
        }
        n += rn;
    }
    gettimeofday(&endTime, NULL);
    timeuse = 1000000*(endTime.tv_sec - startTime.tv_sec) + (endTime.tv_usec - startTime.tv_usec);
    timeuse /= 1000000;
    printf("timeuse = %lf\n", timeuse);
    if(n == -1){
        printf("send file error\n");
        exit(1);
    }

    return EXIT_SUCCESS;
}

测试代码:服务器

服务端测试代码不变

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <string.h>

int main(int argc, char *argv[])
{
    if(argc < 2){
        printf("argument error\n");
        exit(1);
    }
    char *ip = argv[1];
    int port = atoi(argv[2]);

    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(port);
    inet_pton(AF_INET, ip, &server.sin_addr);

    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if(fd < 0){
        printf("create socket error\n");
        exit(1);
    }
    int b_ret = bind(fd, (struct sockaddr*)&server, sizeof(server));
    if(b_ret == -1){
        printf("bind error\n");
        exit(1);
    }
    int l_ret = listen(fd, 64);
    if(l_ret == -1){
        printf("listen error\n");
        exit(1);
    }
    socklen_t len = sizeof(server);
    int sockfd = accept(fd, (struct sockaddr*)&server, &len);
    if(sockfd == -1){
        printf("accept error\n");
        exit(1);
    }else{
        printf("connect success\n");
    }
    char name[128];
    char buffer[2048];
    bzero(name, 128);
    bzero(buffer, 2048);
    int n = recv(sockfd, name, 2048, 0);
    if(n <= 0){
        printf("recv error\n");
        exit(1);
    }
    printf("name:%s\n", name);
    int w_fd = open(name, O_WRONLY | O_CREAT, 777);
    if(w_fd == -1){
        printf("open file error\n");
        exit(1);
    }
    off_t pos = lseek(w_fd, 0, SEEK_CUR);
    if(pos < 0){
        printf("obtain file pointer error\n");
        exit(1);
    }
    while(1){
        bzero(buffer, 2048);
        int n = recv(sockfd, buffer, 2048, 0);
        if(n < 0){
            printf("recv error\n");
            exit(1);
        }else if(n == 0){
            break;
        }
        ssize_t s_t = write(w_fd, buffer, n);
        if(s_t < 0){
            printf("write error\n");
            exit(1);
        }
    }
    printf("recv file success!!!\n");


    return EXIT_SUCCESS;
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/134831.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号