【Linux】传输层协议TCP

报文的理解

1. TCP协议

1.1 TCP协议段格式

  • 源/目的端口号:表示数据是从哪个进程来,到哪个进程去
  • 32位序号/32位确认号
  • 4位TCP报头长:表示该TCP头部有多少个32位bit(有多少个4字节);所以TCP头部最大长度是60
  • 6位标志位:URG:
  1. URG(紧急):紧急指针是否有效,优先传输紧急数据,不用等缓冲区满
  2. ACK(确认) :确认号是否有效,绝大多数传输报文都带 ACK
  3. PSH(推送) :接收端立即从 TCP 缓冲区读取数据,不缓存等待
  4. RST(复位) :强制断开、重新建立连接,携带 RST 的叫复位报文段
  5. SYN(同步) :请求建立连接,三次握手用,带 SYN 的叫同步报文段
  6. FIN(结束) :本端请求关闭连接,四次挥手用,带 FIN 的叫结束报文段
  • 16位窗口大小
  • 16位校验和
  • 40字节头部选项

1.2 确认应答(ACK)机制

TCP将每个字节的数据都进行了编号,即序列号每一个ACK都带有对应的确认序列号,意思是告诉发送者:收到了哪些数据,下次从哪开始发

1.3 超时重传机制

  • 主机A发送数据给B之后,可能因为网络拥堵等原因,数据无法到达主机B
  • 如果主机A在一个特定时间间隔内没有收到B发来的确认应答,就会进行重发
  • 因此主机B会收到很多重复数据。TCP协议需要能够识别出那些包是重复的包,并且把重复的丢弃 掉,利用序列号,就可以很容易做到去重效果

TCP 超时时间怎么确定?

  • 超时时间以 500ms 为一个时间单位
  • 第一次超时:等待 500ms 重传
  • 还没收到应答:等待 1000ms(2×500ms) 再重传
  • 还没应答:等待 2000ms(4×500ms)
  • 后续按 指数 2ⁿ × 500ms 成倍拉长等待时间
  • 重传次数累积到上限后:TCP 判定网络故障 / 对端主机异常,直接强制断开连接

1.4 连接管理机制

在正常情况下,TCP要经过三次握手建立连接,四次挥手断开连接

其中三次握手本质其实是四次握手,TCP把「第二次握手的应答」和「服务端同步请求」合并捎带了,就变成三次握手

为什么要进行三次握手?

  • 以最小成本,100%确定双方通信意愿
  • 以最短方式,验证双方是支持全双工 的!

服务端状态转化:

  • CLOSED -\> LISTEN 服务器端调用listen后进入LISTEN状态,等待客户端连接
  • LISTEN -\> SYN_RCVD 一旦监听到连接请求(同步报文段),就将该连接放入内核等待队列中,并向客户端发送SYN确认报文
  • SYN_RCVD -\> ESTABLISHED 服务端一旦收到客户端的确认报文,就进入ESTABLISHED状态,可以进行读写数据了
  • ESTABLISHED -\> CLOSE_WAIT 当客户端主动关闭连接(调用close),服务器会收到结束报文段, 服务器返回确认报文段并进入CLOSE_WAIT
  • CLOSE_WAIT -\> LAST_ACK 进入CLOSE_WAIT后说明服务器准备关闭连接(需要处理完之前的数据);当服务器真正调用close关闭连接时,会向客户端发送FIN,此时服务器进入LAST_ACK状态,等待最后一个ACK到来(这个ACK是客户端确认收到了FIN)
  • LAST_ACK -\> CLOSED 服务器收到了对FIN的ACK,彻底关闭连接

客户端状态转化:

  • CLOSED -\> SYN_SENT 客户端调用connect,发送同步报文段
  • SYN_SENT -\> ESTABLISHED connect调用成功,则进入ESTABLISHED状态,开始读写数据
  • ESTABLISHED -\> FIN_WAIT_1 客户端主动调用close时,向服务器发送结束报文段,同时进入FIN_WAIT_1
  • FIN_WAIT_1 -\> FIN_WAIT_2 客户端收到服务器对结束报文段的确认,则进入FIN_WAIT_2,开始等待服务器的结束报文段
  • FIN_WAIT_2 -\> TIME_WAIT 客户端收到服务器发来的ACK结束报文段,进入TIME_WAIT,并发出 LAST_ACK
  • TIME_WAIT -\> CLOSED 客户端要等待一个2MSL(Max Segment Life,报文最大生存时间)的时 间,才会进入CLOSED状态

1.5 理解TIME_WAIT状态

当启动server绑定一个端口后,使用ctrl+c使server终止,马上再运行server会出现以下现象:

因为:虽然server的应用程序终止了,但TCP协议层的连接并没有完全断开,因此不能再次监听同一个的server端口

  • TCP协议规定,主动关闭连接的一方要处于TIME_WAIT状态,等待两个MSL的时间后才能回到CLOSED状态
  • 使用Ctrl-C终止了server,所以server是主动关闭连接的一方,进入TIME_WAIT

