read函数返回立即返回0

本文通过一个简单的C++示例程序,演示了当客户端断开TCP连接后,服务端进行read操作会立即返回0的现象,并解释了其背后的原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

              ❤️强烈推荐人工智能学习网站❤️

                     最近在测试一个网络程序的时候,每次调用read函数返回立即返回0,read是阻塞函数,没有读到数据竟然立即返回0,有些奇怪。想了一下,和tcp的四次挥手有关,看代码。

服务端:

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <malloc.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <stdarg.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#define MAXLINE 4096



int main()
{
   int listenfd,acceptfd,n;
   socklen_t  clilen;
   char recvbuf[30]={0};
   struct sockaddr_in cliaddr,servaddr;

   listenfd=socket(AF_INET,SOCK_STREAM,0);
   servaddr.sin_family=AF_INET;
   servaddr.sin_port=htons(8888);
   servaddr.sin_addr.s_addr = INADDR_ANY; 

   bind(listenfd,(struct sockaddr *)&servaddr,sizeof(struct sockaddr_in));
   listen(listenfd,5);

   clilen=sizeof(cliaddr);
   acceptfd=accept(listenfd,(struct sockaddr *)&cliaddr,&clilen);

   while(1)
   {
        sleep(1);
        printf("test\n");
        memset(recvbuf,0,sizeof(recvbuf));
   	n=recv(acceptfd,recvbuf,sizeof(recvbuf)-1,0);
   	printf("recvbuf=%s,n=%d\n",recvbuf,n);
   }

   getchar();
   close(listenfd);
   return 0;
}

客户端:

 

 

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <malloc.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <stdarg.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <netinet/tcp.h>
#define MAXLINE 4096


int main()
{
   int sockfd;
   struct sockaddr_in servaddr;
   char sendbuf[66495]="111111111";

   sockfd=socket(AF_INET,SOCK_STREAM,0);
   bzero(&servaddr,sizeof(servaddr));
   servaddr.sin_family=AF_INET;
   servaddr.sin_port=htons(8888);
   servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");


   int ret=connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
   printf("ret=%d\n",ret);


   getchar();
   close(sockfd);
   return 0;
}

 

makefile:

 

all:server client

server.o:server.cpp
	g++ -c server.cpp
client.o:client.cpp
	g++ -c client.cpp
server:server.o
	g++ -o server server.o
client:client.o
	g++ -o client client.o

clean:
	rm -f server client *.o

先启动服务端,再启动客户端,然后再关掉客户端,看打印。

 

 

[mapan@localhost test]$ 
[mapan@localhost test]$ ./server 
test
recvbuf=,n=0
test
recvbuf=,n=0
test
recvbuf=,n=0
test
recvbuf=,n=0
test
recvbuf=,n=0
test
recvbuf=,n=0
test
recvbuf=,n=0
test
recvbuf=,n=0
test
recvbuf=,n=0
test
recvbuf=,n=0
test
recvbuf=,n=0

 

 

服务端的read函数理解返回,且返回值为0。其原因是,客户端关掉进程后发送FIN到服务端,然后系统给read函数发信号,告知read函数无数据,于是read函数马上返回。随后服务端每次调用read函数时都会收到系统发的这个信号,所以立即返回0。
 


 

 

 

 

 

 

 

