TCP原理解析

发布于:2025-05-23 ⋅ 阅读:(18) ⋅ 点赞:(0)

目录

TCP协议概述

1. 基础寻址段​​

​​2. 序列控制段​​

​​3. 控制信息段​​

​​4. 流量控制段​​

​​5. 校验与应急段​​

​​6. 扩展功能段​​

​​7. 数据承载段​​

 TCP原理

确认应答与序列号(安全机制)

超时重传机制(安全机制) 

连接管理机制(安全机制) 

滑动窗口(效率机制)

流量控制(安全机制)

拥塞控制(安全机制) 

延迟应答(效率机制) 

捎带应答(效率机制) 

粘包问题 

TCP异常情况

 TCP/UDP对比


TCP协议概述

TCP(Transmission Control Protocol)是互联网通信中最核心的传输层协议,为应用程序提供可靠、有序的端到端数据传输服务。作为现代网络通信的基石,TCP通过多种机制在保证数据可靠性的同时,尽可能提升传输效率。

1. 基础寻址段​
  • ​16位源端口号​​(0-15位)
    标识发送方应用程序的端口号(范围:0-65535)
    示例:Web服务器通常使用80端口,SSH使用22端口

  • ​16位目的端口号​​(16-31位)
    标识接收方应用程序的端口号
    技术细节:客户端端口通常为随机高位端口(>1024)

​2. 序列控制段​
  • ​32位序号​​(32-63位)
    当前报文段第一个字节的全局编号
    作用:解决网络包乱序问题,如初始随机值ISN=123456

  • ​32位确认号​​(64-95位)
    期望收到的下一个字节序号(ACK=1时有效)
    工作逻辑:若收到seq=1000的包,则回复ack=1001

​3. 控制信息段​
  • ​4位首部长度​​(96-99位)
    以​​4字节​​为单位的TCP首部总长度(范围:5-15 → 20-60字节)
    计算示例:首部长度值=5 → 实际长度=5x4=20字节

  • ​6位保留字段​​(100-105位)
    保留位(全置0),为未来协议扩展预留

  • ​6个标志位​​(106-111位)

    • ​URG​​:紧急指针有效(用于发送中断命令)
    • ​ACK​​:确认号有效(建立连接后始终为1)
    • ​PSH​​:接收方应立即推送数据给应用层
    • ​RST​​:强制重置异常连接
    • ​SYN​​:发起连接同步请求(三次握手首个包)
    • ​FIN​​:正常关闭连接请求(四次挥手首个包)
​4. 流量控制段​
  • ​16位窗口大小​​(112-127位)
    接收方当前可接收的字节数(滑动窗口核心参数)
    动态计算:窗口值=接收缓冲区空闲空间
​5. 校验与应急段​
  • ​16位检验和​​(128-143位)
    覆盖首部、数据和伪首部的CRC校验值
    防篡改机制:发送方计算,接收方验证

  • ​16位紧急指针​​(144-159位)
    URG=1时生效,标识紧急数据结束位置
    应用场景:Telnet中断字符传输

​6. 扩展功能段​
  • ​选项​​(长度可变,160位起)
    可选扩展功能,常见类型:
    • ​MSS​​(最大报文长度):协商双方MTU
    • ​Window Scale​​:窗口缩放因子(突破65535限制)
    • ​SACK​​:选择性确认(精确重传)
    • ​Timestamp​​:时间戳(计算RTT与防序号回绕)
​7. 数据承载段​
  • ​数据​​(首部之后)
    应用层传递的有效载荷(如HTTP请求、文件流等)
    技术规范:数据长度=IP报文总长 - IP头 - TCP头

 TCP原理

TCP对数据传输提供的管控机制,主要体现在两个方面:安全和效率。 这些机制和多线程的设计原则类似:保证数据传输安全的前提下,尽可能的提高传输效率。内容全展开的话实在是太多了,所以我只写点重要的

确认应答与序列号(安全机制)

