当前位置: 首页 > news >正文

腾讯后端 C++一面:recv 返回值,什么错误是可接受的?

在 C/C++网络编程中,尤其是在处理 TCP 套接字时,recv函数扮演着基石般的角色。它是从连接对端读取传入数据的主要机制。

recv 函数原型

我们先从标准的 POSIX 函数原型开始:

#include <sys/socket.h>

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

sockfd: 接收数据的套接字文件描述符。

buf: 指向用于存储接收数据的缓冲区的指针。

lenbuf缓冲区的最大长度,即本次调用最多接收的字节数。

flags: 控制接收操作的标志位(例如 MSG_PEEKMSG_WAITALL)。通常设为 0 表示标准行为。

返回值 ssize_t 类型对于理解调用的结果至关重要。

解读 recv 返回值

recv 函数的返回值主要有以下三种情况:

返回值 > 0: 成功接收数据
含义: 调用成功,并从对端接收到了数据。
: 返回值表示实际接收到并放入buf中的字节数。
注意: 这个值可能小于你请求的 len,即使发送方发送了更多的数据。这是正常现象,原因包括网络缓冲区大小、TCP 分段、以及调用时套接字接收缓冲区中实际可用的数据量等。

处理: 处理接收到的 返回值 这么多字节的数据。如果你的应用层协议期望更多的数据,你可能需要在一个循环中再次调用 recv,直到接收到完整的消息或发生错误/连接关闭。

返回值 == 0: 对端已正常关闭连接

含义: 远端对等方(peer)已经执行了有序关闭(graceful shutdown)序列(发送了 FIN 包),表示它不会再发送任何数据了。
0
注意: 这不是一个错误。这是一个信号,表明连接的读取方向已经由对端关闭。你无法再从此连接接收到任何数据。

处理: 识别到连接正在关闭。通常应停止尝试接收数据,可能需要关闭你自己的写端(如果还没关闭,使用 shutdown(sockfd, SHUT_WR)),并最终调用 close(sockfd) 来释放套接字资源。

返回值 == -1: 发生错误

含义recv 调用失败。
-1

处理: 这是错误处理的关键所在,不同的值处理方式是不一样的。

错误处理:哪些 errno 值是“可接受”的?

recv 返回 -1 时,并非所有的 errno 值都意味着连接已死或必须立即放弃。有些错误指示的是临时状态或需要特定处理逻辑的情况,而不是直接终止连接。这些就是在面试题里面说的“可接受”的错误。

“可接受” / 非致命错误: EAGAINEWOULDBLOCK: (这两个宏通常具有相同的值)

场景: 主要发生在套接字被设置为非阻塞模式 (O_NONBLOCK) 时。

含义: 当前套接字的接收缓冲区中没有数据可读,并且在不阻塞的情况下无法立即完成读取操作。这不是一个真正的错误,而是对套接字状态的一种描述。

处理: 不要关闭连接。这是非阻塞 I/O 中的预期行为。标准的处理方式是使用 I/O 多路复用机制(如 epollselectpoll)。这些机制会通知你套接字何时变为可读状态,届时你再安全地重试 recv 调用。在一个紧密循环中反复调用 recv 而不等待(忙等待)是非常低效的。

// 概念性的非阻塞循环(结合epoll/select)
while (true) {
    // 使用epoll_wait, select等等待sockfd变为可读
    // ... 等待逻辑 ...

    ssize_t bytes_received = recv(sockfd, buffer, sizeof(buffer), 0);

    if (bytes_received > 0) {
        // 处理数据...
    } else if (bytes_received == 0) {
        // 对端关闭连接
        handle_close(sockfd);
        break;
    } else { // bytes_received == -1
        if (errno == EAGAIN || errno == EWOULDBLOCK) {
            // 当前无数据可用,返回继续等待(epoll/select)
            continue;
        } else if (errno == EINTR) {
            // 被信号中断,直接重试
            continue;
        } else {
            // 发生其他错误
            perror("recv failed"); // 打印错误信息
            handle_error_close(sockfd); // 处理错误并关闭连接
            break;
        }
    }
}

