免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12
最近访问板块 发新帖
楼主: osmanthusgfy

[函数] linux下有没有支持直接从socket中接收文件的API? [复制链接]

论坛徽章:
0
发表于 2011-02-22 15:03 |显示全部楼层
本帖最后由 osmanthusgfy 于 2011-02-22 15:04 编辑

回复 8# xinglp


报告一下测试使用splice的感受,顺便分享一下自己的总结,
先上一段使用splice本地复制文件的代码:

  1. /*
  2. * ================================================================
  3. * Name:      SpliceDemo.c
  4. * Author:        
  5. * Copyright: BSD
  6. * ================================================================
  7. */

  8. #define _GNU_SOURCE

  9. #include <errno.h>
  10. #include <fcntl.h>
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <sys/stat.h>
  14. #include <unistd.h>

  15. #define FILE_IN_PATH "/home/a.rmvb"
  16. #define FILE_OUT_PATH "a.rmvb"

  17. //len=65536

  18. int main( int argc, char** argv )
  19. {
  20.         int pipefd[2];

  21.         int infd = open( FILE_IN_PATH, O_RDONLY );
  22.         int outfd;

  23.         long max_pipe_buf_size;

  24.         struct stat statbuf;
  25.         long infile_size;

  26.         long total_cpsize = 0;
  27.         ssize_t cpsize;

  28.         if ( -1 == infd ) {
  29.                 fprintf( stderr, "open1 error:%s\n", strerror( errno ) );
  30.                 return EXIT_FAILURE;
  31.         }

  32.         outfd = open( FILE_OUT_PATH, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IXUSR );

  33.         if ( -1 == outfd ) {
  34.                 fprintf( stderr, "open2 error:%s\n", strerror( errno ) );

  35.                 close( infd );

  36.                 return EXIT_FAILURE;
  37.         }

  38.         if ( -1 == pipe( pipefd ) ) {
  39.                 fprintf( stderr, "pipe error:%s\n", strerror( errno ) );

  40.                 close( infd );
  41.                 close( outfd );

  42.                 return EXIT_FAILURE;
  43.         }

  44.         max_pipe_buf_size = pathconf( ".", _PC_PIPE_BUF );

  45.         if ( -1 == max_pipe_buf_size ) {
  46.                 fprintf( stderr, "pathconf error:%s\n", strerror( errno ) );

  47.                 close( infd );
  48.                 close( outfd );

  49.                 close( pipefd[0] );
  50.                 close( pipefd[1] );

  51.                 return EXIT_FAILURE;
  52.         }

  53.         fprintf( stderr, "_PC_PIPE_BUF=%ld\n", max_pipe_buf_size );

  54.         if ( -1 == fstat( infd, &statbuf ) ) {
  55.                 fprintf( stderr, "fstat error:%s\n", strerror( errno ) );

  56.                 close( infd );
  57.                 close( outfd );

  58.                 close( pipefd[0] );
  59.                 close( pipefd[1] );

  60.                 return EXIT_FAILURE;
  61.         }

  62.         infile_size = statbuf.st_size;

  63.         fprintf( stderr, "filesize=%ld\n", infile_size );

  64.         if ( 0 == infile_size ) {
  65.                 fprintf( stderr, "Done1.\n" );

  66.                 close( infd );
  67.                 close( outfd );

  68.                 close( pipefd[0] );
  69.                 close( pipefd[1] );

  70.                 return EXIT_SUCCESS;
  71.         }

  72.         while ( infile_size != total_cpsize ) {
  73.                 cpsize = splice( infd, NULL, pipefd[1], NULL, max_pipe_buf_size,
  74.                                 SPLICE_F_MOVE | SPLICE_F_MORE );

  75.                 if ( -1 == cpsize ) {
  76.                         fprintf( stderr, "splice1 error:%s\n", strerror( errno ) );

  77.                         close( infd );
  78.                         close( outfd );

  79.                         close( pipefd[0] );
  80.                         close( pipefd[1] );

  81.                         return EXIT_FAILURE;
  82.                 }

  83.                 cpsize = splice( pipefd[0], NULL, outfd, NULL, max_pipe_buf_size,
  84.                                 SPLICE_F_MOVE | SPLICE_F_MORE );

  85.                 if ( -1 == cpsize ) {
  86.                         fprintf( stderr, "splice2 error:%s\n", strerror( errno ) );

  87.                         close( infd );
  88.                         close( outfd );

  89.                         close( pipefd[0] );
  90.                         close( pipefd[1] );

  91.                         return EXIT_FAILURE;
  92.                 }

  93.                 total_cpsize += cpsize;
  94.         }

  95.         fprintf( stderr, "total_cpsize=%ld\n", total_cpsize );

  96.         close( infd );
  97.         close( outfd );

  98.         close( pipefd[0] );
  99.         close( pipefd[1] );

  100.         fprintf( stderr, "Done.\n" );

  101.         return EXIT_SUCCESS;
  102. }
