目录
[一. TCP协议格式](#一. TCP协议格式)
[1. 端口](#1. 端口)
[2. 序号](#2. 序号)
[3. 确认号](#3. 确认号)
[4. 数据偏移](#4. 数据偏移)
[5. 保留](#5. 保留)
[6. URG](#6. URG)
[7. ACK](#7. ACK)
[8. PSH](#8. PSH)
[9. SYN](#9. SYN)
[10. FIN](#10. FIN)
[11. RST](#11. RST)
[12. 窗口](#12. 窗口)
[13. 紧急指针,检验和](#13. 紧急指针,检验和)
[14. 选项](#14. 选项)
[15. 填充](#15. 填充)
[二. 确认应答机制](#二. 确认应答机制)
[1. 三次握手](#1. 三次握手)
[2. 四次挥手](#2. 四次挥手)
[3. TIME_WAIT 状态](#3. TIME_WAIT 状态)
[4. CLOSE_WAIT 状态](#4. CLOSE_WAIT 状态)
[三. 重传机制](#三. 重传机制)
[1. 超时重传](#1. 超时重传)
[2. 快速重传](#2. 快速重传)
[3. SACK 方法](#3. SACK 方法)
[四. 连接管理机制](#四. 连接管理机制)
[五. 滑动窗口](#五. 滑动窗口)
[六. 流量控制](#六. 流量控制)
[七. 拥塞控制](#七. 拥塞控制)
[1. 慢启动](#1. 慢启动)
[2. 拥塞避免算法](#2. 拥塞避免算法)
[3. 拥塞发生算法](#3. 拥塞发生算法)
[4. 快速恢复](#4. 快速恢复)
[八. 应答机制](#八. 应答机制)
[1. 延迟应答](#1. 延迟应答)
[2. 捎带应答](#2. 捎带应答)
[九. 面向字节流](#九. 面向字节流)
[十. 粘包问题](#十. 粘包问题)
TCP(Transmission Control Protocol,传输控制协议)是互联网中最核心的传输层协议之一,与 IP(网际协议)共同构成了 TCP/IP 协议族的基础(通常简称 "TCP/IP")。它提供了可靠的、面向连接的、字节流的通信服务,确保数据在网络中准确、有序地传输,是 HTTP、FTP、SMTP 等应用层协议的底层支撑。
一. TCP协议格式
TCP协议在接受到上层应用层的数据后,对应用层数据进行封装,与UDP的封装逻辑类似

1. 端口
端口分为源端口和目的端口,我们可以将要发送的数据理解为要发出去的快递,端口指明了发送双方的地址,端口大小为16位,可以表示从0到65535的数据范围。
2. 序号
32位4字节,用于标记字节流传递的位置,例如初始序号是从0开始发送,发送了100个字节后,最后一个字节序号变为99
3. 确认号
32位4字节,告知发送方已经接收到数据的范围,例如接收方接收到了1000-1099序号的内容,确认号返回1100表示已经接收到1100之前的内容。
4. 数据偏移
32位4字节,表示报文首部与数据部分偏移的字节数,例如数据偏移为3,那么首部大小就是 3 * 4字节偏移量
5. 保留
TCP报文暂未启用该位,保留6位为后续新功能特性添加而预留空间
6. URG
1位的控制标记位,用于标记需要紧急传输的数据,例如发送的报文起始序号为1000,发送50字节,前10字节是紧急信号,那紧急指针数值为10,紧急数据截止序号就是1000+10-1
7. ACK
1位的控制标记位,用于接收方回复发送方确认确认号有效,1表示有效,0表示无效
8. PSH
1位的控制标记位,翻译为push,让接收方立即提取缓冲区中的数据交给应用层
9. SYN
1位控制位,建立连接时使用,当syn=1 ack=0 时意味着发送建立连接的请求,syn=1 ack=1 时表示建立连接成功返回应答
10. FIN
1位控制位,发起连接关闭请求,是四次挥手的重要标志位
11. RST
1位控制位,当RST=1时,表示连接存在严重错误,需要重新连接
12. 窗口
16位4字节,由于接收缓冲区接收容量有限,接收方需要告知当下缓冲区还能容纳多少的数据上限,这样发送方可以动态的调整发送数据量
13. 紧急指针,检验和
紧急指针是一个16位4字节,与URG配合使用,用于告知紧急数据的位置(偏移量)。检验和是一个16位4字节,用于检验报文在发送过程中是否损坏,发送方会将数据量的总和取反相加得到一个数,接收方接收到数进行计算,若计算结果为0表示未损坏,否则直接丢弃报文
14. 选项
我们可以理解为TCP的补充扩展包,我们可以加上时间戳,或者告知接收方扩大窗口因子,发送最大报文长度等等
15. 填充
保证TCP报头首部是4字节的整数倍,由于一些选项字段可能导致首部不是4字节倍数,所以在末尾处填充保证4字节倍数
二. 确认应答机制
1. 三次握手
在网络中,我们要进行不同主机间的通信那么就需要建立连接,建立连接就需要两台主机达成共识,这个达成共识的过程最少的交流次数就是三次,这也为什么是三次握手的原因。我们将这种共识类比于我们找工作的过程,首先提交入职请求,hr审批后同意请求并且告知求职者已经接收到请求同意入职,求职者接收到hr信息回复马上入职。这样就是最快的达成共识的方式。接下来我们讨论内部细节
握手的本质就是报文的传输

首先客户端和服务端都处于CLOSE状态。服务端先进行LISTEN,监听某个窗口,客户端发送SYN请求建立连接,同时发送一个初始序号,客户端进入SYS_SENT状态。服务端接收到请求后,发送SYN请求给客户端,并且确认接收到客户端请求,服务端初始化自己的序号server_isn,并且确认序号填上client_isn+1,随后服务端进入SYN_RCVD状态。客户端接收到服务端连接信息后,回复服务端,在确认序号出填上server_isn+1,服务端进入ESTABLISHED状态,服务端接收到客户端确认进入ESTABLISHED状态。
三次握手完成后,就可以进行相互发送数据了。
三次握手可以避免历史连接的建立,因为可能存在网络延迟等原因导致报文没有第一时间传到服务端,服务端接收到报文响应后,若只进行两次握手,很可能导致连接到历史连接。
2. 四次挥手
TCP断开连接的方式是通过四次挥手

客户端发送关闭连接请求,将FIN置为1,客户端进入FIN_WAIT_1状态(等待对方确认);服务端接收到报文向客户端发送ACK确认收到,服务端进入CLOSED_WAIT状态(等待应用层处理数据);客户端收到ACK报文后进入FIN_WAIT_2状态(等待对方关闭);服务端处理完数据后,向客户端发送FIN报文,服务端进入LAST_ACK状态(等待最终确认);客户端接收到FIN报文后,向服务端发送确认ACK收到报文,并且进入TIME_WAIT状态(等待对方确认收到ACK);服务端接收到ACK报文后进入CLOSE状态完成关闭连接;客户端在2MSL后也完成关闭连接。
3. TIME_WAIT 状态
为什么在确认断开连接后还要等待2MSL的时间才进入CLOSE状态。MSL是TCP报文的最长生命周期,2个MSL意味着两个周期,我们保留了端口号,可以让尚未接收到的报文迟到的报文消失,若我们直接进入CLOSE状态,端口恰好被其他程序使用很可能导致未发送成功的报文被其他端口接收。同时2MSL也能保证ACK报文确认收到,若丢失还可以进行重传操作。
我们在平常测试时,不能短时间内多次使用一个刚刚使用过的端口
4. CLOSE_WAIT 状态
被动关闭方明确对方不会再发送数据,但被动关闭方当前可能还有未处理完成的数据,被动关闭方需要等待上层应用层进行close()调用。若应用层长时间未close(),会导致 CLOSE_WAIT 连接长期占用系统资源(如文件描述符、端口),最终可能引发 "too many open files" 等错误,导致新连接无法建立。
三. 重传机制
通常情况下,主机A传输数据(1~1000)给主机B,主机B确认收到数据确认应答(1001)传回给主机A,这样完成一次数据传输。但如果因为网络原因等外部因素导致数据没有送达该怎么办呢?
TCP 使用了重传机制针对不同情况进行不同重传
1. 超时重传
数据丢失通常有两种情况,一种是传输时丢失,另一种是应答时丢失。我们可以设定一个时间,当这个时间段内未收到应答就重新发送,这个时间由RTT算法进行计算。
2. 快速重传
快速重传机制不以时间为衡量标准,而是以数据应答为标准

如发送方发出5份数据,第一份送到了返回ACK2,第二份因为其他原因没有收到,随着第三份第四份第五分数据收到了,因为前面没有收到第二份数据,所以返回ACK2,这样发送端收到了三份ACK2。知道了前面第二份数据可能有丢失,所以会在定时器过期前重新传第二份数据。最后收到了第三第四第五分数据,返回ACK6。
快速重传机制在收到三份相同的ACK时,会在定时器过期前进行数据重传。
3. SACK 方法
在TCP头部选项中可以添加SACK,SACK可以将图发送给发送方,这样发送方就可以知道哪些数据接收到了,哪些数据没有接收到,这样只传丢失的数据即可

四. 连接管理机制
TCP通常情况都要经历三次握手四次挥手

客户端socket创建套接字,connect绑定服务端端口;服务端创建套接字,bind绑定套接字,listen监听,accept接收客户端端口。随后系统会自主进行握手,connect和accept返回。
传输信息时客户端写入write,服务端read数据确认返回。
最后断开连接,客户端主动close,服务端进入CLOSE_WAIT状态,等待数据处理完成执行close,最后四次挥手完成
五. 滑动窗口
TCP每发送一个数据都要进行一次确认应答,当上一个数据收到应答之后,才会继续发送下一个数据。这样就导致效率低下,性能差,一旦遇到传递时间往返长时,性能变差
于是我们就想能不能一次性发送大量的数据,这样就大大提高了效率
所以有了滑动窗口
在窗口大小范围内,数据包无需收到ACK确认就可以发送数据,窗口大小决定了发送的数据量大小。接收端通过报文告诉发送端自己缓冲区接收能力大小,发送端可以此为依据动态的调整发送量大小。

当前发送端窗口容量是4000字节,前1000字节已完成发送,窗口移动到1000-5000;主机B ACK确认收到主机A的报文后滑动窗口向前移动,移动到2000-6000范围,剩余的6000往后的数据是未接收的数据。
六. 流量控制
接收端处理数据的能力是有限的,一旦接收端发现当前缓冲区的数据快满了时,会调整自己的窗口大小,将调整窗口大小的报文发送给发送端,发送端就会减缓发送的速度,让接收端有更多的时间处理数据。当接收端缓冲区满了就会将窗口大小调整为0,此时发送端就不会给接收端发送数据。但是发送端会每隔一段时间给接收端发送一个探测报文,接收端收到报文后,会将自己动态的窗口大小再传回给发送端,这样发送端就能根据接收端的接收能力动态调整数据量了。
七. 拥塞控制
在网络出现拥堵时,我们发送的大量数据很可能导致丢包延时等问题,这样就需要发送方重新传递数据,消耗网络资源
为了根据实际的网络情况动态的控制发送数据量大小,于是就引入了拥塞机制。发送方会根据接收方报文应答的速度判断当前网络状况,接收端当前缓冲区大小判断发送数据量大小。
为了在发送方调节发送的数据量,这里引入了拥塞窗口 cwnd ,会根据网络状况动态的变化,当网络拥塞时该值变小,网络畅通时该值变大。
我们的发送窗口 swnd,接收窗口 rwnd,我们实际中 swnd = min(cwnd,rwnd),也就是两者取最小值。
下面是拥塞机制的四种算法
1. 慢启动
在刚开始时,TCP刚刚完成连接,需要一点一点的发送数据进行测试,慢启动意味着一点一点提高发送数据量大小。
一开始 cwnd = 1,在收到第一个ACK应答后,cwnd增加1,;在收到第二个ACK应答后,cwnd增加到 4 ,以此类推,以指数倍进行增长。

当然这样长迟早会超过接收端缓冲区的大小,所以我们要及时调整。
于是我们引入一个指标 ssthresh ,当拥塞窗口大于这个值时就减缓速度,小于这个值时继续以指数增长。
当 cwnd<=ssthresh时,启用慢启动算法;当cwnd>ssthresh时,启用拥塞避免算法。
2. 拥塞避免算法
当超过慢启动的ssthresh界限是,就会进入拥塞避免算法。
当进入拥塞避免算法后,每收到一个ACK,cwnd增加1/cwnd。也就是后续会慢慢的进行线性增长

对比慢启动,拥塞避免算法放缓了增速,当然这样一直增长也会达到上限,所以当第一次出现丢包时,需要对数据进行重传,就会进入拥塞发生算法
3. 拥塞发生算法
当网络出现拥塞进行重传,有两种机制;超时重传和快速重传
超时重传是将 ssthresh 设为 cwnd/2 ,然后将 cwnd 设置为1

重新回到1之后继续进行慢启动。
这样就导致数据传输的波动较大,容易造成网络卡顿,于是有了快速恢复算法
4. 快速恢复
快速恢复算法通常与快速重传同时使用,当发送方接收到三份同样的ACK报文时,说明当前网络状态是不错的,因为还是接收到了报文。
触发快速恢复后,首先 ssthresh = cwnd / 2 ,cwnd = ssthresh + 3;每收到一次重复的ACK时,cwnd + 1,当收到新的ACK后,cwnd重新变为ssthresh值,重新进入拥塞避免状态进行线性增长

八. 应答机制
1. 延迟应答
延迟应答是接收方优化网络资源的一种方式,接收方在接受到数据后不会立即回应发送方,而是等待一段时间再回复。例如接受缓冲区大小为1M,接收到了500K的数据,如果立即回复窗口大小就是500K,但我们传递信息的这段时间可能已经处理完了数据,也就是说我们并未完全使用完接收方的空间。所以接收方晚一点应答,发送方传递的数据包就会更大,可以减少网络中小数据包的数量,有效的优化了网络资源。
2. 捎带应答
网络通信需要一问一答,如果我们每次应答的时候只发送一个ACK确认报文就特别的浪费资源。我们可以将自己想发送给发送方的数据捎带传回,相当于搭上顺风车
九. 面向字节流
TCP是面向字节流的,接收方和发送方存在着接收缓冲区和发送缓冲区。当我们写入数据时,若数据过长,则会分多次写入缓冲区中,若数据过短,则会等待合适的数据长度后再进行发送。假如发送方调用了一次write写入100字节数据,但接收方可以调用100次read来读取数据,因为缓冲区的存在读写不需要一一匹配。
十. 粘包问题
UDP不存在粘包问题但TCP存在粘包问题
由于UDP是面向数据报发送的要么就接受到一份完整的数据报要么就接受不到。但TCP是面向字节流发送的,TCP机制可能为了提高效率会将数据合并在一起进行发送,接收方也可能一次提取或多次提取数据。
在读取TCP数据包时,通常包头前会有一个数据总量大小,读取了大小,对端就知道数据报的边界在哪,这样就解决了粘包问题。
TCP小结:
可靠性:
校验和,序列号,确认应答,超时重发,连接管理,流量控制,拥塞控制
提高性能:
滑动窗口,捎带应答,延迟应答,快速重传