确认应答是TCP的最核心机制,正是这个机制支持了TCP的可靠传输!

确认应答和序列号是TCP用来保证数据不乱、不丢、不被篡改的核心机制。每次发送数据时,TCP会为数据块分配一个序列号(比如从12345开始),接收方收到后必须回一个ACK包,明确告诉对方“下一个我要的是序列号12345+数据长度的位置”。比如你发了一个序列号1001-2000的包,对方ACK就回2001,相当于说“2000之前的我全收到了,下次从2001开始发”。

序列号还有个隐藏的安全作用:连接建立时初始序列号是随机生成的(比如不是从0开始),这让攻击者很难伪造数据包插入到现有连接里,因为猜不中正确的序列号范围。接收方会严格检查每个包的序列号是否在预期窗口内,不符合的直接丢掉,防止中间人乱塞数据。

ACK包虽然简单,但设计得很聪明。比如发送方连续发三个包(序列号1-1000、1001-2000、2001-3000),接收方可能直接回一个ACK=3001,表示前面三个包都收到了。这种“累积确认”机制减少了ACK的数量,同时让发送方明确知道哪些数据需要重传(比如如果ACK卡在1001不动,说明第二个包丢了)。序列号和ACK配合起来,既管顺序又管确认,把不可靠的IP协议硬生生改造成了可靠传输。

TCP将每个字节的数据都进行了编号。即为序列号

每一个ACK都带有对应的确认序列号,意思是告诉发送者,我已经收到了哪些数据;下一次你从哪里开始发。

如何区分数据包是不是ACK

区分普通数据包和ACK包主要看两个地方:一是TCP头部的标志位,二是数据包有没有实际内容。普通数据包一般带着应用层的数据(比如网页内容或文件片段),虽然它可能同时带有ACK标志(表示确认之前收到的数据),但重点是它有实际数据在传输。而纯ACK包的作用仅仅是确认对方发来的数据,所以它的ACK标志位会置1,但数据部分完全是空的,长度为零。类似地,SYN(连接请求)和FIN(断开请求)包也会通过自己的标志位(SYN=1或FIN=1)暴露身份,它们同样不携带数据,只负责控制连接的建立或关闭。简单来说,看标志位有没有特殊标记,再看有没有“货”(数据)在里面,基本就能分清了。 

超时重传机制(安全机制) 

超时重传就是TCP用来对付数据包半路消失的保底手段。发送方每发一个包就启动一个定时器,如果超过一定时间还没收到对方的确认(ACK),就默认这包丢了,立刻重新传一次。比如你传了个序号1001-2000的数据块,结果对方没回ACK,等超时了就直接再发一遍,确保数据最终能送到。

超时时间不是固定的,而是根据历史往返时间动态算出来的。比如之前发个包平均0.1秒就能收到ACK,这次超时时间可能设成0.3秒(留点缓冲防网络波动)。如果重传后还没收到ACK,下次超时时间会直接翻倍(比如从0.3秒变成0.6秒),避免在极端拥堵时反复重传雪上加霜。

重传的包到了接收方那里也不用担心重复。接收方早就记着已经收过哪些序号,如果发现是重复数据(比如同一个序号传了两次),直接扔掉多余的,只留一份给上层应用,同时照样回ACK。这样既解决了丢包问题,又不会让应用层收到重复数据。

超时重传和快速重传(比如连收三个重复ACK就提前重传)是互补的。超时重传属于“宁可多等也要确认真丢了”,而快速重传更敏捷,但两者最终目的都是把数据可靠送达。整个机制全靠发送方自己判断和兜底,不依赖接收方主动报错。

正常流程​

  • 主机A发送数据段(序列号1~1000)
  • 主机B接收成功后返回ACK=1001(表示期望接收下一字节序号)
  • 主机A收到ACK后继续发送新数据

