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

ngx_unix_recv

1 定义

ngx_unix_recv 函数 定义在 ./nginx-1.24.0/src/os/unix/ngx_recv.c
ssize_tngx_unix_recv(ngx_connection_t*c,u_char*buf,size_tsize){ssize_tn;ngx_err_terr;ngx_event_t*rev;rev=c->read;#if(NGX_HAVE_KQUEUE)if(ngx_event_flags&NGX_USE_KQUEUE_EVENT){ngx_log_debug3(NGX_LOG_DEBUG_EVENT,c->log,0,"recv: eof:%d, avail:%d, err:%d",rev->pending_eof,rev->available,rev->kq_errno);if(rev->available==0){if(rev->pending_eof){rev->ready=0;rev->eof=1;if(rev->kq_errno){rev->error=1;ngx_set_socket_errno(rev->kq_errno);returnngx_connection_error(c,rev->kq_errno,"kevent() reported about an closed connection");}return0;}else{rev->ready=0;returnNGX_AGAIN;}}}#endif#if(NGX_HAVE_EPOLLRDHUP)if((ngx_event_flags&NGX_USE_EPOLL_EVENT)&&ngx_use_epoll_rdhup){ngx_log_debug2(NGX_LOG_DEBUG_EVENT,c->log,0,"recv: eof:%d, avail:%d",rev->pending_eof,rev->available);if(rev->available==0&&!rev->pending_eof){rev->ready=0;returnNGX_AGAIN;}}#endifdo{n=recv(c->fd,buf,size,0);ngx_log_debug3(NGX_LOG_DEBUG_EVENT,c->log,0,"recv: fd:%d %z of %uz",c->fd,n,size);if(n==0){rev->ready=0;rev->eof=1;#if(NGX_HAVE_KQUEUE)/* * on FreeBSD recv() may return 0 on closed socket * even if kqueue reported about available data */if(ngx_event_flags&NGX_USE_KQUEUE_EVENT){rev->available=0;}#endifreturn0;}if(n>0){#if(NGX_HAVE_KQUEUE)if(ngx_event_flags&NGX_USE_KQUEUE_EVENT){rev->available-=n;/* * rev->available may be negative here because some additional * bytes may be received between kevent() and recv() */if(rev->available<=0){if(!rev->pending_eof){rev->ready=0;}rev->available=0;}returnn;}#endif#if(NGX_HAVE_FIONREAD)if(rev->available>=0){rev->available-=n;/* * negative rev->available means some additional bytes * were received between kernel notification and recv(), * and therefore ev->ready can be safely reset even for * edge-triggered event methods */if(rev->available<0){rev->available=0;rev->ready=0;}ngx_log_debug1(NGX_LOG_DEBUG_EVENT,c->log,0,"recv: avail:%d",rev->available);}elseif((size_t)n==size){if(ngx_socket_nread(c->fd,&rev->available)==-1){n=ngx_connection_error(c,ngx_socket_errno,ngx_socket_nread_n" failed");break;}ngx_log_debug1(NGX_LOG_DEBUG_EVENT,c->log,0,"recv: avail:%d",rev->available);}#endif#if(NGX_HAVE_EPOLLRDHUP)if((ngx_event_flags&NGX_USE_EPOLL_EVENT)&&ngx_use_epoll_rdhup){if((size_t)n<size){if(!rev->pending_eof){rev->ready=0;}rev->available=0;}returnn;}#endifif((size_t)n<size&&!(ngx_event_flags&NGX_USE_GREEDY_EVENT)){rev->ready=0;}returnn;}err=ngx_socket_errno;if(err==NGX_EAGAIN||err==NGX_EINTR){ngx_log_debug0(NGX_LOG_DEBUG_EVENT,c->log,err,"recv() not ready");n=NGX_AGAIN;}else{n=ngx_connection_error(c,err,"recv() failed");break;}}while(err==NGX_EINTR);rev->ready=0;if(n==NGX_ERROR){rev->error=1;}returnn;}
ngx_unix_recv 函数 是 Nginx 在 Unix/Linux 系统上从套接字接收数据的核心底层函数。 它封装了 `recv()` 系统调用, 并根据当前使用的 I/O 多路复用机制及触发模式(边缘/水平), 动态管理读事件的 `ready`、`available`(可读字节数)、`eof` 和 `error` 等状态。 同时,该函数处理信号中断(EINTR)重试、无数据可读(EAGAIN) 返回 `NGX_AGAIN`、正常关闭返回 0, 以及异常错误返回 `NGX_ERROR`, 为上层事件驱动模型提供一致且高效的数据接收接口。

2 详解

1 函数签名

ssize_tngx_unix_recv(ngx_connection_t*c,u_char*buf,size_tsize)
返回值 > 0:成功读取的字节数,可能小于等于 size。 0:对端已关闭连接(EOF),此时函数会将读事件的 eof 标志置为 1。 负值:表示特殊情况或错误,具体由 Nginx 定义的宏控制: NGX_AGAIN(通常为 -2): 表示非阻塞套接字当前无数据可读,或事件尚未就绪,调用者应将读事件重新加入事件循环等待通知。 NGX_ERROR(通常为 -1): 表示发生不可恢复的错误,函数已处理连接错误并设置事件错误标志。
参数1 ngx_connection_t *c 指向一个连接对象 c->fd:套接字文件描述符,是 recv() 操作的目标 参数2 u_char *buf 接收数据的存储区,由调用者预先分配,大小至少为 size 字节 参数3 size_t size 指定了接收缓冲区的容量,即本次调用最多能读取的字节数

2 逻辑流程

1 局部变量 2 Kqueue 处理 3 无需调用 recv 4 接收循环 5 循环后统一状态更新与返回

1 局部变量
{ssize_tn;ngx_err_terr;ngx_event_t*rev;rev=c->read;
初始化 rev:获取连接 c 的读事件对象

2 Kqueue 处理
#if(NGX_HAVE_KQUEUE)if(ngx_event_flags&NGX_USE_KQUEUE_EVENT){ngx_log_debug3(NGX_LOG_DEBUG_EVENT,c->log,0,"recv: eof:%d, avail:%d, err:%d",rev->pending_eof,rev->available,rev->kq_errno);if(rev->available==0){if(rev->pending_eof){rev->ready=0;rev->eof=1;if(rev->kq_errno){rev->error=1;ngx_set_socket_errno(rev->kq_errno);returnngx_connection_error(c,rev->kq_errno,"kevent() reported about an closed connection");}return0;}else{rev->ready=0;returnNGX_AGAIN;}}}#endif

3 无需调用 recv
#if(NGX_HAVE_EPOLLRDHUP)if((ngx_event_flags&NGX_USE_EPOLL_EVENT)&&ngx_use_epoll_rdhup){ngx_log_debug2(NGX_LOG_DEBUG_EVENT,c->log,0,"recv: eof:%d, avail:%d",rev->pending_eof,rev->available);if(rev->available==0&&!rev->pending_eof){rev->ready=0;returnNGX_AGAIN;}}#endif
#1 当前事件模块是 epoll 配置启用了 epoll_rdhup 允许使用 EPOLLRDHUP 检测对端半关闭。 #2 调试日志 #3 判断是否需要真正调用 recv(): 若 epoll 指示没有数据可读(available == 0) 且并未因对端关闭而触发(!pending_eof), 则直接避免无意义的 recv() 调用。 返回 NGX_AGAIN:清除 ready 标志,重新等待事件。 这是边缘触发模式下的重要优化,可以减少系统调用开销。

4 接收循环
do{n=recv(c->fd,buf,size,0);ngx_log_debug3(NGX_LOG_DEBUG_EVENT,c->log,0,"recv: fd:%d %z of %uz",c->fd,n,size);if(n==0){rev->ready=0;rev->eof=1;#if(NGX_HAVE_KQUEUE)/* * on FreeBSD recv() may return 0 on closed socket * even if kqueue reported about available data */if(ngx_event_flags&NGX_USE_KQUEUE_EVENT){rev->available=0;}#endifreturn0;}if(n>0){#if(NGX_HAVE_KQUEUE)if(ngx_event_flags&NGX_USE_KQUEUE_EVENT){rev->available-=n;/* * rev->available may be negative here because some additional * bytes may be received between kevent() and recv() */if(rev->available<=0){if(!rev->pending_eof){rev->ready=0;}rev->available=0;}returnn;}#endif#if(NGX_HAVE_FIONREAD)if(rev->available>=0){rev->available-=n;/* * negative rev->available means some additional bytes * were received between kernel notification and recv(), * and therefore ev->ready can be safely reset even for * edge-triggered event methods */if(rev->available<0){rev->available=0;rev->ready=0;}ngx_log_debug1(NGX_LOG_DEBUG_EVENT,c->log,0,"recv: avail:%d",rev->available);}elseif((size_t)n==size){if(ngx_socket_nread(c->fd,&rev->available)==-1){n=ngx_connection_error(c,ngx_socket_errno,ngx_socket_nread_n" failed");break;}ngx_log_debug1(NGX_LOG_DEBUG_EVENT,c->log,0,"recv: avail:%d",rev->available);}#endif#if(NGX_HAVE_EPOLLRDHUP)if((ngx_event_flags&NGX_USE_EPOLL_EVENT)&&ngx_use_epoll_rdhup){if((size_t)n<size){if(!rev->pending_eof){rev->ready=0;}rev->available=0;}returnn;}#endifif((size_t)n<size&&!(ngx_event_flags&NGX_USE_GREEDY_EVENT)){rev->ready=0;}returnn;}err=ngx_socket_errno;if(err==NGX_EAGAIN||err==NGX_EINTR){ngx_log_debug0(NGX_LOG_DEBUG_EVENT,c->log,err,"recv() not ready");n=NGX_AGAIN;}else{n=ngx_connection_error(c,err,"recv() failed");break;}}while(err==NGX_EINTR);
#1 系统调用 recv: 从连接套接字 c->fd 读取数据到 buf, 最多 size 字节,flags 为 0(无特殊标志)。 返回值存入 n。
#2-1 情况一:n == 0 —— 对端关闭连接 检查返回值是否 0: recv() 返回 0 表示对端已执行了关闭写入(发送了 FIN),TCP 连接半关闭或完全关闭。 更新事件状态:清除 ready 置 eof = 1,保证上层逻辑能正确处理 EOF。
#2-2 Kqueue 特化清理:kqueue 模型下, 可能还存在残留的 available 值,这里强行归零,避免状态错乱。
#2-3 返回 0:通知上层连接已关闭。
#3-1 情况二:n > 0 —— 成功读取数据
#3-2 Kqueue 机制下的 available 更新
#3-3 条件编译:支持 FIONREAD ioctl(如 Linux、FreeBSD 等), 可以查询套接字接收缓冲区中待读字节数。 如果已知 available: 进入基于该值的递减逻辑。 递减可用字节数 处理负值情况: 若 available 变成负数,说明有额外数据到达, 因此清 available 并置 ready = 0(边缘触发下表示本次通知数据已被取完)。 记录剩余量:便于调试。
available 未知(初始为 -1)且本次读满了: 说明缓冲区可能还有更多数据,但之前没有获取 available, 现在需要主动探测。 通过 ioctl 查询: 调用 ngx_socket_nread 获取套接字接收缓冲区未读字节数存入 rev->available。 如果 ioctl 失败,调用 ngx_connection_error 记录错误,设置 n = NGX_ERROR, 然后 break 跳出循环,最终由尾部统一处理返回。
#3-4 EPOLLRDHUP 模式下的就绪状态管理 epoll + EPOLLRDHUP 环境下 判断本次读取是否未填满缓冲区: 若 n < size,通常表示内核缓冲区已被读空(已完成本次事件通知的所有数据)。 更新状态: 若没有挂起的 EOF,则清除 ready(边缘触发下已无数据可读,不再就绪)。 将 available 强制置 0,表示无剩余数据。 返回实际读取字节数, 跳过后续通用的 ready 调整(因为 epoll 路径已完成专门处理)。
#3-5 基于 GREEDY_EVENT 的就绪处理 (size_t) n < size: 读取量小于请求量,暗示内核缓冲区已空。 !(ngx_event_flags & NGX_USE_GREEDY_EVENT): 当前事件模块不是“贪心”模式。 在 Nginx 中,水平触发(如 select)通常也设为非贪心, 表示单次事件通知只读取一次(而不在循环中直到 EAGAIN), 因此这里清除 ready,让事件循环下次再次通知。 返回实际读取字节数
#4 情况三:n < 0 —— recv() 返回错误 获取错误码
#4-1 判断是否为可恢复错误: NGX_EAGAIN:非阻塞套接字无数据可读(等同于 EWOULDBLOCK)。 NGX_EINTR:系统调用被信号中断。 设置返回值为 NGX_AGAIN:告诉上层需要再次等待事件 while (err == NGX_EINTR) 会判断: 如果是 EINTR,err == NGX_EINTR 成立,循环会回到 do 开头重新执行 recv()。 如果是 EAGAIN,err == NGX_EINTR 不成立,循环退出,保留 n = NGX_AGAIN 作为最终返回值。
#4-2 不可恢复的其他错误 ngx_connection_error 会记录错误日志并返回 NGX_ERROR。 n 被赋值为 NGX_ERROR(内部通常为 -1)。 break 退出 do-while 循环。

循环条件:仅当错误是 EINTR(信号中断)时继续循环,重新调用 recv()。 对于 EAGAIN 或已通过 break 退出的错误,循环终止。

5 循环后统一状态更新与返回
rev->ready=0;if(n==NGX_ERROR){rev->error=1;}returnn;}
#! 循环后统一状态更新与返回 清除 ready 标志:无论因 EAGAIN 退出还是出现错误, 此时套接字已无数据可读,必须置零,防止事件循环误认为仍然就绪。
#2 标记错误状态: 若最终返回值为 NGX_ERROR(不可恢复错误), 在读事件上设置 error = 1,以便上层销毁连接或进行清理。
#3 返回最终结果: n > 0:成功读取的字节数。 0:正常 EOF(在前面的 if (n == 0) 分支已提前返回,不会执行到这里,但逻辑完备)。 NGX_AGAIN:暂时无数据,需重新等待。 NGX_ERROR:连接发生致命错误。
http://www.jsqmd.com/news/790697/

相关文章:

  • 2026年5月最新劳力士官方售后网点核验报告(含迁址新开)实地考察・多方验证 - 亨得利官方服务中心
  • 独立开发者如何通过Taotoken Token Plan有效控制月度AI支出
  • agent-skills:给 AI 编程 Agent 装上高级工程师的工程能力
  • 如何在Taotoken模型广场下载模型列表并完成选型与测试
  • KeyboardChatterBlocker:Windows键盘连击问题的终极免费开源解决方案
  • 2026年南京婚纱摄影哪家好?基于平台真实评价数据的机构口碑测评 - charlieruizvin
  • 郑州婚纱照外景地怎么选?2026四季外景攻略+机构推荐 - 江湖评测
  • 微信聊天记录永久保存完整指南:3步掌握数据自主权
  • 零代码AI翻唱制作指南:用AICoverGen让任何声音唱任何歌
  • 如何高效使用VideoDownloadHelper:3分钟免费安装Chrome视频下载扩展完整指南
  • 2026 年 NC 程序管理软件选型:为何优选南京万化智造科技有限公司(Concreate) - 小艾信息发布
  • 【紧急更新】大会主入口周边道路封闭预案(8月15日起执行),3套替代路线已通过交管局备案
  • 2026汕头必喝奶茶店:这3杯本地人私藏最好喝 - 速递信息
  • 别再死磕官方例程了!用STM32CubeMX+DWM1000实现TWR测距,我踩过的坑都帮你填好了
  • CARAMEL架构:嵌入式系统控制流审计的硬件优化方案
  • 如何永久保存微信聊天记录?WeChatMsg本地化解决方案完整指南
  • Adobe-GenP 3.0:免费解锁Adobe全家桶完整功能的5步终极指南
  • Noto Emoji表情库:3个技巧解决跨平台表情显示难题
  • 【独家解密】2026奇点大会闭门方法论:AI原生开发流程重构的4层架构、12项SLA指标与实时可观测性框架
  • 从PASCAL VOC到Cityscapes:手把手教你用PyTorch复现经典分割模型并跑通自己的数据集
  • LibreDWG:打破AutoCAD垄断的开源CAD文件处理利器
  • 2026济南婚纱照排名大揭秘!权威报告综合评价热门工作室哪家专业 - charlieruizvin
  • 【AI原生API设计黄金法则】:2026奇点大会官方认证的7大不可妥协规范(附Gartner验证数据)
  • 基于MCP协议与FCM构建AI助手移动推送通知系统
  • AI知识资产如何不流失、不沉睡、不冲突?SITS2026标准落地的5步实战法(附企业级Checklist)
  • 从零构建学术信息抓取工具:模块化设计与Python实现
  • 模型即代码,流水线即推理:为什么你的CI/CD在2026年已成技术负债?
  • 郑州婚纱照怎么选不踩坑?2026最新排名+真实避坑指南 - charlieruizvin
  • 如何用DyberPet桌面宠物框架打造你的专属数字伙伴?终极完整指南
  • vSphere UI健康状态告警:从内存激增到服务调优的实战解析