# 网络基本功(十):细说TCP确认机制
**转载请在文首保留原文出处:EMC中文支持论坛**[https://community.emc.com/go/chinese](https://community.emc.com/go/chinese) [![image001.gif](https://community.emc.com/servlet/JiveServlet/downloadImage/2-842879-97159/image001.gif)](https://community.emc.com/servlet/JiveServlet/showImage/2-842879-97159/image001.gif)
## 介绍
在TCP确认机制中,无法有效处理非连续TCP片段。确认号表明所有低于该编号的sequence number已经被发送该编号的设备接收。如果我们收到的字节数落在两个非连续的范围内,则无法只通过一个编号来确认。这可能导致潜在严重的性能问题,特别是高速或可靠性较差的网络。
## 更多信息
还是以下图为例,服务器发送了4个片段并收到1条回复,确认号为201。因此,片段1和片段2被当成已确认。它们从重传队列中移出,同时允许服务器发送窗口向右移动200字节,从而发送数据增加200个字节。
然而,再次假设片段3,从sequence number201开始,在发送过程中丢失了。由于客户端从没有收到这一片段,所以它也无法发送确认号高于201的确认信息,从而导致滑动窗口停滞。服务器可以继续发送其他片段直到填满客户端的接收窗口,但是直到客户端发送另一条确认信息,服务器的发送窗口都不会滑动。
另一个问题是如果片段3丢失了,客户端将无法告知服务器是否收到后续的片段。在客户端接收窗口填满之前,很有可能客户端已经接收到片段4以及之后的片段。但是客户端无法发送值为501的确认信息以表明接收到片段4,**因为这意味着片段3也接收到了**。
[![image002.jpg](https://community.emc.com/servlet/JiveServlet/downloadImage/2-842879-97160/image002.jpg)](https://community.emc.com/servlet/JiveServlet/showImage/2-842879-97160/image002.jpg)
这里我们看到了TCP单编号,累积确认机制的缺点。我们可以想象一个最差的情况,服务器被告知它有一个10,000字节窗口,20个片段每个片段500字节。第一个片段丢失了,其他19个被接收到了。但是由于第一个片段从没有接收到,其他19个也无法确认。
**未确认片段处理策略:**
我们怎样处理丢失片段之后的片段呢?本例中,当服务器片段3重传超时,它必须决定怎样处理片段4,它不知道客户端是否已经接收到。在上述最差情况下,第一个片段丢失后,其余19个可能或可能无法被客户端接收到。
处理这种情况有两种可能的方式:
**仅重传超时片段**:这是一种更加保守的方式,仅重传超时的片段,希望其他片段都能够成功接收。如果该片段之后的其他片段实际上接收到了,这一方式是最佳的,如果没接收到,就无法正常执行。后者的情况每一个片段需要单独计时并重传。假设上述最坏情况下,所有20个500字节片段都丢失了。我们需要等片段1超时并重传。这一片段也许会得到确认,但之后我们需要等待片段2超时并重传。这一过程会重复多次。
**重传所有片段**:这是一种更激进或者说更悲观的方式。无论何时一个片段超时了,不仅重传该片段,还有所有其他尚未确认的片段。这一方式确保了任何时间都有一个等待确认的停顿时间,在所有未确认片段丢失的情况下,会刷新全部未确认片段,以使对端设备多一次接收机会。在所有20个片段都丢失的情况下,相对于第一种方式节省了大量时间。这种方式的问题在于可能这些重传是不必要的。如果第一个片段丢失而其他19个实际上接收到了,也得重传那9500字节数据。
由于TCP不知道其他片段是否接收到,所以它也无法确认哪种方法更好,但只能选择一种方式。上图示例了保守的方式,而下图显示的是激进的方式:
[![image003.jpg](https://community.emc.com/servlet/JiveServlet/downloadImage/2-842879-97164/image003.jpg)](https://community.emc.com/servlet/JiveServlet/showImage/2-842879-97164/image003.jpg)
问题的关键在于无法确认非连续片段。解决方式是对TCP滑动窗口算法进行扩展,添加允许设备分别确认非连续片段的功能。这一功能称为选择确认(selective acknowledgment, SACK)。
**选择确认:**
通过SACK,连接的两方设备必须同时支持这一功能,通过连接时使用的SYN片段来协商是否允许SACK。这一过程完成之后,任一设备都可以在常规TCP片段中使用SACK选项。这一选项包含一个关于已接收但未确认片段数据sequence number范围的列表,由于它们是非连续的。
各设备对重传队列进行修改,如果该片段已被选择确认过,则该片段中的SACK比特位置为1。该设备使用图2中激进方式的改进版本,一个片段重传之后,之后所有片段也会重传,**除非SACK比特位为1**。
例如,在4个片段的情况下,如果客户端接收到片段4而没有接收到片段3,当它发回确认号为201(片段1和片段2)的确认信息,其中包含一个SACK选项指明:“已接收到字节361至500,但尚未确认”。如果片段4在片段1和2之后到达,上述信息也可以通过第二个确认片段来完成。服务器确认片段4的字节范围,并为片段4打开SACK位。当片段3重传时,服务器看到片段4的SACK位为1,就不会对其重传。如下图所示。
在片段3重传之后,片段4的SACK位被清除。这是为了防止客户端出于某种原因改变片段4已接收的想法。客户端应当发送确认号为501或更高的确认信息,正式确认片段3和4接收到。如果这一情况没有发生,服务器必须接收到片段4的另一条选择确认信息才能将它的SACK位打开,否则,在片段3重传时或计时器超时的情况下会对其自动重传。
[![image004.jpg](https://community.emc.com/servlet/JiveServlet/downloadImage/2-842879-97165/image004.jpg)](https://community.emc.com/servlet/JiveServlet/showImage/2-842879-97165/image004.jpg)
- 介绍
- 网络基本功(一):细说网络传输
- 网络基本功(二):细说交换机
- 网络基本功(三):细说VLAN与Trunk
- 网络基本功(四):细说路由(上)
- 网络基本功(五):细说路由(下)
- 网络基本功(六):链路聚合
- 网络基本功(七):细说IP地址与子网
- 网络基本功(八):细说TCP滑动窗口
- 网络基本功(九):细说TCP重传
- 网络基本功(十):细说TCP确认机制
- 网络基本功(十一):TCP窗口调整与流控
- 网络基本功(十二):细说Linux网络配置(上)
- 网络基本功(十三):细说Linux网络配置(下)
- 网络基本功(十四):细说诊断工具ping
- 网络基本功(十五):细说网络性能监测与实例(上)
- 网络基本功(十六):细说网络性能监测与实例(下)
- 网络基本功(十七):细说tcpdump的妙用(上)
- 网络基本功(十八):细说tcpdump的妙用(下)
- 网络基本功(十九):细说NAT原理与配置
- 网络基本功(二十):细说ICMP和ARP
- 网络基本功(二十一):细说HTTP(上)
- 网络基本功(二十二):细说HTTP(下)
- 网络基本功(二十三):Wireshark抓包实例诊断TCP连接问题
- 网络基本功(二十四):Wireshark抓包实例分析TCP重传
- 网络基本功(二十五):Wireshark抓包实例分析TCP重复ACK与乱序
- 网络基本功(二十六):Wireshark抓包实例分析TCP窗口及reset
- 网络基本功(二十七):Wireshark抓包实例分析HTTP问题(上)
- 网络基本功(二十八):Wireshark抓包实例分析HTTP问题(下)
- 网络基本功(二十九):Wireshark抓包实例诊断数据库常见问题
- 网络基本功(三十):细说DNS(上)
- 网络基本功(三十一):细说DHCP