​异常处理​(如图)

  • 若主机A的数据包在网络中丢失(图中标注"X")
  • 主机A在​​500ms超时窗口​​内未收到确认,触发重传原数据(1~1000)

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

  • 主机A首次等待500ms未收到ACK,重传数据
  • 如果第二次仍未收到ACK,等待时间翻倍至​​1000ms(2×500ms)​​后再次重传
  • 持续重传直至收到ACK或达到最大次数(强制断开连接)
  • 主机B通过序列号检测到重复数据,自动丢弃冗余包(避免应用层收到重复内容) 

为什么会丢包 

丢包可能因为网络拥堵(比如路由器处理不过来直接丢弃数据包)、硬件故障(比如网线接触不良或路由器性能差)、信号干扰(无线网络受距离或障碍物影响)、配置问题(防火墙或路由策略错误拦截数据),或者协议本身的机制(比如TCP重传前故意丢弃重复包)。此外,数据包传输路径中的节点(如交换机、运营商设备)超载或异常也可能导致丢包。如果是广域网,跨地区链路不稳定或带宽不足会更常见。简单来说,只要传输路径上的任何一个环节出问题或扛不住压力,都可能让数据包“消失”。

重复数据怎么办

接收方内部有个叫“接收缓冲区”的内存空间,专门存已经收过的数据和对应的序号。比如你发了个1-1000的数据包,对方确认要1001之后,如果同样的数据包又传了一次,接收方一查发现这序号在缓冲区里已经存在,直接就把后来的重复包扔了,根本不会让应用程序读到两条一样的。哪怕发送方因为网络问题反复发同一个包,到应用层这里(比如inputStream.read)还是只会拿到一条数据,完全不用担心重复扣钱或者处理错数据这种问题。并且“接收缓冲区”还能对数据进行重新排序,确保发送的数据和应用读取的数据一致

连接管理机制(安全机制) 

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

TCP通过经典的“三次握手”建立连接,这一过程看似简单,实则暗藏安全设计。当客户端发起连接时,会发送一个携带​​随机生成序列号​​的SYN报文。服务器收到后,不仅会回复自己的随机序列号(SYN-ACK报文),还会将客户端的初始序列号+1作为确认依据。这种随机数的引入,使得攻击者难以伪造合法的序列号组合,有效防止了中间人攻击或会话劫持。

针对常见的SYN洪泛攻击(攻击者发送大量伪造SYN请求耗尽服务器资源),现代系统普遍采用​​SYN Cookie机制​​。服务器在收到SYN报文时,并不立即分配内存资源,而是通过加密算法生成一个“临时凭证”作为响应报文的序列号。只有当客户端返回正确的ACK确认(携带凭证解密后的信息),服务器才会正式分配连接资源。这种“先验证后建连”的策略,既保障了正常用户的连接效率,又大幅提高了攻击成本。

连接建立后,TCP通过​​序列号与确认号机制​​保障数据完整性。每个数据包都携带唯一序列号,接收方会严格检查这些序号是否连续且在预期窗口范围内。例如,若收到一个序列号远大于当前窗口的数据包,TCP会直接丢弃并触发重传机制。这种动态校验不仅能识别网络传输中的乱序或丢包,还能拦截攻击者伪造或篡改的数据注入。

此外,TCP的​​滑动窗口机制​​也具备安全属性。接收方通过动态调整窗口大小,不仅能实现流量控制,还能在遭遇异常流量(如短时间内大量重复数据包)时缩小窗口,间接限制攻击者的数据发送速率,为系统争取应急响应时间。

TCP通过“四次挥手”优雅地关闭连接,这一过程中的状态管理也隐含安全考量。例如,主动关闭方在发送最终ACK后会进入​​TIME_WAIT状态​​,等待足够时间以确保对方收到确认。这种设计不仅是为了处理网络中可能滞留的延迟数据包,还能防止攻击者利用旧连接的残留信息伪造新连接。

对于异常终止(如RST报文强制断开),操作系统内核会严格校验RST报文的序列号是否合法。如果检测到序列号与当前连接不匹配,系统会直接忽略异常请求,避免攻击者通过伪造RST报文实施“连接重置攻击”。