复制代码
经测试表明:splice一次最大能操作的数据的缓冲区的大小为65536byte
(我的OS为Fedora 14, kernel版本为2.6.35.10-74.fc14.i686),
所以拷贝较大的文件时必须循环着调用splice;又由于splice必须结合管道使用,
而OS对管道一次读写数据的缓冲区的最大大小有限制,可以用pathconf/fpathconf
根据_PC_PIPE_BUF参数获取,所以一般splice操作的缓冲区的大小最好为_PC_PIPE_BUF的值.


以上代码,本地拷贝文件,表现良好.
将以上代码稍作修改,可以用来从socket中接收文件.在网络环境中使用splice时需注意两点:
1>.设置socket为非阻塞模式时,需检测"EAGAIN == errno"的情况;
2>.若文件发送端非正常断开时,splice返回0,这与read从socket读取数据是一样的.

经测试,本机经socket传输文件,发送端使用sendfile,接收端使用splice,
大部分情况,收发文件正常,但少数情况是:接收端还没有接收完毕,发送端sendfile已经返回,结束了传输,
所以接收端使splice返回0,导致文件接收不全.感觉splice从socket接收文件不太稳定,
难道是因为本机测试的原因吗?不知道有人碰到过这个情况没.


以上有什么错误的地方,敬请指正,谢谢!

论坛徽章:
5
2015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:53:172015亚冠之水原三星
日期:2015-06-02 16:34:202015年亚冠纪念徽章
日期:2015-10-19 18:13:37程序设计版块每日发帖之星
日期:2015-11-08 06:20:00
发表于 2011-02-23 13:36 |显示全部楼层
回复 11# osmanthusgfy

pathconf( ".", _PC_PIPE_BUF ); //这是什么意思呢? 为什么不是pathconf(pipefd[0],xxxx);

splice() 怎么会接收不全呢, 我用它来说用户态的数据转发很好用啊.
你用recv()能把文件收全了么?

论坛徽章:
0
发表于 2011-02-24 14:47 |显示全部楼层
本帖最后由 osmanthusgfy 于 2011-02-24 14:51 编辑

回复 12# xinglp

感谢大哥的回复!


首先指出大哥的一个笔误"pathconf(pipefd[0],xxxx);" 应该是"fpathconf(pipefd[0],xxxx);"

然后指出我之前的一句"splice一次最大能操作的数据的缓冲区的大小为65536byte",
仅测试fpathconf( pipefd[1], _PC_PIPE_BUF )返回的值刚好为65536,所以我这句话不太准确.

经过再次测试,还是得出:
使用splice本机拷贝文件,表现良好;
本机socket CS传输文件,发送端使用sendfile,接收端使用splice,
接收端有时接收不完整,有时,CS都阻塞了(感觉是某一端死了).阻塞的频率很高,感觉极其不稳定.
我测试,若接收端使用read-write方式,则没有这些问题,接收文件也很稳定.
不知大哥在网络环境下使用过splice了没?

贴上CS的代码,若有错误的地方,敬请指正,谢谢!

