当前位置: 首页 > news >正文

【Linux】TCP原理

📝前言:

这篇文章我们来讲讲TCP原理

🎬个人简介:努力学习ing
📋个人专栏:Linux
🎀CSDN主页 愚润求学
🌄其他专栏:C++学习笔记,C语言入门基础,python入门基础,C++刷题专栏


目录

  • 一,TCP协议格式
  • 二,确认应答机制
    • 1. 序号和确认序号
    • 2. 捎带应答
  • 三,超时重传机制
  • 四,连接管理机制
    • 1. 三次握手
    • 2. 四次挥手
  • 五,TIME_WAIT状态
    • 1. TIME_WAIT的作用
    • 2. 解决TIME_WAIT状态引起的bind失败
  • 六,CLOSE_WAIT状态
  • 七,滑动窗口
  • 八,拥塞控制
  • 九,延迟应答机制
  • 十,面相字节流(粘包问题)
  • 十一,TCP异常
  • 十一,报头的封包和分用
  • 十二,TCP和UDP的对比
    • 1. 核心特性对比
    • 2. 典型应用场景
    • 3. 用 UDP 实现可靠传输

一,TCP协议格式

在这里插入图片描述

  • 除去选项,前面的内容占20字节
  • 序号和确认序号后面重点讲解
  • 4位首部长度:用来标识TCP报头的长度(以四字节为单位)
  • URG … FIN:标记位
    • URG:紧急数据指示,让接收方优先处理
    • ACK:确认接收,同时携带 “确认序号”
    • PSH:即时通讯,让接收方 TCP 层收到数据后不缓存,直接推给应用层(避免消息延迟)(非重点)
    • RST:主动终止 “异常 / 无效” 的连接
      • 如:客户端向 “不存在的服务器端口” 发数据,服务器回复 RST=1,告知 “连接无效”,然后断开连接
    • SYN:请求建立连接,用于 TCP 连接的 “建立阶段”(如三次握手的前两次),此时报文不携带应用层数据
    • FIN:请求释放连接,用于 TCP 连接的 “关闭阶段”,表示 “没有更多数据要发送”
  • 16位窗口大小:用来进行“流量控制”,以保证效率。窗口大小==对方缓冲区剩余空间大小
  • 16位检验和:检测报文是否被损坏或篡改
  • 16 位紧急指针:配合URG,定位 “紧急数据” 的边界
    • 它是 “相对于当前序号的偏移量”,用于标记 “紧急数据的末尾位置”(即 “从当前序号开始,往后偏移 N 字节是紧急数据的结束”)

二,确认应答机制

