目录
- 导语
- [一、3.5.1 TCP连接](#一、3.5.1 TCP连接)
- [二、3.5.2 TCP报文段结构](#二、3.5.2 TCP报文段结构)
- [三、3.5.3 往返时间的估计与超时](#三、3.5.3 往返时间的估计与超时)
- [四、3.5.4 可靠数据传输](#四、3.5.4 可靠数据传输)
- [五、3.5.5 流量控制](#五、3.5.5 流量控制)
- [六、3.5.6 TCP连接管理](#六、3.5.6 TCP连接管理)
- 本节核心总结(必看)
- 结语
导语
大家好呀~经过前面3.1到3.4节的铺垫,我们终于来到了运输层最重磅的一节------TCP(Transmission Control Protocol,传输控制协议)!如果说UDP是一个"甩手掌柜",那TCP就是一个"事无巨细的大管家"------它要管连接建立、管数据可靠、管流量大小、管拥塞避免、管连接释放,几乎把运输层能做的事全包了。
还记得3.4节我们学的可靠数据传输原理吗?rdt 1.0到rdt 3.0的进化、GBN和SR的流水线协议------那些都是"理论模型"。而TCP,就是这些理论在真实世界中的工程化实现。TCP把序号、确认、重传、超时、滑动窗口这些概念全部落地,还额外加了流量控制和拥塞控制两大杀器。可以说,理解了TCP,就理解了因特网为什么能可靠地传输海量数据。
这一节内容非常多,是整本书最核心的章节之一。我们会从TCP连接的本质讲起,然后拆解TCP报文段的每一个字段,接着深入RTT估计和超时重传的数学原理,再看TCP如何实现可靠数据传输,然后学习流量控制机制,最后搞懂三次握手和四次挥手的连接管理。准备好了吗?让我们开始这场TCP深度之旅吧~

一、3.5.1 TCP连接
1. TCP是什么?------"先打电话再说话"的协议
TCP被称为面向连接(connection-oriented)的运输层协议。什么叫"面向连接"?通俗类比:UDP就像寄明信片 ------写好地址直接扔邮筒,不管对方收没收到;而TCP就像打电话------你得先拨号,对方接听了,你们才能开始通话,通话结束后还要挂电话。这个"拨号-通话-挂机"的过程,就是连接的建立、数据传输和释放。
但要注意,TCP的"连接"并不是像电路交换那样在中间预留了一条专用通路。TCP连接是逻辑上的------它只存在于两端的TCP实体中,中间的路由器对TCP连接一无所知。TCP连接提供的是**全双工(full-duplex)**服务:数据可以在两个方向上同时流动,就像打电话时双方可以同时说话一样。
TCP连接也是**点对点(point-to-point)**的:一个发送方对应一个接收方,不支持一对多的广播或多播。这和UDP形成对比------UDP可以一个发送方给多个接收方发数据(广播),但TCP只能一对一。
2. TCP连接的组成------套接字对
一条TCP连接由什么唯一标识呢?答案是套接字对(socket pair),即:
- 发送方IP地址 + 发送方端口号
- 接收方IP地址 + 接收方端口号
这四个数组合在一起,就能在全网唯一确定一条TCP连接。比如你的电脑(IP: 192.168.1.100,端口: 54321)访问百度服务器(IP: 110.242.68.3,端口: 80),这条连接就由(192.168.1.100, 54321, 110.242.68.3, 80)唯一确定。
这也解释了为什么一个服务器可以同时处理成千上万的客户端连接------虽然服务器的IP和端口固定,但每个客户端的IP和端口不同,所以套接字对不同,连接也就不同。
3. TCP连接的建立过程------三次握手
TCP连接的建立需要经过三次握手(three-way handshake),我们会在3.5.6节详细讲解。这里先有个概念:客户端先发一个SYN报文,服务器回一个SYN+ACK报文,客户端再发一个ACK报文,连接就建立了。三次握手的目的是同步双方的初始序号,并确认双方都有发送和接收能力。
连接建立后,双方就可以开始传输数据了。TCP会把应用层交下来的数据看成无结构的字节流(byte stream)------它不关心数据的边界,只负责按序、可靠地把字节流从一端搬到另一端。这就像一条水管,水(数据字节)从一端进去,从另一端出来,TCP保证水不会漏、不会混、顺序不会乱。

二、3.5.2 TCP报文段结构
TCP传输的数据单元叫做报文段(segment) 。一个TCP报文段由**首部(header)和数据(data)**两部分组成。首部长度通常是20字节(没有选项时),最多可以到60字节(有选项时)。下面我们逐字段拆解TCP首部。

1. 源端口号和目的端口号(各16位)
**源端口号(Source Port Number)和目的端口号(Destination Port Number)**各占16位,用于多路复用和多路分解------这和3.2节讲的一样。源端口标识发送方的应用进程,目的端口标识接收方的应用进程。
2. 序号(32位)
序号(Sequence Number)占32位,是TCP可靠传输的核心。TCP把数据看成字节流,每个字节都有一个编号。报文段的序号就是这个报文段所携带数据的第一个字节的编号。
举个例子:如果一个报文段的序号是500,携带了1000字节数据,那么这个报文段覆盖的字节范围是500~1499,下一个报文段的序号就应该是1500。
连接建立时,双方会各自选择一个初始序号(Initial Sequence Number,ISN)。注意,初始序号不是从0开始的!而是随机选择的,这是为了防止某些攻击(比如预测序号注入伪造报文)。
3. 确认号(32位)
确认号(Acknowledgment Number)占32位。TCP使用累积确认(cumulative acknowledgment)------确认号表示"我期望收到的下一个字节的序号",也就是说,确认号之前的所有字节都已经正确收到了。
比如接收方已经正确收到了字节0~999,那么它会发送确认号1000,表示"0到999我都收到了,接下来给我发1000开始的数据吧"。
这里有个重要概念:捎带确认(piggybacking)。因为TCP是全双工的,A给B发数据时,B的确认信息可以"搭便车"放在B给A发的数据报文段里一起带过去,不需要单独发一个确认报文。这样可以提高效率。
4. 首部长度(4位)
**首部长度(Header Length)**占4位,以4字节为单位。因为TCP首部有可变长的选项字段,所以需要这个字段来告诉接收方首部到底有多长。最小值是5(5×4=20字节,无选项),最大值是15(15×4=60字节)。
5. 标志位(6位)
TCP首部有6个标志位(flag bits),每个位代表一种控制功能:
- URG(Urgent):紧急指针有效位。表示这个报文段中有紧急数据,需要优先处理。
- ACK(Acknowledgment):确认号有效位。连接建立后,除了第一个SYN报文,所有报文的ACK都应该置1。
- PSH(Push):推送位。表示接收方应该立即把数据交给应用层,不要等缓冲区满了再交。
- RST(Reset):复位位。用于重置一条混乱的连接,或拒绝一个非法的连接请求。
- SYN(Synchronize):同步序号位。在三次握手中用于同步初始序号。
- FIN(Finish):结束位。在四次挥手中用于释放连接。
这6个标志位是TCP连接管理的核心工具。SYN和FIN用于连接的建立和释放,RST用于异常处理,ACK用于确认,URG和PSH用于数据传输的特殊控制。
6. 接收窗口(16位)
接收窗口(Receive Window)占16位,用于流量控制(flow control)。它表示接收方当前还能接收多少字节的数据,单位是字节。发送方根据这个值来调整自己的发送速率,避免发送太快把接收方的缓冲区撑爆。我们会在3.5.5节详细讲解。
接收窗口最大为65535字节(2^16-1)。但在高速网络中,这个值可能不够用,所以TCP有一个窗口缩放选项(Window Scale Option),可以把窗口扩大到2^30字节,这就是所谓的"大窗口"或"长肥管道"优化。
7. 校验和(16位)
校验和(Checksum)占16位,用于检测报文段在传输过程中是否发生了比特差错。计算方法和UDP类似,也需要加上一个伪首部(pseudo header),包含源IP、目的IP、协议号和TCP长度。虽然TCP运行在不可靠的IP之上,但校验和能提供端到端的差错检测。
8. 紧急指针(16位)
**紧急指针(Urgent Pointer)**占16位,只有当URG标志位为1时才有效。它指向紧急数据的最后一个字节的位置,告诉接收方"从序号开始到序号+紧急指针的位置,这些数据是紧急的,请优先处理"。实际应用中很少用到,但Telnet等交互式应用可能会用它来发送中断信号(比如Ctrl+C)。
9. 选项(可变长)
**选项(Options)**字段是可变长的,最长40字节。常见的选项有:
- 最大报文段长度(Maximum Segment Size,MSS):告诉对方我最大能接收的报文段数据部分长度。
- 窗口缩放(Window Scale):扩大接收窗口的表示范围。
- 时间戳(Timestamp):用于更精确地计算RTT和防止序号回绕。
- 选择确认(Selective Acknowledgment,SACK):允许接收方确认乱序到达的报文段,提高重传效率。
选项字段让TCP具备了良好的可扩展性------新的功能可以通过选项来添加,而不需要改变TCP的基本首部结构。
三、3.5.3 往返时间的估计与超时
TCP的可靠传输依赖于超时重传 机制:发出去的报文段如果在一定时间内没收到确认,就认为丢了,重新发送。但这个"一定时间"------也就是超时重传时间(Retransmission TimeOut,RTO)------该设多长呢?这是一个非常关键的问题。

1. 为什么不能用固定的超时时间?
如果RTO设得太短:包可能没丢,只是ACK还在路上,结果白白重传了,浪费带宽。如果RTO设得太长:包真的丢了,要等很久才重传,性能很差。而且网络状况是动态变化的------有时拥堵、有时顺畅,RTT(往返时间)一直在波动。所以TCP必须动态估计RTT,并根据估计值动态调整RTO。
2. 样本RTT(SampleRTT)
**样本RTT(SampleRTT)**是指:从发送一个报文段到收到它的确认之间所经历的时间。每发一个报文段,就能测一个SampleRTT。但注意,TCP不会对重传的报文段测量SampleRTT------因为你不知道这个ACK是对原始报文的确认还是对重传报文的确认,测出来不准。
3. 估计RTT(EstimatedRTT)
单个SampleRTT波动太大,不能直接用来设RTO。TCP使用**指数加权移动平均(Exponential Weighted Moving Average,EWMA)**来平滑RTT估计:
EstimatedRTT = (1 - α) × EstimatedRTt + α × SampleRTT
其中α是一个介于0和1之间的系数,推荐值为0.125(即1/8)。这个公式的意思是:新的估计值 = 旧估计值的87.5% + 新样本的12.5%。这样,历史值占大头,新样本只占一小部分,估计值就比较平滑,不会因为单个样本的波动而剧烈变化。
打个比方:EstimatedRTT就像你对一个城市天气的"印象"------昨天的天气(新样本)会影响你的印象,但之前长期的印象(旧估计)占主导,不会因为一天突然变冷就觉得这个城市一直很冷。
4. RTT偏差(DevRTT)
光有平均RTT还不够,还需要知道RTT的波动程度。如果RTT很稳定,RTO可以设得接近EstimatedRTT;如果RTT波动很大,RTO就要设得大一些,留足余量。
TCP用DevRTT来估计RTT的偏差程度:
DevRTT = (1 - β) × DevRTT + β × |SampleRTT - EstimatedRTT|
其中β的推荐值为0.25(即1/4)。DevRTT本质上是SampleRTT与EstimatedRTT之差的绝对值的EWMA,反映了RTT的波动幅度。
5. 超时重传时间(RTO)
有了EstimatedRTT和DevRTT,RTO的计算公式就很直观了:
RTO = EstimatedRTT + 4 × DevRTT
为什么是4倍DevRTT?这是一个经验值------在大多数网络环境下,RTT的分布近似正态,均值加4倍标准差能覆盖99.99%以上的情况,既不会因为正常波动而误重传,也不会在真正丢包时等太久。
另外,RTO有一个最小值1秒的限制。即使EstimatedRTT和DevRTT都很小,RTO也不会低于1秒。这是为了防止在局域网等低延迟环境下RTO过小导致大量误重传。
6. Karn算法
前面提到,TCP不对重传的报文段测量SampleRTT。这就是**Karn算法(Karn's Algorithm)**的核心思想:当发生重传时,不更新RTT估计。因为重传后收到的ACK,你无法确定它是对原始报文的确认还是对重传报文的确认------如果是对原始报文的确认,那SampleRTT会被高估;如果是对重传报文的确认,那SampleRTT会被低估。干脆不测,等下一个正常发送的报文段再测。
但Karn算法有一个补充:每次重传后,RTO翻倍(也叫指数退避,exponential backoff)。这是因为重传通常意味着网络拥塞,此时应该更加保守,给网络更多恢复时间。这个策略和以太网的CSMA/CD退避机制思想一致。
四、3.5.4 可靠数据传输
TCP在不可靠的IP层之上提供了可靠的数据传输服务。它综合运用了我们在3.4节学的所有机制:序号、确认、重传、超时、滑动窗口。下面我们看看TCP具体是怎么做的。

1. TCP发送方的事件
TCP发送方主要响应三类事件:
(1)收到应用层数据:发送方把应用层数据封装成TCP报文段,填上序号(当前未发送的第一个字节的编号),然后发送。同时启动定时器(如果还没启动的话)。
(2)定时器超时:重传导致超时的那个报文段,然后重启定时器。注意,TCP的定时器是针对最早的未确认报文段的,类似GBN的单一定时器。
(3)收到ACK:根据确认号更新发送窗口。如果确认号大于当前的base(最早未确认字节的序号),说明有新的数据被确认了,滑动窗口,更新base。如果还有未确认的数据,重启定时器;如果全部确认了,停止定时器。
2. 快速重传(Fast Retransmit)
超时重传有一个问题:如果RTO设得比较大(比如1秒以上),丢包后要等很久才重传,性能很差。TCP引入了**快速重传(Fast Retransmit)**机制来解决这个问题。
快速重传的原理:如果发送方收到了3个重复的ACK (即总共收到4个相同确认号的ACK,第一个是正常的,后面三个是重复的),就认为这个确认号对应的报文段丢了,立即重传,不需要等定时器超时。
为什么是3个重复ACK?因为重复ACK通常是由乱序到达引起的------接收方收到了后面的报文段,但前面的还没到,就会重复发送对前面缺失报文段的确认。如果只是轻微乱序,可能收到1-2个重复ACK后缺失的报文段就到了;但如果收到3个重复ACK,说明缺失的报文段很可能真的丢了,此时重传比较靠谱。

3. TCP的可靠传输:GBN和SR的混合体
TCP的可靠传输机制既不是纯粹的GBN,也不是纯粹的SR,而是两者的混合:
- 像GBN的地方:使用累积确认,发送方只维护一个定时器(针对最早的未确认报文段)。
- 像SR的地方:接收方会缓存乱序到达的正确报文段,不会丢弃。当缺失的报文段到达后,再按序交给应用层。
此外,TCP还支持**选择确认(Selective Acknowledgment,SACK)**选项。启用SACK后,接收方可以告诉发送方"我收到了哪些乱序的报文段",这样发送方就可以只重传真正丢失的那些,而不是从缺失处开始全部重传。SACK让TCP更接近SR协议,提高了丢包时的传输效率。
4. 一个具体的例子
假设发送方发送了报文段A(序号0-999)、B(1000-1999)、C(2000-2999)、D(3000-3999),其中B在传输中丢失了:
- 接收方收到A,回ACK 1000
- B丢了,接收方收不到
- 接收方收到C(乱序),缓存C,回ACK 1000(重复ACK,表示还在等1000开始的数据)
- 接收方收到D(乱序),缓存D,回ACK 1000(又一个重复ACK)
- 发送方收到3个重复ACK 1000,触发快速重传,立即重传B
- 接收方收到B,现在B、C、D都齐了,按序交给应用层,回ACK 4000
看到了吗?通过快速重传,发送方不需要等定时器超时就能发现丢包并重传,大大提高了性能。而接收方缓存乱序报文段的特性,也避免了C和D被白白重传。
五、3.5.5 流量控制
1. 什么是流量控制?------"别把我撑爆了"
**流量控制(Flow Control)**是TCP的一项重要服务,目的是让发送方的发送速率不要太快,确保接收方能来得及处理。通俗类比:就像你往一个桶里倒水,如果倒得太快,水就会溢出来。流量控制就是让倒水的人根据桶里还剩多少空间来调整倒水速度。
注意区分流量控制 和拥塞控制:
- 流量控制是端到端的问题------发送方太快,接收方处理不过来。
- 拥塞控制是网络中的问题------发送方太快,中间路由器处理不过来。
两者目的不同,但都会让发送方降低速率。这一节先讲流量控制,拥塞控制留到3.7节。

2. 接收窗口(rwnd)
TCP通过**接收窗口(Receive Window,rwnd)**来实现流量控制。接收方在每个TCP报文段的"接收窗口"字段中填入自己当前还能接收的字节数,发送方看到这个值后,就会确保自己已发送但未确认的数据量不超过这个值。
具体来说,发送方维护以下变量:
- LastByteSent:最近发送的最后一个字节的序号+1
- LastByteAcked:最近被确认的最后一个字节的序号+1
- rwnd:接收方通告的接收窗口大小
发送方必须保证:LastByteSent - LastByteAcked <= rwnd。也就是说,已发送未确认的数据量不能超过接收窗口。
3. 接收窗口的计算
接收方维护一个接收缓冲区,应用层从缓冲区中读取数据。接收窗口的大小等于:
rwnd = RcvBuffer - [LastByteRcvd - LastByteRead]
其中:
- RcvBuffer:接收缓冲区的总大小
- LastByteRcvd:最近从网络收到的最后一个字节的序号+1
- LastByteRead:应用层最近读取的最后一个字节的序号+1
LastByteRcvd - LastByteRead就是缓冲区中已收到但应用层还没读取的数据量。用总缓冲区减去已占用的,就是还能接收的空间,也就是rwnd。
4. 零窗口问题与持续计时器
如果接收方的缓冲区满了,rwnd就会变成0。发送方收到rwnd=0后,就会停止发送数据,等待接收方重新通告一个非零的窗口。
但这里有一个潜在的死锁问题:如果接收方后来有了空间,发送了一个rwnd非零的ACK报文,但这个ACK丢了怎么办?发送方还在等非零窗口,接收方以为已经通知了发送方,双方就卡死了。
TCP用持续计时器(Persistent Timer)来解决这个问题。当发送方收到rwnd=0时,启动持续计时器。计时器超时后,发送方发送一个窗口探测报文段(Window Probe)------这个报文段只携带1个字节的数据,用来探测接收方当前的窗口大小。接收方收到探测报文后,会回复当前的rwnd。如果还是0,就重置持续计时器继续等;如果非零了,就恢复发送。
5. 糊涂窗口综合征(Silly Window Syndrome)
还有一个经典问题叫糊涂窗口综合征(Silly Window Syndrome,SWS)。想象一下:接收方的缓冲区只剩1个字节了,它通告rwnd=1。发送方收到后,发一个只带1字节数据的报文段(加上20字节首部,总共41字节)。接收方处理了这1字节,又通告rwnd=1,发送方又发1字节......这样每个报文段只传1字节数据,效率极低,就像每次只往桶里倒一滴水。
解决方法有两个方向:
- 接收方:不要一有小空间就通告,而是等缓冲区有了足够大的空间(至少达到MSS或缓冲区的一半)再通告。
- 发送方 :使用Nagle算法(Nagle's Algorithm)------如果发送的数据小于MSS,且还有未确认的数据,就先攒着,等数据攒够一个MSS或者收到确认后再发。
Nagle算法在减少小报文方面非常有效,但在某些交互式应用(如Telnet、游戏)中可能导致延迟增加,所以这些应用通常会禁用Nagle算法(设置TCP_NODELAY选项)。
六、3.5.6 TCP连接管理
TCP是面向连接的协议,连接的建立和释放都需要精心设计。这一小节我们学习TCP最著名的两个机制:三次握手 建立连接和四次挥手释放连接。

1. 三次握手------建立连接
TCP连接的建立需要客户端和服务器之间交换三个报文段,这就是三次握手(Three-Way Handshake)。

过程详解:
第一步(客户端→服务器) :客户端发送一个SYN报文段 (SYN标志位置1),其中包含客户端选择的初始序号client_isn。此时客户端进入SYN_SENT状态。这个报文段的作用是:"服务器你好,我想和你建立连接,我的初始序号是client_isn。"
第二步(服务器→客户端) :服务器收到SYN后,为该连接分配TCP缓冲区和变量,然后回复一个SYN+ACK报文段(SYN和ACK标志位都置1)。其中:
- 确认号 = client_isn + 1(表示收到了客户端的SYN)
- 序号 = server_isn(服务器自己的初始序号)
此时服务器进入SYN_RCVD状态。这个报文段的作用是:"客户端你好,收到你的请求了,我的初始序号是server_isn,确认你的序号client_isn。"
第三步(客户端→服务器) :客户端收到SYN+ACK后,也为连接分配缓冲区和变量,然后回复一个ACK报文段 (ACK标志位置1,SYN标志位为0)。确认号 = server_isn + 1。这个报文段可以携带数据,也可以不携带。此时客户端进入ESTABLISHED状态。
服务器收到这个ACK后,也进入ESTABLISHED状态。连接建立完成,双方可以开始传输数据了。
为什么是三次而不是两次?
两次握手的问题:假设客户端发了一个SYN,但这个SYN在网络中滞留了,客户端以为它丢了,就重发了一个SYN,建立了连接,传输完数据,释放了连接。这时那个滞留的旧SYN才到达服务器。如果是两次握手,服务器收到旧SYN后就会建立连接并等待数据,但客户端根本没有数据要发,服务器就白白维护了一条无用的连接。三次握手可以避免这个问题------服务器收到旧SYN后回SYN+ACK,但客户端不会回第三个ACK,连接就建立不起来。
另外,三次握手还能确保双方的初始序号都得到了对方的确认,这是可靠传输的基础。
2. SYN洪泛攻击
三次握手中有一个安全隐患:服务器在收到SYN后就分配了缓冲区和变量,但此时连接还没完全建立。攻击者可以伪造大量源IP地址,发送大量SYN报文,服务器为每个SYN分配资源,但永远收不到第三个ACK,最终服务器资源耗尽,无法处理正常请求。这就是SYN洪泛攻击(SYN Flood Attack)。
防御方法:SYN Cookie(SYN饼干)。服务器收到SYN后,不立即分配资源,而是根据源IP、目的IP、源端口、目的端口和一个秘密密钥计算出一个cookie,作为初始序号放在SYN+ACK中发回去。如果客户端是真实的,会回复ACK,其中的确认号 = cookie + 1。服务器验证这个cookie有效后,才分配资源建立连接。这样,攻击者不回复ACK,服务器就不分配资源,攻击就失效了。
3. 四次挥手------释放连接
TCP连接的释放需要四个报文段,这就是四次挥手(Four-Way Handshake)。因为TCP是全双工的,两个方向的数据通道需要分别关闭。

过程详解:
第一步(客户端→服务器) :客户端发送一个FIN报文段 (FIN标志位置1),表示"我没有数据要发了,我要关闭这个方向的连接"。此时客户端进入FIN_WAIT_1状态。
第二步(服务器→客户端) :服务器收到FIN后,回复一个ACK报文段 ,确认号 = FIN的序号 + 1。此时服务器进入CLOSE_WAIT 状态,客户端进入FIN_WAIT_2状态。这个ACK只是确认收到了FIN,服务器可能还有数据要发。
第三步(服务器→客户端) :服务器把剩余的数据发完后,发送一个FIN报文段 ,表示"我也没有数据要发了,我也要关闭这个方向的连接"。此时服务器进入LAST_ACK状态。
第四步(客户端→服务器) :客户端收到服务器的FIN后,回复一个ACK报文段 ,确认号 = 服务器FIN的序号 + 1。此时客户端进入TIME_WAIT状态。
服务器收到这个ACK后,进入CLOSED状态,连接释放。客户端在TIME_WAIT状态等待**2MSL(Maximum Segment Lifetime,最大报文段生存时间)**后,也进入CLOSED状态。
4. TIME_WAIT状态------为什么要等2MSL?
TIME_WAIT状态是TCP连接管理中最容易被问到的问题。客户端在发完最后一个ACK后,为什么不直接关闭,而是要等2MSL呢?有两个原因:
原因一:确保最后一个ACK能到达服务器。如果客户端发的最后一个ACK丢了,服务器会重发FIN。客户端在TIME_WAIT状态下还能收到这个重发的FIN,然后重新发送ACK。如果客户端直接关闭了,服务器重发FIN后收不到ACK,就无法正常关闭。
原因二:让旧连接的报文段在网络中消失。MSL是一个报文段在网络中能存活的最长时间。等待2MSL可以确保本次连接的所有报文段都从网络中消失,这样下一次新连接(使用相同的套接字对)就不会收到旧连接的残留报文段,避免混淆。
MSL的推荐值是2分钟,但实际实现中常用30秒、1分钟等。2MSL就是1~4分钟不等。这也是为什么服务器端经常出现大量TIME_WAIT状态的连接------高并发服务器需要处理大量短连接,每个连接释放后都要等2MSL才能完全释放资源。
5. 同时关闭和同时打开
TCP还支持一些特殊情况:
- 同时关闭(Simultaneous Close):双方同时发送FIN,此时四次挥手会变成某种特殊的交换过程,但最终都要经过TIME_WAIT。
- 同时打开(Simultaneous Open):双方同时向对方发起连接请求,此时会交换四个报文段(而不是三次握手的三个),最终也能建立连接。但这种情况非常罕见。
本节核心总结(必看)
这一节内容非常丰富,我们用最简洁的方式回顾核心要点:
一、TCP连接
- TCP是面向连接、全双工、点对点的协议
- 连接由套接字对(源IP+源端口,目的IP+目的端口)唯一标识
- TCP把数据看成无结构的字节流
二、TCP报文段结构
- 首部通常20字节,含源/目的端口、序号、确认号、标志位、接收窗口、校验和等
- 序号是报文段第一个数据字节的编号
- 确认号是期望收到的下一个字节的编号(累积确认)
- 6个标志位:URG、ACK、PSH、RST、SYN、FIN
- 选项字段支持MSS、窗口缩放、时间戳、SACK等扩展
三、RTT估计与超时
- EstimatedRTT = (1-α)×EstimatedRTT + α×SampleRTT,α=0.125
- DevRTT = (1-β)×DevRTT + β×|SampleRTT - EstimatedRTT|,β=0.25
- RTO = EstimatedRTT + 4×DevRTT,最小1秒
- Karn算法:重传时不更新RTT估计,且RTO翻倍
四、可靠数据传输
- 综合运用序号、确认、重传、超时、滑动窗口
- 快速重传:收到3个重复ACK立即重传,不等超时
- TCP是GBN和SR的混合体:累积确认+接收方缓存乱序报文
- SACK选项让TCP更接近SR,只重传真正丢失的报文
五、流量控制
- 通过接收窗口rwnd控制发送速率,防止接收方缓冲区溢出
- 持续计时器解决零窗口死锁问题
- Nagle算法和接收方延迟通告解决糊涂窗口综合征
六、连接管理
- 三次握手建立连接:SYN → SYN+ACK → ACK
- 四次挥手释放连接:FIN → ACK → FIN → ACK
- TIME_WAIT等待2MSL:确保最后ACK到达 + 旧报文段消失
- SYN Cookie防御SYN洪泛攻击
关键概念清单
- 面向连接(Connection-Oriented):传输前先建立逻辑连接
- 全双工(Full-Duplex):数据可双向同时传输
- 报文段(Segment):TCP的数据传输单元
- 序号(Sequence Number):字节流编号
- 累积确认(Cumulative ACK):确认号之前全部收到
- 快速重传(Fast Retransmit):3个重复ACK触发重传
- 流量控制(Flow Control):端到端的速率匹配
- 接收窗口(Receive Window):接收方剩余缓冲区大小
- 三次握手(Three-Way Handshake):连接建立过程
- 四次挥手(Four-Way Handshake):连接释放过程
- TIME_WAIT:等待2MSL的状态
- SYN洪泛(SYN Flood):拒绝服务攻击
结语
这一节我们全面学习了TCP------因特网运输层最重要、最复杂的协议。从TCP连接的本质,到报文段的每一个字段,从RTT估计的数学公式,到可靠传输的快速重传机制,从流量控制的接收窗口,到三次握手和四次挥手的连接管理,每一个机制都经过了几十年的打磨和优化。
TCP的设计哲学值得我们细细品味:它不追求理论上的完美,而是在真实网络环境中不断妥协和优化。比如RTO的计算公式用的是经验值(4倍DevRTT),快速重传用3个重复ACK作为阈值,这些都是在理论和实践之间找到的平衡点。
但是,TCP还有一个非常重要的话题我们还没讲------拥塞控制。流量控制解决的是"接收方处理不过来"的问题,而拥塞控制解决的是"网络中间路由器处理不过来"的问题。下一节3.6我们将先学习拥塞控制的基本原理,然后3.7节深入TCP的拥塞控制算法(慢启动、拥塞避免、快速恢复)。这部分内容是TCP的灵魂,也是面试高频考点,让我们继续前进吧!
如果你觉得这篇笔记对你有帮助,欢迎点赞收藏~有问题也欢迎留言讨论,我们一起进步!