三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

TCP 缓存与 Nagle 算法

TCP 缓存与 Nagle 算法

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));
← 返回列表