Linux网络---传输层协议TCP(二)

1、保证报头和有效载荷分离的问题

4位首部长度

2、如何保证有效载荷数据部分应该交付给上一层的那一层协议呢?

答:16位目的端口号

1. 如何保证报头和有效载荷分离?(关于"4位首部长度")

IPv4 协议 中,正是通过首部中的 "4位首部长度"(Internet Header Length, IHL) 字段来保证分离的。它的工作机制如下:

  • 单位换算 :这个4位字段的长度单位是 4字节(32位) 。它能表示的范围是 0~15,换算成字节数就是 0 ~ 60 字节。

  • 固定与可变 :标准的IPv4首部固定长度为20字节(即该字段值为 5,因为 5 × 4 = 20)。如果包含"选项"字段,首部会变长(最大60字节)。

  • 分离逻辑(接收端操作)

    1. 网卡收到数据包后,IP协议栈首先读取该4位字段的值(比如读取到 6)。

    2. 将其乘以4,计算出报头的实际字节数(6 × 4 = 24 字节)。

    3. 总长度字段(IPv4首部另一个字段)指明了整个IP数据包的总长度。

    4. 总长度 - 首部长度,就能精准地计算出有效载荷(Payload)的起始位置和长度。

  • 异常保证 :协议规定,如果该字段值小于 5(即首部小于20字节)或大于 15(超过60字节),接收方会认为该数据包损坏,直接丢弃,不会进行后续解析,从而在物理层面杜绝了因首部长度错误导致载荷读取越界的问题。


2. 如何保证有效载荷交付给上一层的那一层协议?(关于"16位目的端口号")

你给出的答案"16位目的端口号"非常精准,但需要补充一个细节:IP层和传输层分工不同

为了让你彻底明白,我们分两步看:

  • 第一步(IP层):区分上一层是TCP还是UDP

    • 这一步不靠 端口号,而是靠IP首部中的 "8位协议号"

    • 接收端的IP层收到包后,查看该字段(例如 6 代表TCP,17 代表UDP,1 代表ICMP),然后将剥离了IP首部的"有效载荷"交给操作系统内核中对应的传输层协议模块(TCP模块或UDP模块)。

  • 第二步(传输层):区分具体交给哪一个应用程序(关键点)

    • 当TCP或UDP模块拿到数据后,此时才轮到 "16位目的端口号" 发挥作用。

    • 操作系统内核维护着一张 "端口号 ↔ 应用程序/进程" 的映射表(即套接字Socket绑定关系)。

    • TCP/UDP模块读取该16位目的端口号(例如 804433306),查询映射表,找到正在该端口上监听的特定进程(如Nginx、MySQL客户端)。

    • 最终,传输层将剩余的有效载荷精准地投递到该进程的接收缓冲区中。

PSH标志位

PSH提示接收端应用程序立刻从TCP缓冲区把数据读走 (PUSH)

    • 发送端 :当应用调用 send() 或等效方法并设置 PSH 标志(或由 TCP 栈自动设置,通常当发送缓冲区为空时)时,TCP 会立即构造并发送该报文段,而不等待更多数据填入缓冲区(Nagle 算法可能会抑制此操作,但 PSH 会覆盖它)。

    • 接收端 :当接收到带有 PSH=1 的报文段时,接收端 TCP 栈不会将其放入乱序队列等待更多分段以填满 MSS(最大报文段长度)或接收窗口。它会立即将目前为止接收到的所有缓冲数据向上推送到应用层(套接字接收缓冲区),实质上触发了应用的 read() 调用返回。

    • 重要提示:在现代操作系统中,PSH 标志很大程度上是"建议性"或"历史性"的。由于现代 TCP 栈使用快速路径和即时 ACK,PSH 的效果不如早期明显。然而,它本质上是对接收方"立即交付"的指令。

URG标志位

作用:紧急指针是否有效

我们可以理解紧急指针的位置,当作应用层缓冲区中的某一个偏移量。

1. 工作原理(发送端与接收端逻辑)

  • 发送端设置

    1. 应用层调用 send(..., MSG_OOB)(带外数据)发送紧急字节。

    2. TCP 协议栈将 URG 标志置 1,并计算偏移量。

    3. 关键计算:紧急指针值 = (紧急数据最后一个字节的序列号 - 当前报文段起始序列号)+ 1。

    • 举个例子:当前报文段起始序列号(Seq)= 100,你要紧急发送的字节是 'A'(占 Seq=100)。那么紧急指针的值就是 1(100 - 100 + 1 = 1),表示从 Seq=100 开始的 1 个字节 是紧急的。

    • 如果紧急数据持续到 Seq=105,指针值就是 6,代表 Seq=100~105 这 6 个字节是紧急的。

  • 接收端处理

    1. 网卡收到 URG=1 的报文段后,TCP 协议栈提取紧急指针值,计算出紧急数据的"终点边界"。

    2. 进入"紧急模式" :接收端 TCP 不再按顺序把数据递给应用,而是绕过接收缓冲区排队 ,直接把指针标记的那段数据(带外数据 OOB)推给应用进程,并发送 SIGURG 信号 (或通过 select/epoll 异常事件)通知应用程序"紧急数据到了"。

    3. 应用程序必须通过特殊的 recv(..., MSG_OOB) 来读取这段数据,不能用普通的 read()

