readn和writen

下面是read和write的函数原型
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);    //count通常取sizeof(buf)
返回值:若成功,返回读取的字节数(不一定是count),出错返回-1并设置errno,如果在调read之前已到达文件末尾,则这次read返回0。文件的当前读写位置向后移。

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
返回值:若成功,返回写入的字节数(不一定是count),出错返回-1并设置errno。

read普通的文件如磁盘文件时,返回值通常=count,若<count,通常是到达文件尾了,下次read一般会返回0。所以一般会使用如下语句:
if(n=read( fd, buf,  count)>0)
write( fd, buf, n);
但是读管道、FIFO、终端和socket时,很可能<count,即使没有到达文件尾返回值也通常这样,这时应该继续读。

当write普通的文件如磁盘文件时,返回值通常=count,若<count,通常是出现了问题,所以一般会使用如下语句:
if(write( fd, buf,  count)!=count)
err_sys("write error");

但是write管道、FIFO、终端和socket时,很可能<count,这时候并没有出错,应该继续写余下的数据。(通常只有对非阻塞描述符,或者阻塞描述符但捕捉到一个信号时,才发生这种write的中途返回)

write将buf中的count数量字节内容写入文件描述符fd引用的打开文件中,成功时返回写的字节数,失败时返回-1。 并设置errno变量. 在网络程序中,当我们向套接字文件描述符写时有两种可能.
1)write的返回值大于0,表示写了部分或者是全部的数据.
2)返回的值小于0,此时出现了错误.我们要根据错误类型来处理.

如果错误为EINTR表示在写的时候出现了中断错误.
如果为EPIPE表示网络连接出现了问题(对方已经关闭了连接).


于是对管道、FIFO、终端和socket等文件,我们自定义了下面的函数,直至读写了指定的n字节数据才返回。
版本1

#include"apue.h"
ssize_t readn(int fd,void *ptr,size_t n)
{
    size_t        nleft;
    ssize_t        nread;

    nleft = n;
    while (nleft> 0) {
        if ((nread= read(fd, ptr, nleft))< 0) {        
//当read返回-1,

            if (nleft== n)
                return(-1);                                /*若第一次read时出错, readn 返回 -1 */
            else
                break;                             /*若读了一些数据后出错, readn返回已经读的字节数,而不出错返回 */
        } elseif (nread == 0){                        
//当read返回0时,不再读下去,readn返回已经读的字节数

            break;/* EOF */
        }
        nleft -= nread;                                    
//当read返回值>0但不等于n时,继续读

        ptr += nread;
    }
    return(n- nleft);/* return >= 0 */
}

ssize_t writen(int fd,const void *ptr, size_t n)
    size_t        nleft;
    ssize_t        nwritten;

    nleft = n;
    while (nleft 0){
        if ((nwritten= write(fd, ptr, nleft))< 0) {    
//当write返回-1,

            if (nleft== n)                                /*若第一次write时出错, writen 返回 -1 */
                return(-1);
            else
                break;                             /*若写了一些数据后出错, writen返回已经读的字节数,而不出错返回 */
        } elseif (nwritten== 0){                        
//当write返回0时,不再写下去,writen返回已经读的字节数

            break;
        }
        nleft -= nwritten;                                
//当write返回值>0但不等于n时,继续写

        ptr += nwritten;
    }
    return(n- nleft);/* return >= 0 */
}


上述版本在read write出错后,若已经读写了一些数据,则返回读写的字节量,而非出错返回。但在read write出错后没有检查errno判断是否因为被信号中断而出错。

版本2

#include    "unp.h"
ssize_t                        
readn(int fd,void *vptr,size_t n)
{
    size_t    nleft;
    ssize_t    nread;
    char    *ptr;

    ptr = vptr;
    nleft = n;
    while (nleft> 0) {
        if ((nread = read(fd, ptr, nleft))< 0) {    
//当read返回-1,

            if (errno== EINTR)
                nread = 0;                            
/*若出错是因为被信号中断,继续读*/
            else
                return(-1);                            //若不是因为被信号中断而出错,则不再读readn返回-1
        } else if (nread == 0)                        //当read返回0时,不再读下去,readn返回已经读的字节数
            break;                /* EOF */


        nleft -= nread;                                
//当read返回值>0但不等于n时,继续读

        ptr += nread;
    }
    return(n- nleft);        /* return >= 0 */
}

ssize_t                        
writen(int fd,const void *vptr, size_t n)
{
    size_t        nleft;
    ssize_t        nwritten;
    const char    *ptr;

    ptr = vptr;
    nleft = n;
    while (nleft> 0) {                                        
        if ((nwritten =write(fd, ptr, nleft))<= 0){        
//当write出错返回-1或者返回0时,

            if (nwritten< 0 &&errno == EINTR)
                nwritten = 0;                                /*若出错返回-1是因为被信号中断,继续写 */
            else
                return(-1);                                    
//若不是因为被信号中断而出错返回-1或者 返回0时,则不再写writen返回-1

        }

        nleft -= nwritten;                                    
//当write返回值>0但不等于n时,继续写

        ptr += nwritten;
    }
    return(n);
}

上述版本在read write出错后,不返回读写的字节量,而是出错返回-1。但在read write出错后检查errno判断是否因为被信号中断而出错,若是因为被信号中断,继续读写。

    在将数据写到像socket这样的文件类型时,就可以调用writen,但通常只调用read来接收来自这些设备的数据,只有当事先就知道接收数据的数据量时,才调用readn。
again:
    while ((n = read(sockfd, buf, MAXLINE))> 0)    
//调用read

        writen(sockfd, buf, n);                        
//调用writen


    if (n < 0 &&errno == EINTR)
        goto again;
    else if(n < 0)
        err_sys("str_echo: read error");
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值