2MSL 的两个核心作用:

  • 历史残留数据,会在 2MSL 时间内**全部自然过期消亡。**否则会出现:客户端立刻关闭、马上重启连同一个端口,旧的历史残留报文可能误入新连接,导致数据错乱、脏数据混入
  • 如果这个最后 ACK 丢包了,服务端收不到 ACK会停留在 LAST_ACK 状态,超时后,会重发 FIN 报文。客户端收到后重新补发一次 ACK

解决TIME_WAIT状态引起的bind失败的方法:

  • 使用 setsockopt ()设置 socket 描述符的 选项 SO_REUSEADDR 为 1 ,表示允许创建端口号相同 但IP地址不同的多个 socket 描述符
cpp 复制代码
int opt =1;
setsockopt(
    listenfd,        // 要设置的 socket 文件描述符(服务端监听socket)
    SOL_SOCKET,      // 选项级别:socket 层
    SO_REUSEADDR,    // 选项名:允许地址复用
    &opt,            // 传入值:1=开启
    sizeof(opt)      // 值的长度
);

1.6 滑动窗口

一发一收的方式性能较低,那么一次发送多条数据,就可以大大的提高性能(其实是将多个段的等待时间重叠在一起了)

  • 窗口大小指的是无需等待确认应答而可以继续发送数据的最大值.
  • 发送前四个段的时候,不需要等待任何ACK,直接发送
  • 收到第一个ACK后,滑动窗口向后移动,继续发送第五个段的数据,依次类推
  • 操作系统内核为了维护这个滑动窗口,需要开辟发送缓冲区来记录当前还有哪些数据没有应答。只 有确认应答过的数据,才能从缓冲区删掉
  • 窗口越大,证明网络越好

丢包了怎么重传?

分两种情况:

情况一 :数据包已经抵达,ACK被丢了

部分ACK丢了并不要紧,可以通过后续的ACK进行确认

情况二: 数据包直接丢了

  • 当某1001~2000报文段丢失之后,滑动窗口左侧不向右移动,发送端会一直收到1001这样的ACK
  • 如果发送端主机连续三次收到了同样一个"1001"这样的应答,就会将对应的数据1001-2000重新发送
  • 这个时候接收端收到了1001之后,再次返回的ACK就是7001了,因为2001-7000接收端其实之前 就已经收到了,被放到了接收端操作系统内核的接收缓冲区中

这种机制被称为**"快重传"**

还有一个机制是**"超时重传"** :发送方每发送一个报文段,同时都启动超时计时器**(RTO), RTO 超时仍未收到 ACK**→ 认为丢包,立即重传该报文,并重置计时器

  • 网络轻度丢包 → 快重传快速恢复。
  • 网络严重故障、连重复 ACK 都没有 → 超时重传兜底

快重传和超时重传底层都是滑动窗口支持的

1.7 流量控制

接收端接收缓冲区容量有限,如果发送端发得太快,缓冲区被塞满,后续数据就会丢包,进而触发超时 / 快重传,降低传输效率

TCP根据接收端处理能力,限制发送端发送速率,防止接收缓冲区溢出,这个机制叫做流量控制

  • 窗口大小越大 → 接收端空闲缓冲区越多 → 发送端可以发更快,网络吞吐量越高。
  • 接收端缓冲区快满 → 把窗口调小,通过 ACK 通知发送端 → 发送端降低发送速度。
  • 接收端缓冲区完全满 → 窗口置为 0 → 发送端停止发送业务数据,但是需要定期发送一个窗口探测数据段,使接收端把窗口大小告诉发送端

接收端如何把窗口大小告诉发送端呢?16位窗口字段

实际上,TCP首部40字节选项中还包含了一个窗口扩大因子M,实际窗口大小是窗口字段的值左移M位

1.8 拥塞控制

虽然有了滑动窗口,但如果一次发送大量数据,仍然有可能引发问题

因为不清楚当前网络的情况,如果当前已经拥堵,则会直接加重网络拥塞,甚至丢包、超时重传,越传越堵

TCP 新增拥塞窗口cwnd ,连接初期先发少量报文段探路(慢启动),慢慢探测网络承载能力

1.9 延迟应答

如果接收数据的主机立刻返回ACK应答,这时候返回的窗口可能比较小

  • 例如:接收缓冲区总大小:1M,一次性收到:500K 数据
  • 马上回 ACK:此时缓冲区只剩 524K 空闲,通告窗口 = 524K,但应用层处理很快,10ms 就把 500K 读完了,缓冲区立刻又变回 1M 空闲
  • 缺点:立刻 ACK 报了一个偏小的窗口,浪费了接收端处理能力,发送方不敢多发,吞吐量上不去

可以稍微延迟一会

并不是所有 TCP 报文段都能无限延迟应答,有两大强制触发 ACK 的规则:

  1. 数量限制:每隔 N 个数据包,必须立刻应答一次,常规系统默认:N = 2,即收到第 2 个报文段时,必须回复 ACK,不再延迟
  2. 时间限制:就算数据包数量没凑够 N 个,超过最大延迟超时时间,也必须立即发 ACK。通用默认超时:200ms