EINTR:

致命 / 不可恢复错误:

这些错误通常表明连接本身、套接字状态或程序逻辑存在问题。在这些错误发生后继续在该套接字上操作通常是无意义或不可能的。

ECONNRESET: 连接被对端重置。对方发送了 RST(重置)包,很可能是因为对方异常终止或网络问题。连接已失效。

ENOTCONN: 套接字未连接(例如,在 TCP 套接字上调用 connectaccept 之前,或连接已断开后尝试 recv)。

ETIMEDOUT: 连接超时。可能在连接建立阶段发生,或在数据传输过程中由于网络状况极差或设置了 SO_RCVTIMEO 并发生较长超时而发生。通常意味着连接不可用。

ECONNREFUSED: 远程主机主动拒绝连接(更常见于 connect 调用,但在特定的 UDP recvfrom 场景下也可能遇到)。

EBADF: 无效的文件描述符 (sockfd 没有指向一个打开的套接字)。这是程序逻辑错误。

EFAULT: 传入的缓冲区指针 buf 指向了进程地址空间之外的无效内存。这是程序逻辑错误。

EINVAL: 提供了无效的参数(例如,无效的 flags)。程序逻辑错误。

ENOTSOCK: 文件描述符 sockfd 指的不是一个套接字。程序逻辑错误。

对于致命错误的处理: 记录具体的 errno 值和错误信息(使用 perrorstrerror),清理与该连接相关的资源,并调用 close(sockfd) 关闭套接字。

回答

1、区分出 recv 的三种返回值 (>0, 0, -1) 及其含义。

3、知道 -1 需要检查 errno

3、明确指出 EAGAIN/EWOULDBLOCKEINTR 是可接受的、需要特殊处理(等待/重试)的错误,尤其是在非阻塞 I/O 场景下 EAGAIN/EWOULDBLOCK 是正常情况。

对于“什么错误是可接受的?”这个问题,最核心的答案是 EAGAIN (或 EWOULDBLOCK) 和 EINTR

相关文章:

  • vue3+vite+ts使用daisyui/tailwindcss
  • 微信小程序跳2
  • 【数据结构 · 初阶】- 单链表
  • 算法训练之动态规划(三)
  • Python 实现如何电商网站滚动翻页爬取
  • 亚马逊Amazon商品详情API接口概述,json数据示例返回(测试)
  • opencv人脸性别年龄检测
  • Zotero PDF Translate 翻译插件使用OpenAI API配置教程
  • vue+flask图书知识图谱推荐系统
  • Next.js + Droplet:高并发视频内容平台部署与优化扩展实战
  • 小迪安全-tp框架反序列化,利用链,rce执行,文件删除
  • AI助手:Claude
  • 深入理解全排列算法:DFS与回溯的完美结合
  • 高级java每日一道面试题-2025年3月23日-微服务篇[Nacos篇]-如何使用Nacos进行服务发现?
  • SpringBoot企业级开发之【用户模块-更新用户基本信息】
  • OSPF不规则区域
  • ubuntu20.04在mid360部署direct_lidar_odometry(DLO)
  • HP DeskJet 1212 Printer UOS/Ubuntu下驱动安装
  • Kaggle-Housing Prices-(回归+Ridge,Lasso,Xgboost模型融合)
  • DeepSeek+新媒体运营落地实操方法
  • 网站怎么做动态背景图片/广告营销策划
  • 可以做微信公众号封面的网站/百度最新秒收录方法2022
  • 搜索引擎培训班/系统优化软件哪个最好的
  • 网站内建设的发展/查收录网站
  • 网站建设流程有哪些/合肥网站建设公司
  • 珠海高端网站设计/百度非企推广开户