既然我们已经有了系统提供的接口read和write,为什么还需要readn writen 和readline呢?
因为字节流套接字上调用read或write输入和输出的字节数可能比请求的数量少,然而这不是出错状态(在我的另一篇博文:点击此处 已经指出),为了预防万一,不让实现返回一个不足的字节计数值,unix网络编程作者就封装了以下函数
在/unpv13e/lib 下unp.h中有如下申明
ssize_t readn(int, void *, size_t);
ssize_t writen(int, const void *, size_t);
在lib/下 vim readn.c
/* include readn */
#include "unp.h"
ssize_t /* Read "n" bytes from a descriptor. */
readn(int fd, void *vptr, size_t n)
{
size_t nleft;
ssize_t nread;
char *ptr; //我们人工的加了一层缓冲区,用于存read从fd处读到的指定字节
ptr = vptr;
nleft = n;
while (nleft > 0) {
//下面操作刚接触网络编程的人可能不太理解,read默认是阻塞函数,也就是说应用进程可能会阻塞到此处(如资源数据还没准备到位),对于阻塞函数,我们需要仔细分析每一种
//返回值情况,从而一一处理,read返回值的三种状态,正整数,0,,1,以后我会专门以源码出发的角度详解)
if ( (nread = read(fd, ptr, nleft)) < 0) {
if (errno == EINTR) /*表示此调用被信号中断*/
nread = 0; /* and call read() again */
else
return(-1); /*错误发生,返回-1,文件读写位置无法预期,只能返回-1(表示错误)退出 */
} else if (nread == 0) /*读到文件尾部或者对端执行关闭操作*/
break; /* EOF */
nleft -= nread;
ptr += nread;
}
return(n - nleft); /* return >= 0 */
}
/* end readn */
/*包裹函数*/
ssize_t
Readn(int fd, void *ptr, size_t nbytes)
{
ssize_t n;
if ( (n = readn(fd, ptr, nbytes)) < 0)
err_sys("readn error");
return(n);
}
//readn函数:从一个描述符读n字节
在lib/ 下 vim writen.c
/* include writen */ #include "unp.h" ssize_t /* Write "n" bytes to a descriptor. */ 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) { if (nwritten < 0 && errno == EINTR) //被信号中断 nwritten = 0; /* and call write() again */ else return(-1); /* error ,文件读写位置无法预期*/ } nleft -= nwritten; ptr += nwritten; } return(n); } /* end writen */ /*包裹函数*/ void Writen(int fd, void *ptr, size_t nbytes) { if (writen(fd, ptr, nbytes) != nbytes) err_sys("writen error"); }