TCP协议
参考:https://www.bilibili.com/video/av9876107?from=search&seid=5217429010533979778
TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、点到点、基于字节流、可以提供全双工的传输层通信协议。
TCP报文首段格式
TCP报文首段由20个字节的固定首部和可变长度两部分组成
固定首部
- 16位的目标端口和源端口
- 四个字节的序号:TCP的数据部分分成数据包分段发送,序号指的是发送的每一个数据包的首个数据在整个数据段中的字节号
- 四个字节的确认号:接受方收到数据包后,去掉序号,加上确认号:让发送方发送的下一个数据包的起始数据字节号
如图所示,XP为客户端,Web为服务器。源端口、目标端口、序号、确认号如图所示。
- 4位数据偏移:用来记录TCP报文段第多少个字节以后是数据部分,每一个数代表四个字节,偏移量最多60个字节,因此可变部分最多40个字节
- 6位保留:未用到的6位二进制
- 6位标志位
- 16位窗口:建立连接时协商接收缓存的大小,告诉对方自己剩余的可用缓存区大小,传送时要求发送缓存<=接收缓存
- 16位校验和:TCP校验和是端到端的校验和,由发送方发送,接收方进行验证,判断数据是否有所改动,在TCP报文段的前面加上12字节的伪首部,伪首部的格式与UDP协议相同,把第四个字段的17改为6
- 16位紧急指针:只有在优先传送标志URG=1 时有效,指明了紧急数据结束的位置,如50,则表示1-50位为紧急数据
可变选项部分
规定接收最大数据报长度(MSS),接收时是否支持选择性确认SACK,时间戳等,不够四个字节则填充
TCP传输连接管理
TCP运输连接分为三个阶段:连接建立、数据传送、连接释放。TCP连接的建立都是采用客户-服务器方式。客户端:主动发起建立连接的应用进程(PC),服务器:被动等待建立连接的应用进程(web)。
TCP的连接建立也称为握手,使用三次握手建立连接,即建立三个报文段。
如图所示,客户端A主动向服务器B发起连接。开始时进程A和B都先创建传输控制块TCB,服务器B的进程处于Listen状态。
TCP的连接建立
第一次握手
客户端A向服务器B发出请求连接报文,同步标志位SYN=1,ACK=0,由于此时B未向A传输数据,因此序列号无效(随机初始化),A的数据段起始字节号为x,客户端A进入SYN-SENT状态。SYN=1 报文段不能携带数据,但是需要消耗一个序号。
第二次握手
服务器B接收到A发出的请求报文后,B的状态变为SYN_SENT,B向A发出同意连接,同步标志位SYN=1,确认号ACK=1,且选择初始序号y,同时确认为x+1,此时B发给A的报文也不能携带数据,但也需要消耗一个序号。
第三次握手
客户端A接收到B发送的报文后,还需要给B发送一次确认信号。此时同步标志位SYN = 0,确认号ACK = 1,序号为x+1,确认号为y+1,此时报文段可以携带数据,但是不携带则不消耗序列号(下一个报文段的序列号仍然是x+1)。这时TCP连接已建立,A进入ESTABLISHED状态,当B收到A的报文后,B也进入ESTABLISHED状态。
为什么要用三次握手
A发送建立连接请求,B发送接受连接请求。后面直接开始发送数据不可以么?假设A第一次给B发送了一个请求连接的报文,但是速度很慢,B很久没有收到,A再次给B发送了个请求连接报文,这次发的很快,B很快收到了并同意连接,A收到了B的报文,二者建立连接。一段时间后B接收到了A第一次发送的请求连接报文,B同意连接并向A发送报文,建立了新的运输连接,由于A已经收到了B的报文,因而不会再向B发送数据,因而B一直等待A发数据,这样会浪费B的很多资源。如果多一次确认,由于B没有收到A的确认报文,因而不会等待着A发数据。
TCP的连接释放
TCP连接的释放采用四次挥手,如图所示,A和B都处于ESTABLISHED状态。
第一次挥手
客户端A的应用进程向服务器B发送进程释放报文,FIN = 1,序号seq = u(上一传送的数据最后一个字节号+1),并停止发送数据,关闭TCP连接。A的状态变为FIN-WAIT-1,并等待B的回复。(FIN数据报不携带数据)
第二次挥手
B接收到A的请求,向A发送数据,确认标志ACK = 1,序号seq = v,为上一次传送数据的最后一个字节号+1,确认号为u+1(虽然FIN不携带数据,但是消耗一个序列号),B的状态变为CLOSE-WAIT等待关状态。A接收到B的报文,进入FIN-WAIT-2状态。此时TCP的连接只释放了一半,因为B仍然能向A发送数据。
第三次挥手
如果B不需要再向A发送数据,则B向A发送FIN报文,FIN = 1,确认标志ACK = 1,序列号seq = w,为为上一次传送数据的最后一个字节号+1(第三次挥手与第二次挥手之间B可能会向A发送数据),确认号ack = u+1,此时B的状态变为LAST-ACK,停止向A发送数据。
第四次挥手
A接收到B的连接释放报文,向B发送确认报文,确认标志ACK = 1,序号seq = u+1,确认号ack = w+1(B的FIN报文消耗一个序号),此时A的状态变为TIME-WAIT,等待2MSL后关闭,B接收到A的确认报文后变为关闭状态。(因而B结束TCP连接的时间比A早)
A接收到B的连接释放报文后不立即关闭的原因有两点:① 若B未接收到A的ACK确认报文,B会再向A发送一次连接释放报文,A在2MSL内接收到该报文,再次重传确认报文。② 防止和建立连接时相同情况:即长时间的请求报文由出现在连接中。
TCP可靠传输
TCP使用以字节为单位的滑动窗口技术实现可靠传输,避免传输数据时发生错误。如图所示,A向B发送数据,在建立连接时双方会协商各自的发送缓存。而滑动窗口是根据发送缓存确定,A可以将自己窗口内的数据连续发送出去,因此发送方的发送窗口不得大于接收方的发送窗口。窗口大小会随着网络的拥堵程度而改变。有下图中A最多将窗口内的20个字节发送出去,未接到B的确认之前窗口不得移动。当B收到了A发送的1-6字节数据后,会向A发送确认信息,确认号为7,告诉A收到了1-6个字节。
当A收到了B的确认消息后,A的滑动窗口会向后移动到7,能够继续发送数据,而发送缓存也会删除1-6字节的数据,如下图所示。
当B接收到A的确认信息后,B的滑动窗口也会后移。,如下图所示。
当数据在传输的过程中丢失时,如下图所示,7-9字节的数据未收到,但10-12的数据收到了,则B会向A发送一个选择确认号,让A只需重发7-9的数据,无需再发10-12的数据。
TCP流量控制
如果发送端发送数据速度过快,接收方来不及收取,则很容易造成数据丢失,TCP使用滑动窗口来实现流量控制。
在建立连接时,B向A发送Ack = 0,rwnd = 10,告诉A自己的接收窗口为10,A接收到后也设置自己的发送窗口(小于B的接收窗口)。A开始向B发送数据,如下图所示。
发送的途中5-6字节的数据丢失,此时B改变自己的接收窗口大小,Ack= 5,rwnd = 8,B接收到后也改变自己的发送窗口为8,且重发数据5-6,
B接收到5-12字节的数据后,B改变自己的接收窗口的大小为2,发送Ack = 13,rwnd = 2,。
A接收到后改变发送窗口大小为2,发送13-14字节数据
此时B处理缓存中的数据,将滑动窗口变为0,向A发送Ack = 15,rwnd = 0,A接收到后,发送窗口大小也为0,则不再发送数据。
处理完数据后,B的窗口又变为10,继续发送数据,往复循环。
A的窗口大小为0时,为了避免B的窗口信息传送时丢失,TCP为每一段连接设置了一个持续计时器,收到零窗口通知时,启动持续计时器,A发送一个探测报文,若窗口仍是零,则重置计时器直到窗口不为0.
TCP拥塞避免
与前面的两台计算机之间的数据传呼不同,TCP拥塞控制是指网络上的一组计算机避免拥塞,当需要的资源总和大于可用资源时,就会产生资源拥塞。TCP主要采用基于窗口的方法避免拥塞。
如下图所示,横坐标为输入负载,即单位时间输入给网络的分组数据数,纵坐标为吞吐量,即单位时间输出的分组数据数,在理想状态下,当输入数据大于网络能处理的最大负载时,多于的数据会被直接丢弃,因而继续增加负载,吞吐量不会再增加。但是实际上,随着输入数据量的增大,已经有一部分的数据包被丢弃。即增大负载会降低网络分发数据的能力,当负载增大到一定程度,网络会无法传送数据,产生死锁。
慢开始算法
如图所示,发送方有一个拥塞窗口cwnd,表示每次发送的数据包数,开始时由于不知道网络情况,设为1,即每次发送一个数据包。如果未产生堵塞,则增大指数增大cwnd,cwnd = 2,每次发送4个数据包。如果依然没有产生拥塞,则继续增大cwnd = 4...直到cwnd达到慢开始门限,下图为16.此后不再按照指数增长,每次只加1,16,17,18...,当产生拥塞时,慢开始门限减半,变为8,拥塞窗口cwnd又变为1,如果未阻塞,则继续按照指数增长,到达慢开始门限后线性增长。。。
快重传和快恢复
快重传:当一次性发送M1-M5共五个数据包时,如果M1后面收到的是M3,则可以判断数据包M2丢失,此时接收方立即连续向发送方发送三个确认信号,让其重发M2,不再等到接收到M5再发重传信号。
快恢复:当发生拥塞时,拥塞窗口cwnd不降到1,而是减到新的慢开始门限(即原慢开始门限的1/2),然后继续线性增加。
由以上可知,发送窗口的上限值为 min(cwnd,rwnd)