1. 序号和确认序号

  • 序号:发送方对自己发送的字节流数据进行编号
    • 解决乱序问题:先发的数据可能后到,但是接收方可以根据序号重新排列组织数据
    • 支持超时重传机制(后面会重点讲解):发送方通过序号跟踪哪些数据已被确认,哪些需要重传
  • 确认序号:用于告知发送方 “期望接收的下一个字节的序号”(ACK 被设置成1
    • 累积确认机制(确认应答机制核心):如果应答方给发送方发送序号n+1,则代表前n条数据已经全部接收到了

序号是用来描述自身报文的,确认序号是对对方报文的回应

在这里插入图片描述
自己发出去的报文,只有收到对方的ACK应答以后,才能确保报文有效。(即:对于最新的报文,我们其实是无法确保有效性的)

2. 捎带应答

捎带应答机制就是指:接收方在应答的时候也可以发送自己的数据。
即:

  • 上图中,如果server 接受到了client的全部数据,应答报文中ACK置为1,且确认序号为301,此时自己还想发送100个字节的数据给client,则此时server的应答报文里的序号为1(如果上次收到的确认序号为1
  • client接收到server的这100个字节的数据以后,应答时同样可以带自己的数据。这时候应答的报文里,序号从301开始(代表自己要发送的数据的序号开始),确认序号为101(代表client收到了server的前100个字节)

三,超时重传机制

“报文丢包”场景:

  1. 报文真的丢了(真实丢包)
  2. ACK丢了(反馈异常)
  • 因此,为了防止反馈异常所造成的“误判”,每个报文发出去以后,都会有一个“计时器”,当计时器到时间了,则认为真丢包了,触发重传。
  • 超时以 500ms 为一个单位进行控制, 每次判定超时重发的超时时间都是 500ms 的整数倍(简单认为)
    • 如果重发一次之后, 仍然得不到应答, 等待 2500ms 后再进行重传,还不行就 4500ms,以此类推(指数退避策略,但是也有上限)
    • 累计到一定的重传次数, TCP 认为网络或者对端主机出现异常, 强制关闭连接

理解序号的去重作用:
如果是ACK丢了,则server就会收到client发来的重复数据,此时server就可以根据序号来去重。

四,连接管理机制

在这里插入图片描述

1. 三次握手

client和sever通信前,要先建立TCP连接,整个过程涉及三次握手(本质其实是四次,中间进行了合并)

步骤:

  • client给server发送建立连接的请求:SYN
  • server给client应答,同时,给client发送建立连接的请求:ACK + SYN
  • client给server应答:ACK

细节:

  • 原本需 4 次交互(Client 发 SYN→Server 回 ACK;Server 发 SYN→Client 回 ACK)
  • 在前两步骤,是不带应用层数据的。因为:TCP 连接在第三次握手完成后才被视为 “正式建立”—— 此时双方才确认彼此的 “序号、窗口大小” 等核心参数已同步,应用层数据的传输才有可靠基础(也就是说:前两步在做准备工作)
  • 三次握手的关键:用最短次数验证双向通信的可达性与参数同步

2. 四次挥手

四次挥手是释放连接的过程,TCP 是全双工通信(双方可同时发送和接收数据),所以四次挥手是双方都关闭发送和接收的过程。
下面以 client 主动给 server 发送断开连接的请求为例:

  • client 给 server 发送 FIN(代表已经无数据发送,请求释放连接),进入FIN_WAIT_1状态
  • server 给 client 发送ACK,server 进入CLOSE_WAIT状态。client 接收到ACK后,进入FIN_WAIT_2状态
  • 再由 server 给 client 发送 FIN,sever 进入 LAST_ACK状态
  • client 接收到后,再给 server 发 ACK,进入TIME_WAIT状态(重点)。server接收到ACK以后就立即进入CLOSED(释放连接),但是client要在TIME_WAIT状态并等待2MSL,才能断开连接

五,TIME_WAIT状态

1. TIME_WAIT的作用

(1)确保被动关闭方(服务器)能收到最后的 ACK

TCP 是 “可靠传输协议”,其可靠性依赖于 “确认机制”—— 任何发送的报文都需要对方确认才能确保未丢失。
在四次挥手的第四步中,主动关闭方(客户端)发送给被动关闭方(服务器)的最后一个 ACK 报文,是没有后续确认的(因为服务器收到 ACK 后会直接关闭,不会再回复)。如果这个 ACK 报文因网络延迟、丢包等原因未被服务器收到,服务器会在LAST-ACK状态下超时重发FIN报文。

此时,客户端若已直接关闭(无TIME-WAIT),就无法再接收服务器重发的FIN,更无法重新发送 ACK—— 服务器会因始终收不到 ACK 而一直卡在LAST-ACK状态,导致连接资源泄漏(服务器会持续占用该连接的端口、内存等资源,无法释放)。

而TIME-WAIT的等待时间(2MSL),恰好覆盖了 “报文最大可能的往返时间”:

  • 1 个 MSL:确保服务器重发的FIN报文能在网络中传输到客户端;
  • 第 2 个 MSL:确保客户端重新发送的ACK报文能传输到服务器。
    通过这 2 个 MSL 的等待,可 100% 确保服务器能收到最后的 ACK,正常进入CLOSED状态。

(2)避免 “旧连接的残留报文” 干扰新连接
TCP 连接的唯一标识是 “源 IP + 源端口 + 目的 IP + 目的端口”(即 “四元组”)。在实际网络中,可能存在一些因网络延迟而 “迟到” 的报文 —— 比如旧连接(已断开)中未被及时传输的数据包,会在网络中滞留一段时间(最长不超过 1 个 MSL)。

如果客户端没有TIME-WAIT,而是断开后立即用相同的四元组建立新连接,这些 “旧连接的残留报文” 可能会误闯入新连接中,被新连接的接收方(服务器)当成有效数据处理,导致数据错乱或业务逻辑异常(例如,旧报文的指令被新连接执行)。

TIME-WAIT的 2MSL 等待,能确保:

  1. 网络中所有属于 “旧连接” 的残留报文,都会在 1 个 MSL 内自然过期并被丢弃;
  2. 即使有极个别残留报文未丢弃,客户端在 2MSL 内也不会复用相同四元组建立新连接,从时间上彻底隔离旧连接和新连接,避免干扰。

2. 解决TIME_WAIT状态引起的bind失败

TIME-WAIT状态虽然保障了 TCP 连接关闭的可靠性,但在高并发场景(如电商大促、高频短连接服务)中,大量连接处于TIME-WAIT会导致端口资源被占用(无法立即复用相同四元组,即bind原来的的端口),进而引发 “端口耗尽” 或 “连接建立延迟” 等问题,影响服务吞吐量。

解决方案:
使用setsockopt()设置 socket 描述符的 选项 SO_REUSEADDR 为 1, 表示允许创建端口号相同但 IP 地址不同的多个 socket 描述
在这里插入图片描述

六,CLOSE_WAIT状态

当服务器(第二次挥手)收到客户端的 FIN 后,仅确认 “客户端已停止发送”,但服务器自身可能仍有未发送完的数据(如服务器需向客户端返回最后的响应结果),因此会先进入CLOSE-WAIT状态,待自身数据发送完毕后,再主动发送 FIN 报文,切换到LAST-ACK状态

CLOSE_WAIT 的本质是被动关闭方(服务器)未主动发送FIN报文,导致无法进入后续的LAST_ACK状态。这种情况下,fd,端口等资源被占用,导致资源泄漏。

为了避免这种情况的发生,要确保close()被正确调用

七,滑动窗口

一发一收的效率低,那么我们可以一次发送多条数据,(其实是将多个段的等待时间重叠在一起了)

滑动窗口本质上是发送方的 “可连续发送数据的范围”,由发送缓冲区中的双指针(窗口的左、右边界)维护。在确保可靠性的前提下(通过确认应答机制),允许发送方连续发送多个数据段,无需每发一个就停下来等确认,从而将多个数据段的等待时间重叠,大幅提高传输效率。
在这里插入图片描述
滑动窗口的特点:

  1. 只能向右滑动,左边代表已经传输的数据(可丢弃),右边代表未传输的数据
  2. 滑动窗口的大小实时变化(取决于对方接受速度)
  3. 滑动窗口与确认应答配合保证传输数据的可靠性(下面以左边数据丢失为例)
    • 情况一:ACK丢了
      • 1001-2000的ACK丢了,但是接受到的4001-5000的ACK中确认序号是5001,则代表1001-2000已经收到。(因为确认序号的规定,只有1001-2000收到了,才可能出现2001往后的确认序号)
    • 情况二:1001-2000的数据真丢了
      • 此时,后面三个数据的ACK的确认序号都只会是1001(因为没收到2001之前的所有报文),这时会对1001-2000的报文进行重发
      • 当server收到了1001-2000以后,再次返回时就直接是5001了(因为 2001 -5000接收端其实之前就已经收到了, 被放到了接收端操作系统内核的接收缓冲区中)【快重传】
    • 对于中间数据丢失和右端数据丢失:左端都会滑到相应位置 == 左端数据丢失

(发送窗口)滑动窗口大小 == min(对方接受缓冲区剩余大小, 拥塞窗口大小)

八,拥塞控制

当硬件没问题的时候,网络非常拥堵,这时候就要控制client的数据包的发送速度,避免网络更加拥堵(TCP对此也有相应的拥塞控制)

这里介绍TCP拥塞控制的慢启动方法:
慢启动是 TCP 拥塞控制的起始阶段,用于在网络连接建立初期或拥塞恢复时,让发送方逐步增加发送数据量
在这里插入图片描述
流程:

  • 初始:连接刚建立,发送方“拥塞窗口(cwnd)”先设为1个MSS(每次能发的最大数据段),“慢启动门限(ssthresh)”设为较大值(比如16个MSS)。
  • 增长:每收到一个确认(ACK),cwnd就翻倍(指数增长
  • 切换:当cwnd涨到等于或超过ssthresh,就从“慢启动”切到“拥塞避免”(cwnd改成慢慢线性增长)。
  • 拥塞后:若网络拥塞(比如超时没收到ACK),ssthresh改成当前cwnd的一半,cwnd重置为1个MSS,重新慢启动。

作用:

  • 刚连网时,从少量少量数据开始发,试探网络能承受多少,避免一下发太多导致拥堵。
  • 网络通畅顺时,快速提高发送量(指数增长),尽快达到高效传输。
  • 网络堵了就重置,重新慢慢试探,防止越堵越严重。

九,延迟应答机制

TCP 接收方收到数据后,不立即发送 ACK(确认),而是等待自己有数据 / 数据更多的时候再合并并发送ACK,从而减少网络中 ACK 报文的数量,降低开销。

通常我们采用(数量限制 or 时间限制):

  • 数量限制: 每隔 N 个包就应答一次;
  • 时间限制: 超过最大延迟时间就应答一次;

例如:

  • 假设接收端缓冲区为 1M. 一次收到了 500K 的数据; 如果立刻应答, 返回的窗口
    就是500K;
  • 但实际上可能处理端处理的速度很快, 10ms 之内就把 500K 数据从缓冲区消费
    掉了;
  • 在这种情况下, 接收端处理还远没有达到自己的极限, 即使窗口再放大一些, 也
    能处理过来;
  • 所以,让接收端稍微等一会再应答, 比如等待 200ms 再应答, 那么这个时候返回的
    窗口大小就是 1M;

十,面相字节流(粘包问题)

创建一个 TCPsocket的同时,会在内核中创建一个 发送缓冲区 和一个 接收缓冲区。TCP 报文经解包后,仅正文数据会存入接收缓冲区,且这些数据是无边界的字节流(于是就有了粘包问题)。
通常我们通过自定义用户层协议(如分隔符,长度…),确保读取的TCP报文的单个完整性

十一,TCP异常

  • 进程终止: 进程终止会释放文件描述符, 仍然可以发送 FIN. 和正常关闭没有什么区别.
  • 机器重启: 和进程终止的情况相同。(因为关机前会关闭所有进程)
  • 机器掉电/网线断开: 接收端认为连接还在, 一旦接收端有写入操作, 接收端发现连接已经不在了, 就会进行 reset(断开连接)。即使没有写入操作, TCP 自己也内置了一个保活定时器, 会定期询问对方是否还在。 如果对方不在, 也会把连接释放。
  • 另外, 应用层的某些协议, 也有一些这样的检测机制. 例如 HTTP 长连接中, 也会定期检测对方的状态。 例如 QQ, 在 QQ 断线之后, 也会定期尝试重新连接.

十一,报头的封包和分用

在这里插入图片描述

(图片来源:Linux内核中sk_buff结构详解)

简单来说:

  • sk_buff 是 Linux 网络协议栈里的“数据包容器”,用来统一管理网络包的各种信息(比如协议头、数据)。

  • 当数据往 TCP 传时,data 指针会“上移”,腾出空间给 TCP 头部。就像给礼物包快递盒,先留好贴快递单(TCP 头)的位置,再放礼物(应用数据)。

  • 解包分用呢,就是收到包后,sk_buff 带着数据,从下往上(比如从网卡到 IP 层再到 TCP 层),每一层“剥掉”自己的头,最后 TCP 层拿到应用数据,交给对应的程序。

十二,TCP和UDP的对比

1. 核心特性对比

对比维度TCP(传输控制协议)UDP(用户数据报协议)
连接类型面向连接(需“三次握手”建立连接,“四次挥手”关闭连接)无连接(直接发送数据,无需建立/关闭连接)
数据形态面向字节流(数据无固定边界,需应用层定义)面向数据报(每个包是独立单元,有完整边界)
可靠性可靠传输(丢包重传、顺序保证、流量/拥塞控制)不可靠传输(丢包不重传、无顺序、无流量控制)
效率与开销开销高(需维护连接、处理确认/重传,延迟较高)开销低(无连接维护,头部仅8字节,延迟低、速度快)
数据边界无天然边界(易出现“粘包”,需应用层处理)有天然边界(接收方按“包”接收,不粘包)
适用数据量适合大量、连续数据(如文件、流数据)适合少量、突发数据(如指令、短消息)

2. 典型应用场景

TCP的核心优势是可靠传输,适合对数据完整性、顺序性要求高,且可接受少量延迟的场景:

  • 文件传输:如FTP(文件传输协议)、HTTP/HTTPS(网页资源、文件下载)—— 需确保文件不丢包、不损坏。
  • 数据交互:如数据库连接(MySQL、PostgreSQL)、即时通讯的“消息送达确认”(如微信文字消息)—— 需保证指令/消息准确传递。
  • 流式传输:如视频会议的“关键数据”(如画面帧确认)、直播的“弹幕”—— 关键信息不能丢失。

UDP的核心优势是低延迟、低开销,适合对实时性要求高,且可接受少量丢包的场景:

  • 实时音视频:如直播(抖音、快手)、视频会议(Zoom)、 VoIP(网络电话)—— 延迟比丢包更重要(少量丢包仅导致瞬间卡顿,延迟会影响交互)。
  • 短指令传输:如DNS查询(域名解析,一次请求/响应仅几十字节,无需连接)、游戏指令(如王者荣耀的“移动”“攻击”指令,丢包可通过后续指令补偿)。
  • 广播/组播:如局域网设备发现(如打印机搜索)、物联网传感器数据上报(大量设备高频发短数据,无需可靠确认)。

3. 用 UDP 实现可靠传输

参考 TCP 的可靠性机制, 在应用层实现类似的逻辑:

  • 引入序列号, 保证数据顺序;
  • 引入确认应答, 确保对端收到了数据;
  • 引入超时重传, 如果隔一段时间没有应答, 就重发数据;

🌈我的分享也就到此结束啦🌈
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
📢公主,王子:点赞👍→收藏⭐→关注🔍
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!

http://www.dtcms.com/a/414344.html

相关文章:

  • 论文阅读:arxiv 2024 Fast Adversarial Attacks on Language Models In One GPU Minute
  • OpenJDK 17 方法链接与同步方法入口点生成机制深度解析
  • qt-C++笔记之自定义绘制:QWidget中的paintEvent 与 QGraphicsItem中的paint
  • 项目:智能排队控制系统
  • LeetCode:71.字符串解码
  • LeetCode:66.搜索旋转排序数组
  • 阿帕奇网站搭建六安做网站的
  • wordpress去除评论表单电子商务seo优化
  • deepseek kotlin flow快生产者和慢消费者解决策略
  • 20.NFS iSCSI服务器
  • uniapp 搭建vue项目,快速搭建项目
  • 自动网页浏览助手:基于 Selenium + GLM-4V 的百度自动搜索与内容提取系统
  • 网站地图什么时候提交好网站自响应
  • 深度学习笔记(一)——线性回归、Softmax回归、多层感知机、环境和分布偏移
  • 网站建设教程要去d湖南岚鸿询 问2022年企业年报网上申报流程
  • js构造函数—11
  • Kotlin轻量级互斥锁Mutext与轻量级信号量Semaphore异同比较
  • 【MySQL✨】MySQL 入门之旅 · 第十篇:数据库备份与恢复
  • k8s里三种探针的使用场景
  • 8.基于 Ingress-nginx-controller 实现 k8s 七层代理
  • Kling-Audio-Eval - 高质量视频到音频生成评估数据集
  • LeetCode 812.最大三角形的面积
  • 做网站都需要服务器吗域名类型
  • js逆向实战:爬取淘宝男装商品
  • 前端3.0
  • 机器视觉检测中,最小尺寸多少像素可以检测到?重点解析传统算法和深度学习,对比度很致命
  • 不同浏览器中高效维护需要登录网站的登录态
  • 【C++list】底层结构、迭代器核心原理与常用接口实现全解析
  • socket编程 netstat 大小端 rpc 协程 io yield
  • 网站建设与维护百度百科自己做app的软件