尽管TCP内置了多重防护,但其设计初衷并非解决所有安全问题。例如:

  • ​数据明文传输​​:TCP不提供加密功能,攻击者仍可能窃听数据内容,需依赖TLS/SSL等协议实现加密。
  • ​依赖随机数质量​​:序列号的随机性直接影响安全性,早期系统因随机数生成缺陷曾引发攻击,现代操作系统已采用更安全的随机数算法(如基于硬件熵源)。
  • ​协议栈实现差异​​:不同厂商对TCP协议栈的实现可能存在漏洞,需通过系统更新及时修补。

 如果你懒得去理解,那只需要记住下面这些东西,首先是三次握手,A向B发送SYN(这时是在告诉B,我的发送能力没问题),B收到SYN后向A发送SYN和ACK(这是在告诉A,我的接收能力没问题,并且你的发送能力没问题),此时A向B发送ACK(这是告诉B,你的发送能力没问题,并且我的接收能力没问题).完成以上操作A和B就记录了对方的信息(在握手的过程中会针对一些重要的参数进行协商,比如通信过程中的序列号从几开始),也就是构成了逻辑上的连接,关于四次挥手,

没什么好说的和三次握手的逻辑差不多,只需要特别注意一点,假设是A发起的请求,那么当B发送FIN后A会进入TIME_WAIT状态,这主要是为了确保A能对FIN进行处理发出ACK,否则B收不到A的ACK就会超时重传,产生bug

为什么三次握手可以是三次,而四次挥手是四次

三次握手能合并成三次,是因为服务器在收到客户端SYN后,内核会同时触发ACK(确认)和发送自己的SYN(同步请求),这两个操作发生在同一时间点,直接合并成一次​​SYN+ACK​​响应。而四次挥手会拆成四次,是因为当一方发送FIN(结束请求)后,对方的ACK是内核自动回复的,但另一端的FIN需要等应用程序主动执行close()才会触发。这两个动作(ACK和FIN)的触发时机完全独立,一个由系统自动处理,一个依赖程序主动操作,没法打包成一次发送,所以挥手需要多一步,变成四次。当然实际情况要复杂的多,四次挥手有的时候会变成三次,当看到延时应答这一机制的时候我们再说

滑动窗口(效率机制)

滑动窗口说白了就是让发送方不用傻等确认(ACK),可以一口气多发几个包。比如你传文件时,接收方会告诉发送方自己还能收多少数据(这个数叫窗口大小),发送方就按这个额度不断发,不用每发一个包都停下来等回复。窗口里的数据只要还没确认,就暂时占着位置,等前面的ACK陆续回来,窗口就跟着往前“滑”,腾出位置发后面的数据。这样网络带宽就被持续填满,效率自然高了。

接收方的窗口大小也不是固定的,会根据自己处理能力动态调整。比如接收方的应用程序如果读取慢了,缓冲区快满了,窗口就会变小甚至关窗,告诉发送方“别发了,我处理不过来了”。反过来,如果接收方处理得快,窗口开大,发送方就能飙高速。这种实时反馈让双方节奏同步,既不会撑死接收方,也不会让发送方闲着。

实际传数据时,窗口还会结合确认机制一起用。比如发送方发出去1-1000、1001-2000、2001-3000三个包,只要收到第一个包的ACK,哪怕后面两个包还没确认,窗口也会直接滑到3001继续发新数据。如果中间有包丢了,接收方会一直要求重传丢失的部分,窗口也会卡在丢包的位置等补传,这样既能保证数据顺序,又能尽量维持传输速度。

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

那么如果出现了丢包,如何进行重传?这里分两种情况讨论。

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

这种情况下,部分ACK丢了并不要紧,因为可以通过后续的ACK进行确认; 

