前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Nagle 算法与滑动窗口协议

Nagle 算法与滑动窗口协议

作者头像
用户3147702
发布2022-06-27 12:45:15
1.1K0
发布2022-06-27 12:45:15
举报
文章被收录于专栏:小脑斧科技博客

1. 概述

此前的文章中,我们介绍了 tcp 协议的基本概念和连接的建立与终止 最后,我们介绍了“经受时延的确认”,这是一种将 ACK 包与下一条数据包合并发送的策略,这样可以尽量减少发往网络的报文,以提高传输的效率,节省网络资源。 除此之外,TCP 还有很多其他算法和策略用来优化网络的使用。

2. Nagle 算法

2.1. 概述

Nagle 算法是一种减少 TCP/IP 网络拥塞控制的算法,主要用来解决小包问题。 即使只发送一个字符,传输上也需要 41 字节的包,造成了 4000%,大量小包会导致网络过载,大量小包在负载重的环境下会导致包丢失,妨碍传输速度,导致吞吐量下降。 Nagle 算法在确认数据发送时把数据放入缓存中,直到上一条数据被确认才会发送新的数据。 Nagle 算法保证一个 TCP 连接上最多只有一个未被确认的未完成小分组,在该分组被确认前不能发送其他小分组。

2.2. 算法规则

1. 如果包长度达到 MSS(最长报文大小),则允许发送; 2. 如果该包含有FIN,则允许发送; 3. 设置了 TCP_NODELAY 选项,则允许发送; 4. 未设置 TCP_CORK 选项时,若所有发出去的小数据包(包长度小于MSS)均被确认,则允许发送; 5. 上述条件都未满足,但发生了超时(一般为200ms),则立即发送

3. 滑动窗口协议

3.1. 基本介绍

滑动窗口协议是一种常用的 TCP 流量控制方法,他允许发送方在停止并等待确认前可以连续发送多个分组。 由于发送方不再需要在每个分组发送后都停下来等待 ACK,而是通过发送多个分组后再进行等待统一的 ACK,这样加速了数据的传输。 在滑动窗口协议中,一个 ACK 可以确认若干个分组,ACK 包的确认分组号参数表示到该分组号-1 为止的所有分组都确认收到。

3.2. 窗口大小通告

在 TCP 报文中,我们看到有一个窗口大小选项: 传输控制协议 — TCP 它用来通知对方下一次确认前最多能够接收的字节数,如果通告窗口大小为 0 (零窗口)或小于下一次发送报文的字节数,则发送方必须停止发送,等待确认。 在下一次确认前,接收方也可以发送通过“窗口更新”报文来更新窗口的大小,以使发送方可以在接收到下一次 ACK 前可以再次发送报文。

下图展示了 TCP 滑动窗口协议:

每当报文被确认,窗口都会向右移动,因此而被形象的称为“滑动窗口”。 有三个术语被用来描述窗口的变化:

  • 合拢 — 当数据被发送和确认时,窗口左边沿向右移动
  • 张开 — 当接收端发送“窗口更新”报文增加窗口大小时,窗口右边沿向右移动增加窗口大小
  • 收缩 — 这是被强烈建议不要使用的方式,右边沿向左移动减小窗口大小

4. linux TCP 参数调优

对于 FTP 等成块数据流发送的 TCP 应用程序来说,窗口大小的变动对程序性能的影响非常明显,当然,如果缓冲区设置太大则会造成内存资源不必要的浪费,恰到好处的大小是刚好可以利用带宽。 “时延带宽积”(Bandwidth Delay Product)表示链接带宽(报文在网络上传输的速率)和往返时间(RTT)的积: BDP = link_bandwidth RTT。 也就是说,如果一个应用程序是通过 100Mbps 的局域网进行通信,其 RTT 为 50ms,那么: BDP = 100Mbps/8 0.05sec = 0.625MB。 我们可以将 TCP 窗口设置为 BDP 或 2*BDP。 linux 2.6 默认窗口大小是 110KB,通过计算,可以知道它限制了带宽为 2.2MBps,也就限制了吞吐量,可见我们主动设置 TCP 窗口大小的必要性。 在 linux 中,通过修改下列配置文件 TCP/IP 参数可以实现自动配置 TCP/IP 参数的功能:

linux 可调节 tcp 参数设置

可调节的参数

默认值

选项说明

/proc/sys/net/core/rmem_default

110592

定义默认的接收窗口大小;对于更大的 BDP 来说,这个大小也应该更大

/proc/sys/net/core/rmem_max

110592

定义接收窗口的最大大小;对于更大的 BDP 来说,这个大小也应该更大

/proc/sys/net/core/wmem_default

110592

