read()返回值即实际读取字节数:>0表示成功读取对应字节数,=0表示文件尾或对端关闭,=-1表示出错需查errno(如eintr、eagain)。

read() 返回值就是实际读取字节总数
调用 read() 后,返回值直接告诉你从文件描述符(或 socket)里真正拿到多少字节——它可能小于你请求的长度,这是正常行为,不是错误。比如你传入 read(fd, buf, 1024),返回 327,就表示只读了 327 字节。
常见误判是把返回值当“成功/失败”布尔值用,其实:
- 返回值 > 0:成功读取,数值即字节数
- 返回值 == 0:对普通文件意味着 EOF;对 socket 可能是对方关闭连接(需结合
shutdown()状态判断) - 返回值 == -1:出错,此时要检查
errno(如EAGAIN、EINTR)
std::ifstream::read() 不会自动告诉你读了多少
std::ifstream::read() 本身不返回字节数,它只修改流状态。必须配合 gcount() 才能获取本次 read() 实际读取量。
容易漏掉的是:如果流处于 failbit 或 badbit 状态(比如文件不可读、磁盘满),gcount() 仍可能返回上次成功读取的残留值,所以得先确认状态:
std::ifstream fin("data.bin", std::ios::binary);
char buf[512];
fin.read(buf, sizeof(buf));
if (fin.good() || fin.eof()) {
std::streamsize n = fin.gcount(); // 这才是真实读取数
}
注意:fin.eof() 单独不能作为“已读完”的依据——它只在尝试读越界后才置位。
recv() 和 recvfrom() 的返回值语义一致
在网络编程中,recv() 和 recvfrom() 的返回值规则和 read() 完全一样:返回实际接收的字节数,0 表示对端关闭,-1 表示错误(查 errno)。
关键点在于:TCP 是字节流,没有消息边界,一次 recv() 可能只收半个应用层包;UDP 虽然有边界,但若缓冲区太小,recv() 会截断并设置 MSG_TRUNC(需显式启用 MSG_TRUNC 标志才能感知)。
- 不加
MSG_TRUNC:截断时只返回缓冲区大小,丢数据且无提示 - 加
MSG_TRUNC:返回实际报文长度(哪怕大于缓冲区),可据此 realloc + 重 recv
容易被忽略的细节:信号中断与非阻塞 I/O
在非阻塞 fd 上调用 read() 或 recv(),遇到无数据时会立即返回 -1 并设 errno = EAGAIN 或 EWOULDBLOCK——这不是错误,是预期行为。同样,被信号中断时返回 -1 且 errno = EINTR,需要重试。
所以健壮写法不是简单判断 == -1 就报错,而是:
ssize_t n;
do {
n = read(fd, buf, len);
} while (n == -1 && (errno == EINTR || errno == EAGAIN));
if (n == -1) {
// 真正的错误,比如 EBADF、EFAULT
}
漏掉 EINTR 重试会导致程序在收到 SIGCHLD 等信号后意外终止读取;漏掉 EAGAIN 则会让非阻塞逻辑卡死。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











