Chinaunix首页 | 论坛 | 博客
  • 博客访问: 388855
  • 博文数量: 55
  • 博客积分: 1907
  • 博客等级: 上尉
  • 技术积分: 869
  • 用 户 组: 普通用户
  • 注册时间: 2010-11-04 19:30
文章分类

全部博文(55)

文章存档

2011年(32)

2010年(23)

分类: LINUX

2011-04-30 17:18:26

包裹 read 和 write 系统调用
--------------------------------------------------------------------------------
字节流套接字(TCP套接字)上的 read 和 write 函数所表现的行为不同于通常的
文件 I/O。字节流套接字上调用 read 和 write 输入或者输出的字节数可能比请求
的少,然而这不是出错的状态。这个现象的原因在于内核中用于套接字的缓冲区可能
已到达了极限。此时需要的是调用者再次调用 read 和 write 函数,以输入或输出
剩下的字节数。所以为了防止在我们的应用程序在一次调用 read 和 write 函数时
返回一个异于我们目标的字节数,我们可以为这两个系统调用实现一个包裹函数来
实现我们的目标。
 
readn 跟 writen 函数均能满足套接字缓冲区大于或小于目标字节的情况。
 
以下程序来自 UNIX 网络编程
 
  1. /*-------------------------------------------------------------------------*/
  2. //函数原型声明
  3. ssize_t readn( int filedes, void *buff, size_t nbytes );
  4. ssize_t writen( int filedes, const void *buff, size_t nbytes );

  5.                                       均返回:读或写的字节数,若出错则为-1
  6. /*--------------------------------------------------------------------------*/
  1. /* include readn */
  2. #include    "unp.h"

  3. ssize_t                        /* Read "n" bytes from a descriptor. */
  4. readn(int fd, void *vptr, size_t n)
  5. {
  6.     size_t    nleft;
  7.     ssize_t    nread;
  8.     /*
  9.      * 需将 void * 型指针转换成 char * 型指针。因为指针必须按所读或缩写的
  10.      * 字节数增长,但是C不允许void 指针如此增长(因为C编译器不知道void指向
  11.      * 的数据类型)
  12.       */
  13.     char    *ptr;

  14.     ptr = vptr;
  15.     nleft = n;
  16.     while (nleft > 0) {
  17.         if ( (nread = read(fd, ptr, nleft)) < 0) {
  18.             if (errno == EINTR) /* 系统调用被一个捕获的信号中断 */
  19.                 nread = 0;        /* and call read() again */
  20.             else
  21.                 return(-1);
  22.         } else if (nread == 0)
  23.             break;                /* EOF */

  24.         nleft -= nread;
  25.         ptr += nread;
  26.     }
  27.     return(n - nleft);        /* return >= 0 */
  28. }
  29. /* end readn */

  30. ssize_t
  31. Readn(int fd, void *ptr, size_t nbytes)
  32. {
  33.     ssize_t        n;

  34.     if ( (n = readn(fd, ptr, nbytes)) < 0)
  35.         err_sys("readn error");
  36.     return(n);
  37. }
 
  1. /* include writen */
  2. #include    "unp.h"

  3. ssize_t                        /* Write "n" bytes to a descriptor. */
  4. writen(int fd, const void *vptr, size_t n)
  5. {
  6.     size_t        nleft;
  7.     ssize_t        nwritten;
  8.     const char    *ptr;

  9.     ptr = vptr;
  10.     nleft = n;
  11.     while (nleft > 0) {
  12.         if ( (nwritten = write(fd, ptr, nleft)) <= 0) {
  13.             if (nwritten < 0 && errno == EINTR)
  14.                 nwritten = 0;        /* and call write() again */
  15.             else
  16.                 return(-1);            /* error */
  17.         }

  18.         nleft -= nwritten;
  19.         ptr += nwritten;
  20.     }
  21.     return(n);
  22. }
  23. /* end writen */

  24. void
  25. Writen(int fd, void *ptr, size_t nbytes)
  26. {
  27.     if (writen(fd, ptr, nbytes) != nbytes)
  28.         err_sys("writen error");
  29. }
 
 
下面再贴一个文件复制的经典例子(同样考虑并改善了文章开头提到的情况)。
要仔细理解下面这两个语句。
  1. while ( bytes_read = read(from_fd,buffer,BUFFER_SIZE) );
  2. while ( bytes_write = write(to_fd, ptr, bytes_read) );
当一个文件读完或者写完的时候返回的是0,那么就会跳出 while 循环了。
 
  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <fcntl.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <errno.h>
  7. #define BUFFER_SIZE 1024

  8. int main(int argc,char **argv)
  9. {
  10.     int from_fd, to_fd;
  11.     int bytes_read, bytes_write;
  12.     char buffer[BUFFER_SIZE];
  13.     char *ptr;

  14.     if ( argc != 3 ) {
  15.         fprintf(stderr,"Usage:%s fromfile tofile\n",argv[0]);
  16.         exit(1);
  17.     }
  18.     
  19.     /* 打开源文件 */
  20.     if( (from_fd = open(argv[1], O_RDONLY)) == -1 )    {
  21.         fprintf(stderr, "Open %s Error:%s\n",argv[1], strerror(errno));
  22.         exit(1);
  23.     }

  24.     /* 创建目的文件 */
  25.     if ( (to_fd=open(argv[2], O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR)) == -1 ) {
  26.         fprintf(stderr, "Open %s Error:%s\n",argv[2], strerror(errno));
  27.         exit(1);
  28.     }
  29.     
  30.     /* 以下代码是一个经典的拷贝文件的代码 */
  31.     while ( bytes_read = read(from_fd,buffer,BUFFER_SIZE) ) {
  32.     
  33.         /* 一个致命的错误发生了 */
  34.         if ( (bytes_read==-1) && (errno!=EINTR) ) break;

  35.         else if ( bytes_read > 0 ) {

  36.             ptr = buffer; /* 指针重新指向缓冲区头部,因为接下来要从缓冲区读数据 */

  37.             while ( bytes_write = write(to_fd, ptr, bytes_read) ) {

  38.                 /* 一个致命错误发生了 */

  39.                 if ( (bytes_write==-1) && (errno!=EINTR) ) break;

  40.                 /* 写完了所有读的字节 */

  41.                 else if ( bytes_write == bytes_read ) break;

  42.                 /* 只写了一部分,继续写 */

  43.                 else if ( bytes_write > 0 ) {
  44.                     ptr += bytes_write;
  45.                     bytes_read -= bytes_write;
  46.                 }
  47.             }

  48.             /* 写的时候发生的致命错误 */
  49.             if ( bytes_write == -1 ) break;
  50.         }
  51.     }

  52.     close(from_fd);
  53.     close(to_fd);
  54.     exit(0);
  55. }
阅读(2317) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~