情况二:数据包直接丢了。

  • 当某一段报文段丢失之后,发送端会一直收到 1001 这样的ACK,就像是在提醒发送端 "我想 要的是 1001" 一样;
  • 如果发送端主机连续三次收到了同样一个 "1001" 这样的应答,就会将对应的数据 1001 - 2000 重新发送;
  • 这个时候接收端收到了 1001 之后,再次返回的ACK就是7001了(因为2001 - 7000)接收端 其实之前就已经收到了,被放到了接收端操作系统内核的接收缓冲区中; 

这种机制被称为 "高速重发控制"(也叫 "快重传")

流量控制(安全机制)

流量控制的核心目的是防止发送方把接收方“冲垮”。接收方每次发确认(ACK)时会带上自己的接收窗口大小,这个数值代表当前缓冲区还能存多少数据。比如接收方说窗口还剩1000字节,发送方最多只能发这么多,发完就必须等新的窗口更新,否则不准继续发。如果接收方处理慢了,缓冲区快满了,窗口会越来越小甚至变成零,这时候发送方就彻底暂停,直到对方重新开窗。

窗口更新不是单次操作,而是动态调整的。比如接收方每次从缓冲区取走数据后,可用空间变大,就会在新的ACK中携带更大的窗口值,发送方看到后就能逐步恢复发送速度。这种实时反馈让发送方的节奏完全跟着接收方的处理能力走,避免数据积压导致内存溢出或丢包。

零窗口的情况需要特殊处理。如果接收方窗口变零,发送方会启动“持续计时器”,定期发探测包(比如空数据+ACK),询问对方窗口是否恢复。一旦收到非零窗口的回复,发送方立刻继续传数据,避免双方卡死。整个过程不需要应用层参与,全靠TCP协议自己协调,既安全又省心。

流量控制和拥塞控制容易混淆,但目标不同。流量控制只关心接收方的处理能力,是点对点的安全机制;拥塞控制关注整个网络的拥堵程度,防止全局性的过载。两者配合使用,保证数据传输既高效又稳定。

拥塞控制(安全机制) 

拥塞控制的核心是让发送方主动探测网络承受能力,避免把链路塞爆(也就是考虑通信过程中, 中间节点的情况)。它通过动态调整“拥塞窗口”的大小来控制发送速率。比如刚开始传数据时,窗口很小(比如1个包),每收到一个ACK就把窗口翻倍,指数级增长直到出现丢包或达到阈值,这阶段叫慢启动。一旦触发阈值,就进入拥塞避免阶段,窗口改为线性增长(比如每轮加1),用更保守的方式试探网络极限。

如果发生超时丢包(比如ACK死活等不到),说明网络可能彻底堵死,这时候窗口会直接砍到1,重新开始慢启动,同时把阈值设为丢包时窗口的一半。但如果是快速重传(比如连收三个重复ACK),说明只是部分丢包,网络还有救,窗口会降到当前值的一半加3,进入快速恢复阶段,一边补传丢包一边等新ACK,避免完全重置窗口。

现代算法(如Cubic或BBR)会更智能地预测带宽。比如Cubic用三次函数模拟窗口增长,丢包后先降窗口,再按曲线缓慢爬升,而不是机械地翻倍或加1。BBR则直接测量链路最小延迟和最大带宽,动态计算发包速率,尽量绕开拥堵。这些机制让TCP既能榨干空闲带宽,又能在拥堵时及时刹车,平衡速度和稳定性。

拥塞控制的最终目标是在“发太快导致丢包”和“发太慢浪费带宽”之间找到平衡点。整个过程完全由发送方自主决策,不需要和接收方或路由器协商,靠的纯属算法预测和反馈调整。

  • 此处引入一个概念程为拥塞窗口
  • 发送开始的时候,定义拥塞窗口大小为1;
  • 每次收到一个ACK应答,拥塞窗口加1;
  • 每次发送数据包的时候,将拥塞窗口和接收端主机反馈的窗口大小做比较,取较小的值作为 实际发送的窗口; 

