下面是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"); } |