定义默认的发送窗口大小;对于更大的 BDP 来说,这个大小也应该更大

/proc/sys/net/core/wmem_max

110592

定义发送窗口的最大大小;对于更大的 BDP 来说,这个大小也应该更大

/proc/sys/net/ipv4/tcp_window_scaling

1

启用 RFC 1323 定义的 window scaling;要支持超过 64KB 的窗口,必须启用该值

/proc/sys/net/ipv4/tcp_sack

1

启用有选择的应答(Selective Acknowledgment),这可以通过有选择地应答乱序接收到的报文来提高性能(这样可以让发送者只发送丢失的报文段);(对于广域网通信来说)这个选项应该启用,但是这会增加对 CPU 的占用

/proc/sys/net/ipv4/tcp_fack

1

启用转发应答(Forward Acknowledgment),这可以进行有选择应答(SACK)从而减少拥塞情况的发生;这个选项也应该启用

/proc/sys/net/ipv4/tcp_timestamps

1

以一种比重发超时更精确的方法(请参阅 RFC 1323)来启用对 RTT 的计算;为了实现更好的性能应该启用这个选项

/proc/sys/net/ipv4/tcp_mem

24576 32768 49152

确定 TCP 栈应该如何反映内存使用;每个值的单位都是内存页(通常是 4KB)第一个值是内存使用的下限第二个值是内存压力模式开始对缓冲区使用应用压力的上限第三个值是内存上限在这个层次上可以将报文丢弃,从而减少对内存的使用对于较大的 BDP 可以增大这些值(但是要记住,其单位是内存页,而不是字节)

/proc/sys/net/ipv4/tcp_wmem

4096 16384 131072

为自动调优定义每个 socket 使用的内存第一个值是为 socket 的发送缓冲区分配的最少字节数第二个值是默认值(该值会被 wmem_default 覆盖),缓冲区在系统负载不重的情况下可以增长到这个值第三个值是发送缓冲区空间的最大字节数(该值会被 wmem_max 覆盖)

/proc/sys/net/ipv4/tcp_rmem

4096 87380 174760

与 tcp_wmem 类似,不过它表示的是为自动调优所使用的接收缓冲区的值

/proc/sys/net/ipv4/tcp_low_latency

0

允许 TCP/IP 栈适应在高吞吐量情况下低延时的情况;这个选项应该禁用

/proc/sys/net/ipv4/tcp_westwood

0

启用发送者端的拥塞控制算法,它可以维护对吞吐量的评估,并试图对带宽的整体利用情况进行优化;对于 WAN 通信来说应该启用这个选项

/proc/sys/net/ipv4/tcp_bic

1

为快速长距离网络启用 Binary Increase Congestion;这样可以更好地利用以 GB 速度进行操作的链接;对于 WAN 通信应该启用这个选项

想要修改相应的参数,只需: echo 256960 > /proc/sys/net/core/rmem_default。 echo 256960 > /proc/sys/net/core/rmem_max。 echo 256960 > /proc/sys/net/core/wmem_default。 echo 256960 > /proc/sys/net/core/wmem_max。 echo 0 > /proc/sys/net/ipv4/tcp_timestamps。 echo 1 > /proc/sys/net/ipv4/tcp_sack。 echo 1 > /proc/sys/net/ipv4/tcp_window_scaling。

4.1. window scaling

linux 最大只能支持 64KB 的窗口大小,当启用 window scaling 扩展后,可以实现 32位窗口大小的设置来突破这一限制,因此,如果需要设置 64KB 以上的窗口大小,则必须将 /proc/sys/net/ipv4/tcp_window_scaling 设置为 1。

4.2. 主动设置发送、接收缓冲区大小

下面的代码设置了缓冲区大小:

代码语言:javascript
复制
int ret, sock, sock_buf_size;
sock = socket(AF_INET, SOCK_STREAM, 0);
sock_buf_size = BDP;
ret = setsockopt(sock, SOL_SOCKET, SO_SNDBUF,
        (char *)&sock_buf_size, sizeof(sock_buf_size));
ret = setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
        (char *)&sock_buf_size, sizeof(sock_buf_size));

在 Linux 2.6 内核中,发送缓冲区的大小是由调用用户来定义的,但是接收缓冲区会自动加倍,同时,缓冲区的实际大小还是受限于上面说的系统的相关配置。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-08-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 小脑斧科技博客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 概述
  • 2. Nagle 算法
    • 2.1. 概述
      • 2.2. 算法规则
      • 3. 滑动窗口协议
        • 3.1. 基本介绍
          • 3.2. 窗口大小通告
          • 4. linux TCP 参数调优
            • 4.1. window scaling
              • 4.2. 主动设置发送、接收缓冲区大小
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档