像上面这样的拥塞窗口增长速度,是指数级别的。"慢启动" 只是指初使时慢,但是增长速度非常快。为了不增长的那么快,因此不能使拥塞窗口单纯的加倍。 此处引入一个叫做慢启动的阈值 当拥塞窗口超过这个阈值的时候,不再按照指数方式增长,而是按照线性方式增长

  • 当TCP开始启动的时候,慢启动阈值等于窗口最大值;
  • 在每次超时重发的时候,慢启动阈值会变成原来的一半,同时拥塞窗口置回1; 

少量的丢包,我们仅仅是触发超时重传;大量的丢包,我们就认为网络拥塞; 当TCP通信开始后,网络吞吐量会逐渐上升;随着网络发生拥堵,吞吐量会立刻下降;拥塞控制,归根结底是TCP协议想尽可能快的把数据传输给对方,但是又要避免给网络造成太大压力的折中方案。 

延迟应答(效率机制) 

延迟应答的核心思路是减少ACK包的数量,提升网络利用率。接收方收到数据后不会马上回ACK,而是稍微等一小段时间(比如40毫秒),如果这段时间内正好有数据要发给对方,就把ACK捎带在数据包里一起发出去,省掉单独发ACK的开销。如果等半天也没数据要发,再单独回一个ACK,防止发送方误以为丢包。

这种延迟不是无限制的。操作系统通常会设置最大延迟时间(比如Linux默认40ms),超时后必须发ACK,否则发送方的超时重传机制会被触发,反而浪费带宽。另外,如果接收方的窗口大小(空闲缓冲区)有显著变化,比如突然腾出大量空间,也会立刻更新窗口并发送ACK,避免发送方卡在零窗口状态。

延迟应答和滑动窗口配合起来效果更好。比如接收方攒了几个ACK一起发,发送方看到连续确认后,可以一次性发更多数据,减少来回等待的时间。但极端情况下(比如实时性要求高的场景),可能会手动关闭延迟应答,换回即时ACK,用带宽换低延迟。整体来说,延迟应答是在可靠性和效率之间找平衡的典型操作。

填坑

延时应答拖延了ACK的回应时间,一旦ACK滞后,就有机会和FIN合并在一起,于是四次挥手就变成了三次

捎带应答(效率机制) 

捎带应答是建立在延时应答的基础上的

捎带应答说白了就是“搭便车发ACK”。当通信双方有双向数据传输时,如果接收方刚好要发数据给对方,就会把该回的ACK塞到数据包里一起发走,而不是单独发一个纯ACK包。比如客户端传了个文件块,服务器处理完正好要返回处理结果,这时候ACK就跟着结果数据一起传回去,省了一次单独确认的交互。

这种机制最明显的优势是减少网络上的小包数量。单独发ACK虽然只有几十字节,但在高并发或弱网环境下,积少成多会占用不少带宽和连接资源。捎带应答把确认动作“寄生”在正常数据流里,相当于用顺风车代替专车,自然更高效。

不过捎带应答依赖双向通信的场景。如果一方只收不发(比如下载大文件时服务器持续传数据,客户端很少回传),ACK就只能单独发。这时候可能会结合延迟应答——等40毫秒看是否有数据要发,实在没有才单独回ACK。两者配合起来,能在大部分场景下把冗余的ACK压缩到最少。

实际抓包时能看到,像HTTP接口的请求-响应模式就特别适合捎带应答。客户端发POST请求后,服务器的响应数据包里会同时包含对请求的ACK和对响应数据的序列号,一次交互完成两种操作,效率直接拉满。

粘包问题 

粘包问题本质上是接收方无法正确拆分出发送方原本传输的数据块。因为TCP是流式协议,数据像水管里的水一样连续传输,没有固定分界。比如发送方快速发了“Hello”和“World”两个包,接收方可能一次读到“HelloWorld”,粘成一个包,也可能分两次读成“He”和“lloWorld”,完全不确定。

出现粘包主要两个原因:一是发送方内核的Nagle算法把小包合并发送(比如攒够一个MSS大小才发),二是接收方从缓冲区读取数据时,可能一次性读了多个包或者半个包。这和TCP的设计有关——它只管把数据流可靠送到,不管应用层怎么定义“消息”的边界。

