readn
在Linux中,read的声明为:
ssize_t read(int fd, void *buf, size_t count);
它的返回值有以下情形:
1.大于0,代表成功读取的字节数
2.等于0,代表读取到了EOF,一般是对方关闭了socket的写端或者直接close
3.小于0,出现错误。
我们编写一个readn函数,声明与read一致,但是,readn在未出错或者fd没有关闭的情况下,会读满count个字节。
ssize_t readn(int fd, void *buf, size_t count) { size_t nleft = count; //剩余的字节数 ssize_t nread; //用作返回值 char *bufp = (char*)buf; //缓冲区的偏移量 while(nleft > 0) { nread = read(fd, bufp, nleft); if(nread == -1) { if(errno == EINTR) continue; return -1; // ERROR } else if(nread == 0) //EOF break; nleft -= nread; bufp += nread; } return (count - nleft); }
readn的返回值含义如下:
1.小于0,出错
2.等于0,对方关闭
3.大于0,但是小于count,对方关闭
4.count,代表读满count个字节
writen
write函数的声明如下:
ssize_t write(int fd, const void *buf, size_t count);
man手册中对write的返回值描述如下:
On success, the number of bytes written is returned (zero indicates nothing was writ‐
ten). On error, -1 is returned, and errno is set appropriately.
If count is zero and fd refers to a regular file, then write() may return a failure
status if one of the errors below is detected. If no errors are detected, 0 will be
returned without causing any other effect. If count is zero and fd refers to a file
other than a regular file, the results are not specified.
解释如下:
成功时,返回成功写入的字节数,否则返回-1,并设置相应的errno。
如果count为0,并且fd指向一个普通文件,那么当探测到错误时返回-1.如果没有错误发生,返回0,不会产生任何影响。
如果count为0,并且fd指向的不是普通文件,那么结果未定义。
我们不去追究write为0的情形。编写write如下:
ssize_t writen(int fd, const void *buf, size_t count) { size_t nleft = count; ssize_t nwrite; const char *bufp = (const char*)buf; while(nleft > 0) { nwrite = write(fd, bufp, nleft); if(nwrite <= 0) // ERROR { if(nwrite == -1 && errno == EINTR) continue; return -1; } nleft -= nwrite; bufp += nwrite; } return count; }
从代码中可以看出,writen要么写满count字节,要么失败。
readline
在网络编程中,很多协议是基于文本行的,例如HTTP和FTP,还有telnet,他们的消息每行都是以 作为结束标志的。于是我们开发一个readline函数,声明如下:
ssize_t readline(int sockfd, void *usrbuf, size_t maxlen)
readline函数的语义是:
如果碰不到 ,那么读取maxlen-1个字节,最后一个位置补充 。
否则读取到 ,在后面加一个 。如果中间遇到EOF,直接返回0,而不是已经读取的字节数。
我们先给出一种低效的实现:
ssize_t readline_slow(int fd, void *usrbuf, size_t maxlen) { char *bufp = usrbuf; //记录缓冲区当前位置 ssize_t nread; size_t nleft = maxlen - 1; //留一个位置给 ' ' char c; while(nleft > 0) { if((nread = read(fd, &c, 1)) < 0) { if(errno == EINTR) continue; return -1; }else if(nread == 0) // EOF { break; } //普通字符 *bufp++ = c; nleft--; if(c == ' ') break; } *bufp = '