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

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

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 使用SQL游标进行循环插入数据「建议收藏」

    使用SQL游标进行循环插入数据「建议收藏」使用SQL游标进行循环插入数据–给新建页面添加权限控制declare@RoleIDvarchar(50)declare@Opratorvarchar(50)DECLAREAutoInsertRowCURSORFORSELECTdistinctRoleID,OperatorFROMeOPENAutoInsertRow;FETCHNEXTFROM

    2022年7月14日
    17
  • datagroup激活码_通用破解码[通俗易懂]

    datagroup激活码_通用破解码,https://javaforall.net/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

    2022年3月17日
    45
  • 机器学习python_深入浅出Python机器学习[PDF][176.39MB]

    机器学习python_深入浅出Python机器学习[PDF][176.39MB]内容简介机器学习正在迅速改变我们的世界。我们几乎每天都会读到机器学习如何改变日常的生活。如果你在淘宝或者*这样的电子商务网站购买商品,或者在爱奇艺或是腾讯视频这样的视频网站观看节目,甚至只是进行一次百度搜索,就已经触碰到了机器学习的应用。使用这些服务的用户会产生数据,这些数据会被收集,在进行预处理之后用来训练模型,而模型会通过这些数据来提供更好的用户体验。此外,目前还有很多使用机器学习技术的产品或…

    2022年10月17日
    5
  • PyCharm 自行整理的快捷键「建议收藏」

    PyCharm 自行整理的快捷键「建议收藏」工欲善其事,必先利其器,整理了下PyCharm最常用的快捷键可编辑版本下载:PyCharm快捷键(可编辑文件)

    2022年8月27日
    11
  • Java集合中对象排序

    Java集合中对象排序

    2022年1月26日
    45
  • vim退出编辑模式非esc_centos保存退出vim

    vim退出编辑模式非esc_centos保存退出vimlinux中退出vi编辑器,按下esc没反应的解决办法:1、在正常模式下按下q键盘;2、选择【a-z】或【0-9】中任意一个作为缓冲器的名字,准备开始录制宏;3、在非insert模式下输入q停止宏的录制;4、使用@和定义的缓冲器名字即可。linux中退出vi编辑器,按下esc没反应的解决办法:vimrecording功能介绍使用vim时无意间触碰到q键,左下角出现“recording”这个标识,…

    2022年9月26日
    3

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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