read/write,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的中途返回)
socket上的read/write 操作不同于一般的在文件上的IO操作,socket上用read/write读写的字节数可能比要求的少,但这并不是错误,原因可能是socket的缓冲区已经达到了极限。此时所需要的就是再次调用read/write 以读出或写入剩余的字符,这种情况在socket中很常见,但在写字节流socket时只能在socket非堵塞的情况下才会出现,然而为预防返回不足的字符数值,我们总是调用writen和readn函数,而不是read和write.
于是对管道、FIFO、终端和socket等文件,我们自定义了下面的函数,直至读写了指定的n字节数据才返回。

 

#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返回已经读的字节数,而不出错返回 */
        }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 *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返回已经读的字节数,而不出错返回 */
        }else if(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(intfd,void *vptr,size_tn){
    size_tnleft;
    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(intfd,const void *vptr,size_tn){
    size_t nleft;
    ssize_t nwritten;
    constchar*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)
gotoagain;
elseif(n<0)
err_sys("str_echo: read error");
}

你可能感兴趣的:(read/write,readn/writen)