• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
sn-genuine
博客园    首页    新随笔    联系   管理    订阅  订阅

TCP 缓存与 Nagle 算法

TCP 缓存与 Nagle 算法 笔记


一、TCP 缓存机制

1. 核心概念

系统为每个 TCP socket 维护两块缓冲区:

  • 发送缓冲区:应用进程调用 send()/write() 时,数据会先被拷贝到内核的发送缓冲区,而非直接发送到网络。
  • 接收缓冲区:应用进程调用 recv()/read() 时,数据从内核的接收缓冲区拷贝到用户进程。

2. 工作流程

  • 发送端:应用进程 → 发送缓冲区 → 网络 → 接收缓冲区 → 接收端应用进程
  • 关键:发送数据的本质是“写入发送缓冲区”,接收数据的本质是“读取接收缓冲区”。

3. 关键问题解析

  1. send() 函数会阻塞吗?
    • 当发送缓冲区已满,且对端的接收缓冲区也已满时,send() 会阻塞,直到缓冲区有空间或连接断开。
  2. 写入数据后关闭 socket,对端还能收到数据吗?
    • 可以。关闭 socket 时,内核会确保发送缓冲区中的数据全部发送并确认后,才会断开连接。

4. 查看 socket 缓存大小

#include <sys/socket.h>
#include <iostream>
using namespace std;

int main() {
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    int bufsize = 0;
    socklen_t optlen = sizeof(bufsize);

    // 获取发送缓冲区大小
    getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &bufsize, &optlen);
    cout << "send bufsize = " << bufsize << endl;

    // 获取接收缓冲区大小
    getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &bufsize, &optlen);
    cout << "recv bufsize = " << bufsize << endl;

    close(sockfd);
    return 0;
}

二、Nagle 算法

1. 设计目的

TCP 协议为了减少网络中小数据包的数量,提升带宽利用率,引入了 Nagle 算法。它的核心目标是:尽量发送大块数据,避免网络中充斥大量小数据包。

2. 算法定义

任意时刻,最多只能有一个未被确认的小段(小段指小于 MSS 的数据块)。也就是说:

  • 当有一个小数据块已发送但未收到 ACK 时,后续的小数据块会被暂存,直到收到 ACK 或数据累积到 MSS 大小后再发送。

3. 典型延迟场景(Nagle + ACK 延迟)

  • Nagle 算法:发送端必须等待上一个包的 ACK 才能发送下一个小包。
  • ACK 延迟机制:接收端收到数据后,会延迟约 40ms 再回复 ACK,希望能捎带在后续数据中一起发送。
  • 问题:两者叠加时,小包的发送会延迟约 40ms,对实时性要求高的场景(如游戏、即时通讯)影响极大。

4. 禁用 Nagle 算法

在对延迟敏感的场景中,可通过设置 TCP_NODELAY 选项禁用 Nagle 算法:

#include <netinet/tcp.h>  // 必须包含此头文件
#include <sys/socket.h>

int main() {
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    int opt = 1;
    // 禁用 Nagle 算法
    setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
    // 后续 bind/connect/listen/accept...
    return 0;
}

三、核心对比与应用场景

场景 是否启用 Nagle 算法 原因
文件传输、批量数据 启用 减少小包数量,提升带宽利用率
联机游戏、即时通讯 禁用(TCP_NODELAY) 避免 40ms 延迟,保证实时性

四、关键代码速记

1. 获取 socket 缓冲区大小

int bufsize = 0;
socklen_t optlen = sizeof(bufsize);
getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &bufsize, &optlen);
getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &bufsize, &optlen);

2. 禁用 Nagle 算法

int opt = 1;
setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
posted on 2026-05-17 15:30  SobNov  阅读(20)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3