TCP原理解析
目录
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都是程序员的工具,什么时机用,具体怎么用,还是要根据具体的需求场景去判定。