既然我们已经有了系统提供的接口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,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,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,size_t nbytes) { if (writen(fd,nbytes) != nbytes) err_sys("writen error"); }