解决办法全靠应用层自己定规则。常见的有三种:1. ​​固定长度​​,比如每个包固定100字节,不够就补空格;2. ​​分隔符​​,比如每个包末尾加换行符\n,接收方按这个切分;3. ​​头部声明长度​​,比如前4字节表示后续数据长度,先读头部再读对应长度的内容。像HTTP协议就用了Content-Length头明确数据体大小。

实际开发中,像WebSocket这类协议已经内置了帧结构(比如头部的掩码和长度字段),根本不需要操心粘包。但如果自己写底层通信,比如用Netty,就得通过编解码器(如LengthFieldBasedFrameDecoder)预先处理粘包问题,否则业务逻辑会被不完整的数据搞崩溃。

TCP异常情况

TCP遇到异常时处理方式各不相同。比如进程突然挂了或者机器重启,内核会正常回收连接资源,发FIN包通知对方关闭,和手动close()没区别,对方能按四次挥手流程安全断开。但如果是机器直接断电或者网线被拔了,接收方完全不知道连接已经断掉,这时候如果接收方尝试往这个连接写数据,协议栈会发现对方根本不存在,直接回一个RST重置包强制关闭连接。

就算接收方一直不写数据,TCP自己也带了个保活定时器(默认两小时触发一次),定期发探测包确认对方是否活着。如果连续多次没回应,就认定连接已死,自动释放资源。不过这个机制一般要手动开启(设置SO_KEEPALIVE选项),不是所有连接都默认启用。

应用层协议为了更灵敏,通常会自己加检测。比如HTTP长连接可能设置30秒无请求就主动断掉,或者像QQ这类即时通讯软件,一旦发现收不到心跳包回应,立刻尝试重连,而不是傻等TCP的保活机制。这种双保险让异常断开的恢复更快,用户体验更稳。

关于心跳包

心跳包说白了就是应用层自己搞的“存活检测”。比如客户端和服务器建立长连接后,如果长时间没数据往来,中间的路由器或者防火墙可能会把连接掐掉。这时候双方约定每隔一段时间(比如30秒)发个固定格式的小数据包(比如内容为ping),对方收到立刻回个pong,这样既告诉中间设备“这连接还在用”,又能确认对方还活着。

如果超过一定次数没收到心跳回复(比如连续3次ping没回应),就认为网络断了或者对方宕机,应用层会主动触发重连逻辑,而不是傻等TCP的保活机制(默认两小时才检测)。像微信聊天、在线游戏这种实时性高的场景,心跳包间隔可能短到1秒,确保掉线能秒级感知。

心跳包一般会尽量精简,只带必要信息(比如时间戳或序号),避免浪费带宽。有些协议还会在心跳里夹带业务状态,比如设备上报电量、服务端推送配置更新,做到“一包两用”。不过核心目标始终是维持连接活性,尤其在移动网络(4G/5G)下,基站资源有限,心跳包能有效防止连接被系统回收。

和TCP自带的保活机制相比,心跳包完全由应用层控制,灵活性更高。比如可以根据网络质量动态调整间隔——Wi-Fi环境下心跳调慢,蜂窝网络下调快,甚至后台运行时暂停心跳省电,这些细节都是协议自己说了算。

 TCP/UDP对比

我们说了TCP是可靠连接,那么是不是TCP一定就优于UDP呢?TCP和UDP之间的优点和缺点,不能简单,绝对的进行比较

  • TCP用于可靠传输的情况,应用于文件传输,重要状态更新等场景;
  • UDP用于对高速传输和实时性要求较高的通信领域,例如,早期的QQ,视频传输等。另外 UDP可以用于广播;

归根结底,TCP和UDP都是程序员的工具,什么时机用,具体怎么用,还是要根据具体的需求场景去判定。 


网站公告

今日签到

点亮在社区的每一天
去签到