<think>我们正在讨论read函数返回值,特别是返回0的含义。根据用户提供的引用,我们可以从多个角度分析: 引用[3]中的C语言示例明确说明: - 当read返回值>0时,表示成功读取数据 - 当返回值==0时,表示到达文件末尾(EOF) - 当返回值<0时,表示出现错误 引用[4]中关于串口读取的示例也提到: - n>0:读取到数据 - n==0:没有数据可读(在非阻塞模式下可能不会返回0,而是返回-1并设置EAGAIN) - n<0:出错 因此,read函数返回0通常表示已经到达文件末尾(End Of File,EOF),即没有更多数据可读。 但是,需要注意的是,对于某些特殊文件(如管道、套接字等),返回0可能还有另外的含义: - 对于管道(pipe)或FIFO,当所有写端关闭后,读取端调用read返回0(表示不会再有任何数据到来)。 - 对于套接字(socket),当对端关闭连接(发送了FIN包)后,本地读取会返回0(表示连接已正常关闭)。 所以,read返回0的通用含义是:当前没有数据可读,并且后续也不会再有新数据(因为已经到达了流的末尾)。 下面我们详细解释: ### 1. 文件读取 当从普通文件(如file.txt)读取时,如果当前读取位置已经到达文件末尾,那么read调用会返回0。例如,文件有100字节,我们一次读取100字节后,下一次读取就会返回0。 ### 2. 管道和FIFO 在管道中,如果所有写端文件描述符都被关闭,那么从读端读取时,当管道中已有的数据被读完后,再调用read返回0(表示不会再有任何数据写入)。 ### 3. 套接字 在TCP套接字中,当对端关闭连接(调用close或shutdown(SHUT_WR))后,本端调用read返回0,表示对端已经关闭了连接(发送了FIN包)。这是TCP连接正常关闭的一部分。 ### 4. 终端设备 从终端设备读取时,通常不会返回0,除非终端被关闭(例如,关闭了终端窗口)。但在实际中,终端设备通常不会返回0,因为终端是交互式的。 ### 5. 非阻塞模式下的read 在非阻塞模式下,如果当前没有数据可读,read立即返回-1,并将errno设置为EAGAIN或EWOULDBLOCK(引用[4])。因此,在非阻塞模式下,read返回0的情况仍然只出现在遇到EOF(如文件末尾、连接关闭等)时。 ### 总结 read函数返回0表示: 1. 对于文件:已经到达文件末尾(EOF)。 2. 对于管道、套接字等:对端已经关闭,并且当前没有剩余数据可读。 因此,在编程中,当read返回0时,我们应该做相应的清理工作(比如关闭文件描述符,或处理连接关闭后的状态)。 ### 示例代码分析 参考引用[3]中的代码: ```c ssize_t ret = read(fd, buf, sizeof(buf)); if (ret > 0) { // 成功读取数据 } else if (ret == 0) { // 到达文件末尾(EOF) printf("End of file\n"); } else { // 错误处理 } ``` ### 注意事项 - 在非阻塞模式下,read返回0的情况仍然表示EOF,而不是表示“暂时没有数据”。暂时没有数据的情况会通过返回-1并设置errno为EAGAIN/EWOULDBLOCK来表示(引用[4])。 ### 相关问题 1. 在非阻塞模式下,read返回0返回-1(errno为EAGAIN)有什么区别? 2. 当read返回0时,是否意味着文件描述符可以立即关闭? 3. 在套接字编程中,read返回0后,是否还需要调用shutdown或close?</think>### read函数返回值为0的含义 在系统编程中,`read`函数返回0表示**已到达文件末尾(End of File,EOF)** 或**对端关闭了连接**(针对管道/套接字等)。这是所有类UNIX系统中的标准行为,具体解释如下: #### 1. **文件读取场景** - 当读取普通文件(如文本文件)时: - 返回0表示**已到达文件末尾**,没有更多数据可读 - 示例: ```c int fd = open("data.txt", O_RDONLY); char buf[256]; ssize_t n = read(fd, buf, sizeof(buf)); // n=0 表示文件已读完 ``` - 典型处理逻辑(引用[3]): ```c if (ret == 0) { // 到达文件末尾(EOF) printf("End of file\n"); } ``` #### 2. **管道/套接字场景** - 当读取管道、套接字或串口时: - 返回0表示**对端已关闭连接** - 例如TCP连接中,对端调用`close()`后,本端`read()`会返回0 - 串口场景的特殊说明(引用[4]): > "在阻塞模式下,read可能会一直等待直到有数据到达或出错;在非阻塞模式下,read立即返回,如果没有数据则返回-1并设置errno为EAGAIN" #### 3. **与其他返回值的对比** | 返回值 | 含义 | 典型场景 | |--------|-------------------------------|------------------------------| | >0 | 成功读取的字节数 | 数据正常到达 | | 0 | EOF或对端关闭 | 文件结束/TCP连接关闭 | | -1 | 错误发生 | 需检查`errno`具体原因 | | -1(EAGAIN) | 非阻塞模式下无数据 | 非阻塞I/O需重试 | #### 4. **编程注意事项** 1. **阻塞模式**:返回0是正常结束信号,应关闭文件描述符 ```c if (n == 0) { close(fd); // 资源清理 } ``` 2. **非阻塞模式**:返回0仍表示EOF,与`EAGAIN`不同 - `EAGAIN`(-1)表示暂时无数据,稍后需重试 - 返回0是永久性结束,无需重试 3. **错误处理**:必须区分0和负返回值 ```c if (n < 0) { if (errno == EAGAIN) { /* 非阻塞无数据 */ } else { perror("read error"); } } else if (n == 0) { /* 处理连接关闭/EOF */ } ``` ### 总结 - ✅ **返回0 = 数据流正常终止**:文件结束或连接关闭 - ❌ **不是错误状态**:不同于返回负值的错误情况 - 🔄 **与阻塞模式无关**:在阻塞/非阻塞模式下语义相同 - ⚠️ **必须显式处理**:忽略此返回值可能导致资源泄漏或逻辑错误 [^3][^4]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

盼盼编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值