TCP协议
INFO
尽管与UDP使用相同的网络层(IP),但是TCP向应用层提供与UDP完全不同的服务:一种面向连接的、可靠的字节流服务
TCP报文头部结构

- 一个IP地址和一个端口号也称为一个插口(
socket,最早出现在RFC 793中),插口对socket pair(包含客户IP地址、客户端口号、服务器IP地址和服务器端口号的四元组)可唯一确定互联网络中每个TCP连接的双方 - 序号用来标识从TCP发端向TCP收端发送的数据字节流,它表示在这个报文段中的第一个数据字节,序号位数用尽后又从0开始
- 当建立一个新的连接时,
SYN标志变1。序号字段包含由这个主机选择的该连接的初始序号ISN(Initial Sequence Number)。该主机要发送数据的第一个字节序号为这个ISN加1,因为SYN标志消耗了一个序号 - 确认序号包含发送确认的一端所期望收到的下一个序号。因此,确认序号应当是上次已成功收到数据字节序号加1。只有
ACK标志为1时确认序号字段才有效 - 首部字段的长度是可变的。这个字段占4bit,因此TCP最多有60字节的首部
- TCP的流量控制由定义的窗口字段大小提供,单位为字节B
- 校验和覆盖了TCP的首部和数据,是强制性的字段,由发端计算和存储,由收端验证
- 紧急指针是一个正的偏移量,和序号字段的值相加表示紧急数据最后一个字节的序号(
URG置1时有效)

MSS(Maximum Segment Size)
MSS为最大报文段长度,在TCP首部的选项字段中定义,表示一个报文段能承载的最大数据长度的参数
MSL(Maximum Segment Lifetime)
MSL即报文最大生存时间(Maximum Segment Lifetime),在TCP四次挥手中客户端回复断开连接的ACK帧后经过2MSL才真正断开连接,这一段时间称为TIME_WAIT
四次挥手为什么要设置TIME_WAIT状态?
防止历史连接中的数据被后面相同的 socket 四元组错误接收,比如第二次挥手中服务端回复的 ack 延迟了,在关闭连接后又发送给客户端,则会发生异常
保证被动连接的一方能被正常关闭,比如第四次挥手的 ack 丢失了,服务端重发 FIN ,如果没有TIME_WAIT状态则客户端直接关闭连接,服务端重发的 FIN 也会被客户端判定为错误并返回 RST
TCP连接过程
TCP连接需要三次握手,断开需要四次挥手,这是因为TCP的半关闭(全双工的每个方向必须单独地关闭)
四次挥手中间的两次通常不能合并,但当服务端没有数据要发送时,可以合并变成三次挥手
为什么TCP断开连接需要四次挥手而不是三次?
使用四次挥手是确保服务端能正常结束数据的发送,第二次挥手和第三次之间服务器会将剩余的数据发送给客户端
但是如果服务器此时也正好没有数据需要发送,则四次挥手可以合并中间两次变为三次挥手
为什么一定要三次握手,两次不行吗?
不能使用二次握手的原因是无法确定是否有数据要发送,从而可能浪费网络资源
比如A向B发送了一个SYN = 1想要建立连接,但是这个数据传输过程中丢失了
于是A超时重传了一个,B收到后返回一个ACK就开始传输了,传输完后,第一次发送的SYN不知道啥情况又成功传到了B
B以为A要发数据了就又给A响应一个ACK并等待A发送,但是由于是二次握手,A收到这个ACK后并没有鸟他,然后B就被鸽了,一直等待从而造成资源浪费
可靠传输

超时重传机制

超时重传机制在发送端加入计时器RT0,当计时器经过RT0的时间后则会触发重传
RT0 的选值十分重要,如果太大会导致重传慢网络效率低;如果太小则有可能还没来得及收到确认就重传,重传过多会导致网络拥塞从而引发更多的重传
快速重传机制
ack报文表示接收方期望下一个收到的报文,如果发送方连续三次都收到了同样的ack,表示这个报文需要重传了
SACK机制
SACK 是TCP实现重传的一种机制,需要在TCP头部选项字段中加入 SACK (须通信双方都支持)
它可以将已收到的数据信息发送给发送方,这样发送方就知道哪些数据没有发送成功然后重传,达到只重传丢失的数据的目的
TIP
D-SACK即Duplicate SACK ,它能告诉发送方哪段数据被接收方重复接收了
滑动窗口
TCP头中的 Window 字段定义了窗口大小,窗口的设定大小就是可以连续发送数据的最大值,在一个窗口内发送方不用等待 ack 便可以发送下一个数据报,并且如果窗口内有一个数据报的 ack 丢失,可以通过接受后面数据报的 ack 来判断前面的数据是否被接收,这就是累计应答机制


发送窗口收到确认帧的数据会扔出窗口缓存,窗口向未发送的数据滑动
接收窗口的大小也不是一成不变的,当接收方的应用进程读取数据的速度非常快的话,这样的话接收窗口可以很快的就空缺出来,新的接收窗口大小通过 TCP 报文中的
Windows字段来告诉发送方当窗口为0时,接收方若处理完数据会发送一个
ack并包含扩大的窗口值,告知发送方新的窗口大小,如果这个报文丢失了,那双方会陷入死锁相互等待,所以发送方当收到0窗口通知时会启动一个计时器,超时就会发送一个窗口探测报文询问
TIP
糊涂窗口综合症是当接收端忙碌,来不及接收窗口里的数据,则窗口会越来越小,最后如果接收方腾出几个字节发送方就会马上发送,单算TCP+IP头就有40B,为了传输这几个字节开销太大了
流量控制
当接收端的应用程序没有及时读取缓存时,接收窗口会缩小,同时通告发送端也缩小发送窗口
如果应用程序始终不从缓冲区读取数据,则会导致窗口收缩为0,则窗口关闭
而且如果操作系统繁忙也会导致接收缓存减小
WARNING
TCP规定不允许缓存和窗口同时减小,这样可能会导致大量丢包
拥塞控制
拥塞控制是TCP在网络拥堵时的自我牺牲,它由发送方维护一个拥塞窗口,发送窗口取拥塞窗口和接收窗口中的最小值。拥塞窗口包括四个算法:
- 慢启动:当发送方每收到一个
ACK,拥塞窗口cwnd的大小就会加 1,直到达到慢启动门限ssthresh (slow start threshold),之后就使用拥塞避免算法 - 拥塞避免算法:每当收到一个
ACK时,cwnd增加1/cwnd,即每完成一个拥塞窗口长度数据的发送,窗口才会+1,当窗口越来越大直到发生网络拥塞则进入拥塞发生算法 - 拥塞发生算法:分为超时重传和快速重传两种,发生超时重传则从1重新慢启动,发生快速重传则将拥塞窗口减半并将慢启动门限设置为拥塞窗口值,接着进入快速恢复
- 快速恢复:拥塞窗口变为
ssthresh + 3(意味着收到了3个数据包)