Server:

  1. /*
  2. * ================================================================
  3. * Name:      Serv.c
  4. * Author:        
  5. * Copyright: BSD
  6. * ================================================================
  7. */

  8. #define _GNU_SOURCE

  9. #include <arpa/inet.h>                //inet_addr
  10. #include <ctype.h>                      //isdigit
  11. #include <errno.h>
  12. #include <fcntl.h>
  13. #include <netdb.h>                        //hostent, gethostbyname
  14. #include <netinet/in.h>     //IPPROTO_TCP, sockaddr_in, htons, INADDR_ANY
  15. #include <netinet/tcp.h>                //TCP_NODELAY
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <string.h>
  19. #include <sys/sendfile.h>
  20. #include <sys/socket.h>                 //socket
  21. #include <sys/types.h>
  22. #include <unistd.h>


  23. #define BUF_SIZE         1024
  24. #define FILE_PATH "a.rmvb"
  25. #define FILE_SIZE 392822171
  26. #define PORT                 8888

  27. int Name2Addr( const char* aName, in_addr_t* aAddr )
  28. {
  29.         struct hostent* host;

  30.         if ( 0 < isdigit( (int)(*aName) ) )
  31.                 *aAddr = inet_addr( aName );

  32.         else {
  33.                 if( NULL == ( host = gethostbyname( aName ) ) )
  34.                         return -1;

  35.                 memcpy( (char*)aAddr, host->h_addr_list[0], host->h_length );
  36.         }

  37.         return 0;
  38. }

  39. ssize_t splice_recvfile( const char* path, int infd, const size_t count )
  40. {
  41.         if ( 0 > count ) {
  42.                 errno = EINVAL;
  43.                 return -1;
  44.         }

  45.         int pipefd[2];
  46.         long max_pipe_buf_size;

  47.         long total_cpcount = 0;
  48.         ssize_t recv_count;
  49.         ssize_t out_cout;

  50.         int i = 0;

  51.         fprintf( stderr, "count=%d\n", count );

  52.         if ( -1 == pipe( pipefd ) ) {
  53.                 return -1;
  54.         }

  55.         max_pipe_buf_size = fpathconf( pipefd[1], _PC_PIPE_BUF );

  56.         fprintf( stderr, "max_pipe_buf_size=%ld\n", max_pipe_buf_size );

  57.         if ( -1 == max_pipe_buf_size ) {
  58.                 close( pipefd[0] );
  59.                 close( pipefd[1] );

  60.                 return -1;
  61.         }

  62.         int outfd = open( path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IXUSR );

  63.         if ( -1 == outfd ) {
  64.                 close( pipefd[0] );
  65.                 close( pipefd[1] );

  66.                 return -1;
  67.         }

  68.         while ( count != total_cpcount ) {
  69.                 recv_count = splice( infd, NULL, pipefd[1], NULL, max_pipe_buf_size,
  70.                                 SPLICE_F_MOVE | SPLICE_F_MORE );

  71.                 if ( EAGAIN == errno )
  72.                         continue;

  73.                 if ( -1 == recv_count ) {
  74.                         goto do_err_label;
  75.                 } else if ( 0 == recv_count ) {
  76.                         close( pipefd[0] );
  77.                         close( pipefd[1] );
  78.                         close( outfd );

  79.                         return 0;
  80.                 }

  81.                 //fprintf( stderr, "recv_count=%d\n", recv_count );

  82.                 i = 0;

  83.                 do {
  84.                         ++i;

  85.                         out_cout = splice( pipefd[0], NULL, outfd, NULL, max_pipe_buf_size,
  86.                                 SPLICE_F_MOVE | SPLICE_F_MORE );

  87.                         fprintf( stderr, "i=%d write...\n", i );
  88.                 } while ( -1 == out_cout && EAGAIN == errno );

  89.                 if ( -1 == out_cout )
  90.                         goto do_err_label;

  91.                 if ( out_cout != recv_count ) {
  92.                         fprintf( stderr, "out_cout != cp_count\n" );
  93.                 }

  94.                 total_cpcount += out_cout;
  95.         }

  96.         close( pipefd[0] );
  97.         close( pipefd[1] );
  98.         close( outfd );

  99.         fprintf( stderr, "total_cpcount=%ld\n", total_cpcount );

  100.         return total_cpcount;

  101.         do_err_label: {
  102.                 close( pipefd[0] );
  103.                 close( pipefd[1] );
  104.                 close( outfd );

  105.                 return -1;
  106.         }
  107. }

  108. int main( int argc, char** argv )
  109. {
  110.         int clientfd;
  111.         int servfd;

  112.         struct sockaddr_in clientaddr;
  113.         socklen_t clientaddrlen = sizeof( clientaddr );

  114.         servfd = socket( AF_INET, SOCK_STREAM, 0 );

  115.         if ( -1 == servfd ) {
  116.                 fprintf( stderr, "socket error:%s\n", strerror( errno ) );

  117.                 return EXIT_FAILURE;
  118.         }

  119.         int optval = 1;
  120.         if ( -1 == setsockopt( servfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof( optval ) ) ) {
  121.                 fprintf( stderr, "setsockopt1 error:%s\n", strerror( errno ) );
  122.                 close( servfd );

  123.                 return EXIT_FAILURE;
  124.         }

  125.         if ( -1 == setsockopt( servfd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof( optval ) ) ) {
  126.                 fprintf( stderr, "setsockopt2 error:%s\n", strerror( errno ) );
  127.                 close( servfd );

  128.                 return EXIT_FAILURE;
  129.         }

  130.         struct sockaddr_in localaddr;

  131.         localaddr.sin_family = AF_INET;
  132.         localaddr.sin_port = htons( PORT );
  133.         localaddr.sin_addr.s_addr = INADDR_ANY;

  134.         if ( -1 == bind( servfd, (struct sockaddr*)(&localaddr), sizeof( localaddr ) ) ) {
  135.                 fprintf( stderr, "bind error:%s\n", strerror( errno ) );
  136.                 close( servfd );

  137.                 return EXIT_FAILURE;
  138.         }

  139.         fprintf( stderr, "listening...\n" );

  140.         if ( -1 == listen( servfd, 1024 ) ) {
  141.                 fprintf( stderr, "listen error:%s\n", strerror( errno ) );
  142.                 close( servfd );

  143.                 return EXIT_FAILURE;
  144.         }

  145.         clientfd = accept( servfd, (struct sockaddr*)(&clientaddr), &clientaddrlen );

  146.         if ( -1 == clientfd ) {
  147.                 fprintf( stderr, "accept error:%s\n", strerror( errno ) );
  148.                 close( servfd );

  149.                 return EXIT_FAILURE;
  150.         }

  151.         if ( -1 == splice_recvfile( FILE_PATH, clientfd, FILE_SIZE ) )
  152.                 fprintf( stderr, "splice_recvfile error:%s\n", strerror( errno ) );

  153.         close( clientfd );
  154.         close( servfd );

  155.         fprintf( stderr, "Done.\n" );

  156.         return EXIT_SUCCESS;
  157. }