2. 一个极其重要的"伪命题":紧急指针 ≠ 高优先级传输

很多人以为设了 URG 和紧急指针,数据就能"插队"更快到达对方。这是错的!

  • 在网络传输队列中,带 URG 标志的报文没有任何优先转发权(路由器不看这个标志)。

  • 它的"紧急"仅仅体现在接收端的处理策略 上:即到达接收端后,不排队等待后续乱序数据,直接呈递给应用层。

3. 现实中的残酷真相:现代编程几乎不用它

虽然这是 TCP 标准里的字段,但在今天的互联网开发中(尤其是 HTTP/HTTPS、微服务),16位紧急指针几乎没有用武之地,原因有三:

  1. 仅支持 1 个字节的带外数据(OOB) :TCP 标准规定紧急指针虽然能划一片区域,但实际可靠的带外数据(紧急数据)通常只保证传输 1 个字节。如果你想发一串字符串作为"紧急命令",根本传不完。

  2. 极易引发死锁 :如果紧急数据到达,但接收方应用进程忙于处理其他逻辑,长期不调用 MSG_OOB 读取,该紧急数据会一直占着连接状态,后续正常数据会被阻塞。

  3. 安全风险 :历史上著名的 Ping of Death 变种和某些老版本 Linux 内核漏洞,就是利用伪造的紧急指针(指向不存在的序列号)造成内核崩溃或缓冲区溢出。

为什么会有紧急数据呢?

答:

核心原因 :为了解决 TCP"按序交付"导致的队头阻塞 ,让控制指令(如 Ctrl+C 中断、FTP 中止)能绕过排队的大数据流,直接被接收端优先处理。

历史用途:Telnet 远程中断、FTP 文件传输终止。

确认应答机制

收到应答是对历史报文的可靠性保证,也就是在如上图的主机A收到第一次应答,是确定了数据(1~1000)已经发给主机B了。

超时重传机制

主机A发送数据给B之后, 可能因为⽹络拥堵等原因, 数据⽆法到达主机B;
如果主机A在⼀个特定时间间隔内没有收到B发来的确认应答, 就会进⾏重发;

但是, 主机A未收到B发来的确认应答, 也可能是因为ACK丢失了;

因此主机B会收到很多重复数据。那么TCP协议需要能够识别出那些包是重复的包,并且把重复的丢弃掉。

这时候我们可以利用前面提到的序列号,就可以很容易做到去重的效果。

那么,超时的时间如何确定?

最理想的情况下,找到一个最小的时间,保证"确认应答一定能在这个时间内返回"。但是这个时间的长短,随着网络环境的不同,是有差异的。如果超时时间设的太短,会影响整体的重传效率;如果超时时间设的太短,有可能会频繁发送重复的包。

TCP为了保证无论在任何环境下都能比较高性能的通信,因此会动态计算这个最大超时时间。

Linux中(BSD Unix和Windows也是如此),超时以500ms为一个单位进行控制,每次判定超时重发的超时时间都是500ms的整数倍。如果重发一次之后,仍然得不到应答,等待2*500ms后再进行重传。如果仍然得不到应答,等待4*500ms进行重传。依次类推,以指数形式递增。累计到一定的重传次数,TCP认为网络或者对端主机出现异常,强制关闭连接。

连接管理机制

accept不参与三次握手。双方close是在进行回收过程。

传的就是这个:

结论1:三次握手,每一次都是 从"发出"开始算的。双方TCP,是自主完成的。

为什么是三次握手?

答:1、为通信前,先建立信道,---阻碍通信情况(1、双方意愿2、网络不通畅)

验证全双工通信,以最小的成本验证网络通信问题

(双方得到了彼此的许诺)

2、验证双方通信意愿问题

四次挥手

1、四次挥手,双方都可以作为主动断开连接的一方。

2、一方断开连接的本质是: 一方不写了。不写数据,不认为把链接结构体管理fd释放了。

3、断开连接,不一定是彻底释放,不写用户数据,不代表不发送报头和标志位。

局部关闭fd的系统调用

listen :

#include <sys/socket.h>

int listen(int sockfd, int backlog);

没有accept,链接能够建立成功;接受端维持的暂时不用accept到应用的链接个数是有上限的。

backlog+1

相关推荐
石头猫灯1 小时前
WordPress wp2shell 漏洞链完整拆解流程
网络·数据结构·安全·web安全
吴声子夜歌1 小时前
Java面试题——基础(一)
java·开发语言
网安蟹佬霸1 小时前
OSINT开源情报收集实战:从信息搜集到资产测绘(2026最新万字保姆级指南)
前端·网络·安全·web安全·网络安全·开源
小庞在加油1 小时前
WinDbg实战:QT/跨平台项目死锁与无响应问题排查指南
开发语言·qt·windbg·工具
ltl2 小时前
序列化格式深度对比:Protobuf、FlatBuffers、Cap’n Proto
linux
lengjingzju2 小时前
编译与调试完全指南—第17章 总结
linux
ltl2 小时前
容器网络性能真相:veth vs macvlan vs eBPF 数据面
linux
Vae_Mars2 小时前
C#中的delegate委托
开发语言·c#
一直走下去-明2 小时前
简单的http抓包解包完整代码
开发语言·python