1.10 捎带应答

客户端 / 服务端要发自己的数据 时,顺便把对对方的 ACK 确认 塞在同一个报文段里一起发,不用单独发一条纯 ACK 包,这就是捎带应答

1.11 面向字节流

创建一个 TCP Socket 时,内核会为这个连接分配两个独立的缓冲区:

  • 发送缓冲区:存放应用层要发送的数据
  • 接收缓冲区:存放从网络中收到、还未被应用读取的数据

这两个缓冲区,是 TCP 实现全双工、流量控制、可靠性传输的核心载体

  • 调用write时,数据会先写⼊发送缓冲区中
  • 如果发送的字节数太长,会被拆分成多个TCP的数据包发出
  • 如果发送的字节数太短,就会先在缓冲区里等待,等到缓冲区长度差不多了,或者其他合适的时机 发送出去
  • 接收数据的时候,数据也是从网卡驱动程序到达内核的接收缓冲区
  • 然后应用程序可以调用read从接收缓冲区拿数据
  • 另一方面,TCP的一个连接,既有发送缓冲区,也有接收缓冲区,那么对于这一个连接,既可以读数据,也可以写数据。这个概念叫做全双工

由于缓冲区的存在,TCP程序的读和写不需要一一匹配

1.12 粘包

TCP 协议头无 UDP 那样的报文长度字段,仅有序号字段。站在应用层的角度,仅读取到连续无分界的字节流,无法划分完整应用数据包边界,产生粘包

UDP 首部自带报文长度字段,数据有天然边界。并且一次性向应用层交付完整报文,不存在UDP粘包问题

如何解决粘包问题?手动定义数据包边界

  1. 定长包:每次按固定字节大小读取数据
  2. 变长包 - 包头标识:包头携带数据包总长度,接收端先读长度,再读取完整包体
  3. 变长包 - 分隔符:包与包之间约定特殊分隔符,保证分隔符不与正文内容冲突

1.13 TCP异常情况

  1. 进程终止:进程退出会自动释放文件描述符,内核依旧会发出 FIN 报文,四次挥手正常进行,和正常关闭连接流程无区别
  2. 机器重启:重启前系统会正常收尾,同样会触发FIN 关闭连接,效果和进程终止一致
  3. 机器掉电 / 网线断开:对方无任何报文发出,本地接收端内核仍认为连接存活 。若接收端执行写数据操作 ,立刻感知连接失效,直接收到 RST 复位 断开连接。若无读写操作,TCP 内置保活定时器,定期探测对方状态;多次无响应后,自动释放连接

应用层也可自定义心跳检测:如 HTTP 长连接定时探活、QQ 断线自动重连等机制

2. TCP总结

TCP既要保证可靠性,同时又尽可能的提高性能

可靠性:

  • 校验和
  • 序列号 (按序到达)
  • 确认应答
  • 超时重发
  • 连接管理
  • 流量控制
  • 拥塞控制

提高性能:

  • 滑动窗口
  • 快速重传
  • 延迟应答
  • 捎带应答

其他:

  • 定时器 (超时重传定时器,保活定时器,TIME_WAIT 定时器等)

3. TCP/UDP对比

虽然TCP 是可靠连接,但TCP 并不就优于 UDP,二者的优缺点不能简单、绝对地评判高低

TCP 侧重可靠性、有序性、不丢包、无重复,牺牲了部分传输速度和实时性

UDP 侧重传输速度快、开销小、实时性强,无连接、无握手挥手、无流量拥塞控制,允许少量丢包

基本上要可靠选 TCP,要实时、高速、广播选 UDP

4. 思考

如何用UDP实现可靠传输?

需要自定义协议,引入序列号、确认应答、超时重传、滑动窗口进行流量控制....

相关推荐
hj28625116 分钟前
Linux网络基础一
linux·运维
myenjoy_132 分钟前
串口采集与 Modbus RTU——字节流里的时间敏感博弈
网络·python·网络协议·tcp/ip
小义_41 分钟前
【Linux 1】
linux·运维·云原生·红帽
dxxt_yy43 分钟前
光伏风电组网调试优选,鼎讯信通 GN-W10A 网络综合测试仪全项检测
网络·能源·信息与通信
面向对象World1 小时前
Z8350 Broadcom SDIO网卡调试Ubuntu 22.04 Server版
linux·运维·ubuntu
是枚小菜鸡儿吖1 小时前
IT技术员远程修电脑用什么软件好?低延迟高清远控工具横评
网络·智能路由器·电脑
Irissgwe1 小时前
12、多路转接 select
linux·io多路转接·select
eam0511231 小时前
BGP反射器及联邦实验
网络
小子想咋滴1 小时前
bgp联邦实验
网络·智能路由器
无足鸟ICT1 小时前
【RHCA+】编辑多个文件
linux