复制代码
Client:

  1. /*
  2. * ================================================================
  3. * Name:      Client.c
  4. * Author:        
  5. * Copyright: BSD
  6. * ================================================================
  7. */

  8. #include <arpa/inet.h>
  9. #include <ctype.h>
  10. #include <errno.h>
  11. #include <fcntl.h>
  12. #include <netdb.h>
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. #include <string.h>
  16. #include <sys/sendfile.h>
  17. #include <sys/socket.h>
  18. #include <sys/stat.h>
  19. #include <sys/types.h>
  20. #include <unistd.h>

  21. #define SERV_PORT 8888
  22. #define SERV_ADDR "127.0.0.1"

  23. #define FILE_PATH "/home/a.rmvb"

  24. static int Name2Addr( const char* aName, in_addr_t* aAddr )
  25. {
  26.         struct hostent* host;

  27.         if ( 0 < isdigit( (int)(*aName) ) )
  28.                 *aAddr = inet_addr( aName );

  29.         else {
  30.                 if( NULL == ( host = gethostbyname( aName ) ) )
  31.                         return -1;

  32.                 memcpy( (char*)aAddr, host->h_addr_list[0], host->h_length );
  33.         }

  34.         return 0;
  35. }

  36. #include <limits.h>

  37. int main( int argc, char** argv )
  38. {
  39.         struct sockaddr_in servaddr;

  40.         struct stat statbuf;

  41.         int infd;
  42.         int sockfd = socket( AF_INET, SOCK_STREAM, 0 );
  43.         ssize_t sendsize;


  44.         if ( -1 == sockfd ) {
  45.                 fprintf( stderr, "Socket error:%s\n.", strerror( errno ) );
  46.                 return EXIT_FAILURE;
  47.         }

  48.         servaddr.sin_family = AF_INET;
  49.         servaddr.sin_port = htons( (short)SERV_PORT );

  50.         if ( -1 == Name2Addr( SERV_ADDR, &servaddr.sin_addr.s_addr ) ) {
  51.                 fprintf( stderr, "Name2Addr error:%s\n.", strerror( errno ) );
  52.                 close( sockfd );

  53.                 return EXIT_FAILURE;
  54.         }

  55.         fprintf( stderr, "connect...\n" );

  56.         if ( -1 == connect( sockfd, (struct sockaddr*)(&servaddr),
  57.                         sizeof( struct sockaddr ) ) ) {
  58.                 fprintf( stderr, "Connect error:%s\n.", strerror( errno ) );
  59.                 close( sockfd );

  60.                 return EXIT_FAILURE;
  61.         }

  62.         infd = open( FILE_PATH, O_RDONLY );

  63.         if ( -1 == infd ) {
  64.                 fprintf( stderr, "Open error:%s\n", strerror( errno ) );
  65.                 close( sockfd );

  66.                 return EXIT_FAILURE;
  67.         }

  68.         if ( -1 == fstat( infd, &statbuf ) ) {
  69.                 fprintf( stderr, "Fstat error:%s\n", strerror( errno ) );

  70.                 close( infd );
  71.                 close( sockfd );

  72.                 return EXIT_FAILURE;
  73.         }

  74.         fprintf( stderr, "Sendfile...\n" );

  75.         if ( -1 == ( sendsize = sendfile( sockfd, infd, 0, statbuf.st_size ) ) ) {
  76.                 fprintf( stderr, "Sendfile error:%s\n", strerror( errno ) );

  77.                 close( infd );
  78.                 close( sockfd );

  79.                 return EXIT_FAILURE;
  80.         }

  81.         fprintf( stderr, "statbuf.st_size=%d\n", statbuf.st_size );
  82.         fprintf( stderr, "sendsize=%d\n", sendsize );

  83.         close( infd );
  84.         close( sockfd );

  85.         fprintf( stderr, "Done.\n" );

  86.         return EXIT_SUCCESS;
  87. }
复制代码

论坛徽章:
0
发表于 2011-02-24 15:25 |显示全部楼层
在网上搜了一下:
Linux Kernel 'splice(2)'两次锁本地拒绝服务漏洞
http://www.securityfocus.com/bid/35143

vmsplice也曾爆过漏洞.

不知道我的情况和这有关没?
我也是在本地测试的.

论坛徽章:
5
2015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:53:172015亚冠之水原三星
日期:2015-06-02 16:34:202015年亚冠纪念徽章
日期:2015-10-19 18:13:37程序设计版块每日发帖之星
日期:2015-11-08 06:20:00
发表于 2011-02-25 11:55 |显示全部楼层
回复 14# osmanthusgfy


   你的内核版本号在那个列表里面, 升级一下试试呢, 我用的2.6.37.1

论坛徽章:
0
发表于 2011-02-25 13:55 |显示全部楼层
我的内核版本是2.6.35.11,

难道我的问题真的与内核版本有关系?

看来splice还是不太稳定.
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP