TCP 的特点:
面向连接的传输服务
支持字节流传输:
应用程序与 TCP 每次交互的数据长度可能都不相同,但 TCP 将应用程序提交的数据看作时一连串的、无结构的字节流,同时在发送方与接收方都维护一个缓存。发送方将几个写操作合并成一个报文段,提交给IP协议封装成IP分组后发送。接收方将接受的字节存储在接收缓存中,由应用程序的读操作取出。
支持全双工服务:
通信双方的应用程序在任何时候都可以发送数据。通信双方都有发送缓冲区与接收缓冲区,应用程序将要发送的数据字节提交给发送缓冲区,并在合适的时候从接收缓冲区读取数据,至于实际的发送接收过程则由 TCP 协议控制。
支持同时建立多个并发连接:
TCP 支持同时建立多个连接,即在一台主机的多个端口可以建立多个 TCP 连接。(同一个端口上的 TCP 连接可以被表示为多个全连接的五元组)
可靠传输:
TCP 能够对发送与接收的自己进行跟踪、确认与重传。
TCP 为发送字节流中每个字节按顺序编号,连接建立时,通信双方都随机产生一个初始序号(ISN),且双方的序号是不同的
确认序号表示接收方已正确接收了序号 N 及之前的字节,要求发送方发送序号为 N+1 的字节的报文段。
报头长度:4位,以 字节 为单位记录TCP报头的长度。由于 TCP 报头的长度为 20~60 字节,故该字段的值为5~15。
保留字段:留作今后使用
控制字段:控制字段定义了6种不同的控制位
标志 | 说明 |
---|---|
SYN | |
ACK | 只有当ACK=1时,确认序号字段才有意义 |
FIN | 终止连接。FIN=1时,表明请求释放连接 |
RST | 复位字段。要求立即关闭连接 |
URG | 此报文为紧急数据,应优先发送,需配合紧急指针使用 |
PSH | 将数据向前推,当 PSH=1 时,请求接收方 TCP 尽快将该报文推给应用程序 |
窗口字段:以 字节 为单位,表示要求对方必须维持的窗口大小
紧急指针:只有当紧急标志=1时才有效,其值表示本报文段中的紧急数据的最后一个字节的编号
选项:TCP 报头可以有长达 40 字节的选项字段
校验和:TCP 校验和的计算过程与 UDP 相同。但在 UDP 中校验和为可选,而在 TCP 中是必需的。TCP 校验和同样需要伪报头,不同的是协议字段的值为 6
TCP报文头的长度为 20~60 字节(具体的长度取决于选项字段)。
TCP 协议对报文数据的最大长度的限定称为最大段长度(MSS)。MSS 是指报文中数据的最大字节数限定,不包含报头长度,且 TCP 最终发送的报文数据长度由 窗口大小 与 MSS共同决定。
MSS的选取一般会综合考虑:① 协议开销 ② 发送与接收方缓冲区的限制
三次握手指有三个报文被发送:SYN、SYN+ACK、ACK
期间 CLIENT(连接发起者)的状态:CLOSED -> SYN-SEND -> ESTABLISHED
期间 SERVER(连接接收方)的状态:LISTEN -> SYN-RECD -> ESTABLISHED
连接建立后,CLIENT 与 SERVER 就可以使用连接进行全双工的字节流传输。(这里使用C/S模式为了便于区分)
客户端(连接发起者)与服务端(连接接收者)都可以发起TCP连接的释放。
CLIENT 进入FIN+WAIT-1(释放等待1)状态,并向 SERVER 发送第一个“FIN”(FIN=1)报文。
SERVER 收到“FIN”报文后,向 CLIENT 发回“ACK”报文,表示对第一个“FIN”请求报文的确认,并进入CLOSE-WAIT状态
TCP通知高层应用程序 CLIENT 请求释放连接,此时 CLIENT 不会再向 SERVER 发送数据,CLIENT 到 SERVER 的连接断开;但 SERVER 到 CLIENT 的TCP连接还没有断开,若 SERVER 愿意,还可以继续发送数据直至完毕,这种连接状态称为“半关闭状态”
CLIENT 收到“ACK”报文后,进入FIN+WAIT-2状态
当 SERVER 的高层应用无数据要写入时,会通知 TCP 释放连接,此时 SERVER 向 CLIENT 发送“FIN”报文,并进入LAST-ACK状态
CLIENT 收到“FIN”报文后,向 SERVER 发送“ACK”报文,表示对“FIN”报文的确认。此时 CLIENT 进入TIME-WAIT状态,并等待至少2个最长报文寿命(MSL)时间后,才真正进入CLOSE状态
设置延时等待机制(时间等待定时器)的原因:
确保连接能被可靠地关闭(若“FIN”报文或主动关闭端的“ACK”丢失,则被动关闭端会一直发送“FIN”报文,主动关闭端需要保持在某种状态一定时间,以对这种情况进行处理。若主动关闭端已经不存在,则主动关闭端的 TCP 协议栈会向对端发送一个 RST 报文作为响应。1 个 MSL 时间留给最后的 ACK 确认报文到达被动关闭端节点,另一个 MSL 留给重传的 FIN 报文);
保证网络中冗余的 TCP 报文能够有足够长的时间失效并被丢弃,使得新的化身能够被安全地建立(若 CLIENT 与 SERVER 在连接关闭后重新建立原连接的化身,即具有相同的地址与端口号,而若不等待网络中迟来的报文过期,则新的化身可能收到这些报文。而 TCP 的最大生存周期是 MSL,坚持 2*MSL 的时间能够使得两个传输方向上迟来的报文都已失效)
SERVER 收到“ACK”报文后,进入LISTEN状态
四次挥手指有四个报文被发送:FIN、ACK、FIN、ACK,其中每个ACK报文都是对上一个FIN报文的确认。
期间 CLIENT(连接发起者)的状态:ESTABLISHED -> FIN-WAIT-1(发送FIN后) -> FIN-WAIT-2(接收ACK报文后) -> TIME-WAIT(收到FIN后,延时关闭) -> CLOSE
期间 SERVER(连接接收方)的状态:ESTABLISHED -> CLOSE-WAIT(收到FIN后) -> LAST-ACK(发送FIN后) -> LISTEN(收到ACK后)
TCP 设置了四个定时器:重传定时器、坚持定时器、保持定时器、时间等待定时器,其中与 TCP 连接状态有关的为:
保持定时器:又称激活定时器,用以防止 TCP 连接处于长时间空闲状态。超时设置通常为 2 小时,每当服务器端收到客户端的信息时,就将定时器复位;而若超过 2 小时没有收到客户端的信息,则将发送探测报文,若发送了 10 个探测报文(每个相隔75秒)还没有响应,则关闭连接。
时间等待定时器:在连接终止时使用。当客户端向服务端发送最后一次报文确认时,会等待 2*MSL(报文寿命)后再关闭连接
TCP 差错控制机制的实现:检测、确认、重传实现。
TCP 用以字节为单位的滑动窗口协议来控制字节流的发送、接收、确认与重传:
发送方 TCP 维护一个发送缓存,用来存储应用程序准备发送的数据,同时对发送缓存设置一个发送窗口,只要这个窗口值不为 0,就可以发送报文段;
接收方 TCP 维护一个接收缓存,用来存储接收到的字节流,等待应用程序的读取,同时设置一个接收窗口,窗口等于接收缓冲可以接收的字节流大小;
接收方通过 TCP 报头通知发送方已经正确接收的字节号与发送方还能继续发送的字节数;
接收窗口的大小却决于接收缓存剩余空间的大小、应用程序读取字节流的速度;而发送窗口的大小取决于接收窗口。
虽然 TCP 面向字节流,但却不可能对每个字节都进行确认。而是将数据打包成段,封装成报文,通过报头的“序号”来标识发送的字节,用“确认序号”来表示哪些字节已被正确接收。
为达到利用滑动窗口协议控制差错,TCP 引入了“传输的字节流状态”的概念:
第1类:已发送且被确认的字节;
第2类:已发送但没有被确认的字节;
第3类:尚未发送但接收方 已准备号接收的字节;
第4类:尚未放松且接收方没有做好准备的字节。
发送方能够发送字节流的大小取决于发送窗口的大小。
发送方可立即发送可用窗口的字节,则第3类字节就会变为第 2 类字节,等待确认。
接收方发送确认报文报文,确认序号为 20~25 的字节(确认序号为26),若保持发送窗口不变,则窗口向左划,并将进入窗口的第 4 类字节变为第 3 类字节。
若传输过程中丢失了报文段,就会造成字节流序号不连续,而发送方对字节流序号不连续的处理方式有两种:
若采用回退的方式处理接收的字节流序号不连续,则需要再丢失中间报文段时,不管之后的报文段是否被正确接收,都会从该中间报文段的第1个字节序号开始,重发所有的报文段,这种方式是很低效的。
选择重发 SACK,当接收方收到与前面接收的字节流序号不连续的字节时,若这些字节的序号都在接收窗口之内,则首先接收这些字节,然后将丢失的字节流序号通知发送方。发送方只需重发丢失的报文段即可,而无需重发已经接收的报文段。
用来处理报文确认与等待重传的时间。当发送方 TCP 发送一个报文时,会将它的副本放入重传队列,同时启动一个重传定时器。
若在重传计时器归0前收到确认,表示该报文传输成功;若在计算器归0前未收到确认,则报文传输失败,准备重传报文。
每个 TCP 都要维持一* R T T RTT RTT 变量,即估算的往返时延,自适应重传定时是基于这个 R T T RTT RTT 进行的。
重传时间的计算公式为: T i m e o u t = β ∗ R T T Timeout=\beta * RTT Timeout=β∗RTT,其中 β β β为大于1的常量加权因子, R T T RTT RTT 为估算的往返时间;
而RTT的计算公式为: R T T = α ∗ 旧 R T T + ( 1 − α ) ∗ 最新 R T T 测量值 RTT=\alpha * 旧RTT+(1-\alpha) *最新RTT测量值 RTT=α∗旧RTT+(1−α)∗最新RTT测量值,其中 α \alpha α决定了 R T T RTT RTT 对时延变化的反应程度
流量控制的目的控制发送方的发送速率,使之不超过接收方的接收速率,防止因接收速率过低而出现报文丢失。TCP 可利用滑动窗口协议实现收发双方的流量控制。
若接收方应用层从缓存中读取字节的速度大于或等于字节达到的速度,那么接收方会在确认报文中发出非零窗口通告(即窗口字段为 0 的 TCP 确认报文);
若发送方发送的速率较快,导致接收缓存溢出,则接收方会发出零窗口通告(即窗口字段非0的TCP确认字段);
当发送方接收到零窗口通告时,停止发送,直到收到下一个非零窗口通告为止;
接收方发送的“窗口”通告即根据自己的接收能力给出一个合适的接收窗口,并将其写入到TCP的报头当中,通知发送方。流量控制过程中,接收窗口又称通知窗口。
当接收方缓存区因应用层的读取由满变为未满,则接收方会发送一个额外的确认,告知发送窗口的大小,因而不用担心接收缓存空出来时发送方仍不发送数据。
通过通知窗口可以有效保证接收缓存不会溢出。
若下一个“非零窗口”通告丢失,为了防止发送方无休止的等待,协议引入了一个坚持定时器。
当发送方收到一个零窗口通告时,就启动一个坚持定时器,当坚持定时器到时而未收到接收方的确认时,就会发送一个特殊的探测报文。这个报文只有一个字节,包含一个序号,但这个序号不需要确认,且接收方计算其他数据的确认时,该序号也会被忽略。该报文的作用是:提示接收方确认已丢失,请重传。
应用进程将数据传送到 TCP 的发送缓存后,由 TCP 协议来负责整个传输过程。以下算法用以解决应用进程传输数据到发送缓存与从发送缓冲发送数据至接收方的时间差问题:
发送方第一次只发送1字节,其他字节放入缓冲区;当第一个报文应答确认时,再把缓冲区中的数据放在第二个报文段中发送,这样一边等待确认,一边缓存数据,能够有效提高传输速率;
当缓存的数据字节数达到发送窗口的1/2或接近最大报文长度(MSS)时,立即将它们作为一个报文段发送。
Nagle算法解决数据每次以1字节进入传输层,相当于将 TCP 弱化为一个停止等待协议:
直接发送数据的情况:
- 发送缓冲区的数据大到MSS时
- 发送缓冲区的数据达到发送窗口的一半时
- 发送方不在等待 ACK 确认(新的发送阶段)
Clark算法用于解决 “糊涂窗口综合征”:接收端应用进程处理接收缓冲区数据很慢(每次1B),使得通知窗口过小,造成传输效率过低:
TCP 的拥塞控制方法分为:慢开始、拥塞避免、快重传与快恢复。
拥塞控制用于防止过多的报文进入网络而造成路由器与链路过载。网络出现拥塞的条件是:
∑ 对网络资源的需求>网络可用资源 \sum对网络资源的需求 > 网络可用资源 ∑对网络资源的需求>网络可用资源
其中:
负载:单位时间内进入网络的报文数
吞吐量:单位时间内通过网络输出的报文数
由图可知:
无拥塞控制:在达到饱和点之后,吞吐量会随着网络负载的增加而减少
理想的拥塞控制:在网络负载达到饱和点之前,网络吞吐一直保持线性增长;达到饱和点之后吞吐量保持不变;
实际的拥塞控制:在网络负载增长的初期,由于要在拥塞控制过程中消耗一定资源,因此其吞吐量小于无拥塞控制状态;但它可以在负载持续增长的过程中,通过限制进入网络的报文或丢弃部分报文,使得整个系统的吞吐量逐渐增加,而不出现下降。
TCP 滑动窗口是实现拥塞控制最基本的手段(也是流量控制的基本手段)。发送方发送数据时,即要考虑接收方接收能力,又要使网络不要发生拥塞:
发送窗口 = M i n ( 通知窗口,拥塞窗口 ) 发送窗口 = Min(通知窗口,拥塞窗口) 发送窗口=Min(通知窗口,拥塞窗口)
其中:
通知窗口:是由接收方根据自己的接受能力判断的窗口值,它来自接收方的流量控制。接收方将通知窗口值放在TCP报头中,传送给发送端。
拥塞窗口:发送方根据网络拥塞情况得出的窗口值,它来自发送方的流量控制。发送方在确定拥塞窗口大小时,可以使用慢开始与拥塞避免算法。
在一个 TCP 连接中,发送方维持一个拥塞窗口的状态参数。拥塞窗口的大小根据网络情况动态调整。只要网络没有出现拥塞,发送方就逐渐增大拥塞窗口;当出现拥塞时,拥塞窗口就立即减少。
慢开始与拥塞避免算法中,网络是否出现拥塞是根据路由器是否丢弃分组来确定的。(这里假设分组丢失不是由于物理层传输差错造成,而是由于分组传输的总量较大,造成路由器负载过重而导致的。)
当发送方开始发送数据时,采用试探性的方法,由小到大逐步增大拥塞窗口:
将从发送方发送报文到接收方,接收方在规定时间内返回确认报文作为一个往返。
发送方建立 TCP 连接时,将“慢开始”的初始值设置为1(最大报文长度,MSS)
第一个往返中,将拥塞窗口设置为 2,然后向发送方发送两个最大报文长度的字节。(即两个报文)
若接收方每次都能在定时器允许的往返时间 内返回确认,表示网络没有出现拥塞,发送方的拥塞窗口按二进制指数方式增长;
若没有在规定时间内收到确认,则表示网络出现拥塞,将 SST 值设为出现拥塞的 cwnd 值的一半,并将 cwnd 设置为1,进入下一轮的慢开始过程。
这里需注意:
- 虽然拥塞窗口控制的是发送数据的字节数,但由于每次窗口的递增都是以 MSS 为单位,且 MSS 为一个TCP报文的最大长度,故可以将拥塞控制理解为控制报文发送的数量。
- 每次发送的往返时间(RTT)是不同的:
若在某次往返过程中拥塞窗口值为N,则发送方只有在连续发送的N个报文都收到确认之后,才能判定网络没有出现拥塞。因此在拥塞控制过程中每一个往返过程的往返时间是从连续发送浮动个报文段到接收所有报文段的确认所需要的时间。往返时间的长短取决于连续发送报文段的多少。
- 慢开始阈值(SST):为避免拥塞窗口增长过快引起的网络拥塞所定义的参数,对于拥塞窗口与慢开始阈值间的关系可以有:
- 当 cwnd < SST 时,使用慢开始算法
- 当 cwnd > SST 时,停止使用慢开始,转而使用拥塞避免算法
- 当 cwnd = SST 时,既可以使用慢开始,也可以使用拥塞避免
在慢开始阶段,若出现拥塞,则发送方可以将慢开始的阈值SST设置为出现拥塞的cwnd值的一半。
- 实际上在慢开始中,发送方的拥塞窗口并不是指数增加的,而是每当发送方收到一个报文段的确认,拥塞窗口就增加一个 MSS 大小;但在理想情况下,每个报文的确认都无延时,相当于每个 RTT 后将拥塞窗口乘 2:
当 cwnd>SST 时,停止慢开始过程,转而进入拥塞避免。
该算法采用每增加一个往返时间就将拥塞窗口值加1个MSS。即在拥塞避免算法阶段,拥塞窗口呈线性增加的规律缓慢增长。
且与慢开始一样,只要发现接收方没有按时返回确认就认为出现网络拥塞,将 SST 设为发生拥塞时 cwdn 的一半,将当前 cwnd 设为1,重新进入下一轮慢开始过程。
下面是一个使用慢开始与拥塞避免进行拥塞控制的过程:
在 慢开始 与 拥塞避免 的基础上,还提出了 快重传 与 快恢复。
在发送了 N 个报文后,若缺少了少数报文的返回确认,则接收方应尽快连续发送三次缺失报文之前报文的重复确认,要求尽快尽快重传未被确认的报文。即不能因为某些报文确认的缺失就判断网络出现拥塞:
快恢复与快重传配合,它规定:
当发送方收到第 1 个“重复确认”时,立即将 cwnd 设置为最大拥塞窗口值的 1/2,并执行拥塞避免算法,拥塞窗口按线性方式增长;
当发送方收到第 2 个“重复确认”时,立即减少 cwnd 的值,执行拥塞避免算法,拥塞窗口按线性方式增长;
当发送方收到第 3 个“重复确认”时,立即减少 cwnd 的值,执行拥塞避免算法,拥塞窗口按线性方式增长;
RST 指复位字段,当 TCP 发生错误时发送的一个复位报文,要求立即关闭 TCP 连接,产生 RST 分节的三种情况是:
接受到目的为某端口的 SYN 分组,但该端口上并没有正在监听的服务器;
TCP 接收到一个不存在的连接上的分组;
TCP 想取消一个已有连接。