首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >最通俗易懂的TCP协议讲解!

最通俗易懂的TCP协议讲解!

作者头像
JavaEdge
发布2025-06-01 10:41:16
发布2025-06-01 10:41:16
2350
举报
文章被收录于专栏:JavaEdgeJavaEdge

点击下方“JavaEdge”,选择“设为星标”

第一时间关注技术干货!

免责声明~ 任何文章不要过度深思!万事万物都经不起审视,因为世上没有同样的成长环境,也没有同样的认知水平,更「没有适用于所有人的解决方案」; 别急着评判文章列出的观点,只需代入其中,适度审视一番自己即可,能「跳脱出来从外人的角度看看现在的自己处在什么样的阶段」才不为俗人。 怎么想、怎么做,全在乎自己「不断实践中寻找适合自己的大道」

0 前言

UDP包括传输层所必须的端口字段。它相信“网之初,性本善,不丢包,不乱序”。

后来,我们慢慢长大,了解社会残酷,变得复杂成熟,就像TCP协议。它之所以复杂,因为它秉承“性恶论”。它认为网络环境天生恶劣的,丢包、乱序、重传,拥塞都是常事,一言不合就可能送达不了,要从算法层保证可靠性。

1 TCP包头格式

1.1 TCP头
adfc9349db9711be4f9089cd36c40d79.png
adfc9349db9711be4f9089cd36c40d79.png
① 源、目标端口号

和UDP一样。没这俩端口号,数据就不知该发给哪个应用。

② 包的序号

为啥要给包编号?解决乱序。不编号,咋确认哪个该先来,哪个该后到?

③ 确认序号

发出去的包应该有确认。若没收到就该重发,直到送达,以不丢包。

TCP是靠谱协议,但不代表它所处的网络环境很好。IP层来看,若网络状况差,无任何可靠性保证,即使是IP的上一层TCP也无能为力,能做的只是更努力,不断重传,通过各种算法尽量保证。即对于TCP,IP层你丢不丢包,我管不着,但在我TCP层会尽力保证可靠性。

④ 状态位
  • SYN:发起一个连接
  • ACK:回复
  • RST:重新连接
  • FIN:结束连接

TCP是面向连接,因而双方要维护连接状态,这些带状态位的包的发送,会引起双方状态变更。

⑤ 窗口大小

TCP要做流量控制,通信双方各声明一个窗口,标识自己当前能够的处理能力,别发送太快或太慢。 拥塞控制,对于真正的通路堵车不堵车,它无能为力,唯一能做的就是控制自己,也即控制发送的速度。

2 三次握手

先建立一个连接,TCP的连接建立,称为三次握手。

  • A:你好,我是A
  • B:你好A,我是B
  • A:你好B

也常称“请求->应答->应答之应答”的三回合。

2.1 为啥三次,两次不够吗?

生活里两人打招呼,一来一回就够,那既然为了可靠,为啥不四次?

假设这个通路非常不可靠,A要发起一个连接,当发了第一个请求,无响应,会有很多可能性:

  • 第一个请求包丢了
  • 没丢,但绕了弯路,超时了
  • B没响应,不想和我连接

A不能确认结果,于是再发发发。终于有个请求包到了B,但请求包到了B这件事,A还不知道,所以A可能再发。

B收到请求包,就知道了A的存在,且知道A要和它建立连接。若B:

  • 不情愿建立连接,则A会重试一阵后放弃,连接建立失败
  • 乐意建立连接,则会发送应答包给A

对于B,这个应答包也不知道能否到达A。这时B自然也不能认为连接是建立好了,因为应答包可能:

  • 会丢
  • 会绕弯路
  • A挂了
网络延迟或包丢失

这时B还能碰到诡异现象:A和B原来建立了连接,简单通信后,结束了连接。

假设A和B之间已经建立过一次连接,并且这次连接已经顺利地建立并结束了。在连接结束后,网络中的某个节点因为某种原因出现了延迟,导致A最初发送的一个“连接请求”包(SYN)绕了一大圈,直到连接结束后才被B收到。这时B收到这个延迟的SYN包,会认为这是A再次发起的连接请求,而B并不知道这是一个“历史遗留”的请求包。

此时B基于这个延迟到达的SYN包,重新建立了一个新的连接。这就是所谓的“单相思”现象:实际上A已经结束了连接,但由于网络延迟,B误以为A重新发起了连接请求,于是建立了一个新的连接。但因为A其实并没有发起新的连接,所以B的这个连接不会真正进行下去,导致一种“诡异”的情况——B在与一个“已经结束通信的A”进行通信。

所以两次握手不够!

B发送的应答可能发送多次,但只要一次到达A,A就认为连接已建立,因为对于A,他的消息【有去有回,请求响应】。A会给B发送应答的应答,而B也在等这个消息,才能确认连接建立了,只有等到这消息,对于B,才算它的消息【有去有回,请求响应】。

当然A发给B的应答的应答也可能:

  • 丢了
  • 绕路
  • B挂了

看起来应该还有个应答的应答的应答,但这样下去就没底。所以四次握手可,四十次都可,关键四百次你也不能保证就真可靠。所以只要保证:双方的消息都有去有回即可。

大部分情况下,AB建立连接后,A会马上发数据,一旦A发数据,则很多问题都得到解决。 如A发给B的应答丢了,当A后续发送的数据到达时,B可认为该连接已建立,或B就是挂了,A发送的数据,会报错,说明B不可达,A就知道B有异常。

当然你可以说A比较坏,就不发数据,建立连接后一直空着。可开启keepalive机制,即使没有真实数据包,也有探活包。作为服务端B的开发人员,对于A这种长时间不发包的客户端,可主动关闭,从而空出资源响应其它客户端。

三次握手还为了解决:

2.2 TCP包的序号问题

A要告诉B,我这面发起的包的序号起始号,B也要告诉A,B发起的包的序号起始号。

为啥序号不都从1开始?

会出现冲突。如A连上B后,发1、2、3三包。发3时,中间丢了或绕路了,于是重发。

后来A掉线了,重连上B后,序号又从1开始,然后发送2,但没想过发送3,但上次绕路的那个3又回来了,发给了B,B自然认为,这就是下一个包,发生错误!

所以每个连接要有不同序号。序号的起始序号随时间变化,可看成一个32位计数器,每4ms加一。稍稍计算,若重复,需4h+,那个绕路的包也早就没了,因为IP包头里有个TTL。

最后双方终于成功建立连接。为维护该连接,双方都要维护一个状态机。

2.3 连接建立过程中的双方状态变化时序图
0102da048a9eefe08e47086042af141d.png
0102da048a9eefe08e47086042af141d.png
  • 起初,客户端(Client)、服务端(Server)处CLOSED态
  • Server主动监听某端口,处LISTEN态
  • 然后Client主动发起连接SYN,之后处于SYN-SENT状态
  • Server收到发起的连接,返回SYN,并ACK客户端的SYN,之后处于SYN-RCVD态
  • Client收到Server发送的SYN和ACK后,发送ACK的ACK,之后处ESTABLISHED态,因为它一发一收成功了
  • 服务端收到ACK的ACK后,处于ESTABLISHED状态,因为它也一发一收

★ TCP协议中,**监听某端口(Listen on a port)**是指服务器端准备接收来自客户端的连接请求。更具体地说,服务器会在某个特定的端口上开启一个监听进程,等待客户端发起的连接。 详解

  1. 端口
  • 端口号是计算机网络中用来标识网络进程或服务的逻辑编号。
  • 每个网络服务(例如HTTP、FTP、SSH)都在特定的端口号上运行,比如HTTP服务通常运行在80端口,HTTPS服务运行在443端口。

监听

  • 当服务端程序希望接受客户端的连接时,它会对某个端口执行“监听”操作。这意味着服务端在这个端口上“等待”客户端的连接请求。
  • 监听的含义就是服务端程序告知操作系统,它准备好在这个特定的端口上接收新的网络连接。

如何监听

  • 服务器通常会调用系统的listen()函数来使自己进入监听状态。当服务端处于LISTEN状态时,它会接收进入该端口的连接请求,并且在收到来自客户端的SYN(连接请求)时响应。

例子 假设有一个Web服务器,它运行在80号端口并开始监听:

  • 服务器调用listen()函数,告诉操作系统它准备在80号端口接收客户端的连接。
  • 当客户端发起请求,例如通过浏览器访问一个网页时,客户端会向服务器的IP地址和80号端口发送一个SYN包(连接请求)。
  • 服务器收到请求后,确认连接请求并与客户端建立连接。

总结 监听某端口就是服务器在某个端口上开启服务,等待客户端的连接请求。当服务端进入LISTEN状态时,它已经准备好处理任何发往该端口的连接请求。 ”

3 四次挥手

好说好散,四次挥手。

  • A:B啊,我不想玩了
  • B:哦,你不想玩了啊,我知道了

这时,还只是A不想玩了,即A不会再发数据,但B能在ACK时直接关闭吗?

当然不可以,很可能A发完最后的数据就准备不玩,但B还没做完自己的事,还是可发送数据,所以称半关闭状态。这时A可选择:

  • 不再接收数据
  • 或最后再接收一段数据,等待B也主动关闭

B:A啊,好吧,我也不玩了,拜拜 A:好的,拜拜

这样整个连接就关闭了。这才是和平分手。

A开始说“不玩了”,B说“知道了”,这没问题,因为此前,双方还处合作态。

若A说“不玩了”,没收到回复,则A会重发“不玩了”。但这回合结束后,可能出现异常,因为已有一方率先撕破脸:

  • A说完“不玩了”后,直接跑路,就有问题,因为B还没发起结束,而若A跑路,B就算发起结束,也得不到回答,B就不知道该咋办了
  • A说完“不玩了”,B直接跑路,也有问题,因为A不知道B是还有事要处理,还是过一会儿会发送结束回来

咋办?TCP协议专门设计几个状态处理这些问题。

3.1 断开连接时的状态时序图
c9b536fa7427809e0a6cdf1b386ac4d2.png
c9b536fa7427809e0a6cdf1b386ac4d2.png

断开时可见:

  • 当A说“不玩了”,就进入FIN_WAIT_1态
  • B收到“A不玩”消息后,发送知道了,进入CLOSE_WAIT态
  • A收到“B说知道了”,就进入FIN_WAIT_2态。若此时B直接跑路,则A将永远在这状态。TCP协议里并没有对这个状态的处理,但Linux有,可调整tcp_fin_timeout参数,设置一个超时时间
  • 若B没跑路,发送“B也不玩了”请求到达A时
  • A发送“知道B也不玩了”ACK后,从FIN_WAIT_2态结束。按理说,A现在可以跑路了,但最后这ACK万一B收不到?则B会重发一个“B不玩了”,这时若A已跑路,B就再也收不到ACK,所以TCP协议要求A最后等待一段时间TIME_WAIT,这时间要够长,长到如果B没收到ACK,“B说不玩了”会重发的,A会重新发一个ACK且足够时间到达B

A直接跑路还有个问题:A的端口就直接空出,但B不知道,B原来发过的很多包可能还在路上。此时若A的端口被一个新应用占用,新应用会收到上个连接中B发过来的包,虽然序列号是重新生成的,但这里要上一个双保险,防止混乱,因而也要等够长时间,等原来B发送的所有的包都死翘翘,再空出端口。

MSL

等待的时间设为2MSL,MSL(Maximum Segment Lifetime,报文最大生存时间),是任何报文在网络上存在的最长时间,超过这时间,报文将被丢弃。因为TCP报文基于IP协议,而IP头有个TTL域,是IP数据报可经过的最大路由数,每经过一个处理他的路由器,此值-1,值为0则数据报将被丢弃,同时发送ICMP报文通知源主机。协议规定MSL为2min,实际应用中常用30s,1min和2min等。

Q:若B超过2MSL,依然没收到它发的FIN的ACK,咋办? A:按TCP原理,B会重发FIN,这时A再收到这个包后,A就表示,我已在此等这么久,仁至义尽,之后的我也不认了,于是直接发送RST,B就知道A早已跑了。

4 TCP状态机

综合连接建立、连接断开的时序状态图,就是著名的TCP状态机。建议将状态机和时序状态机对照着便于理解:

58c15d431a292a6c54cf950a569392b7.png
58c15d431a292a6c54cf950a569392b7.png

阿拉伯数字序号,是连接过程中的顺序,而大写中文数字的序号,是连接断开过程中的顺序。

  • 粗实线:Client A的状态变迁
  • 粗虚线:Server B的状态变迁

关注我,紧跟本系列专栏文章,咱们下篇再续!

★ 作者简介:魔都架构师,多家大厂后端一线研发经验,在分布式系统设计、数据平台架构和AI应用开发等领域都有丰富实践经验。 各大技术社区头部专家博主。具有丰富的引领团队经验,深厚业务架构和解决方案的积累。 负责:

  • 中央/分销预订系统性能优化
  • 活动&券等营销中台建设
  • 交易平台及数据中台等架构和开发设计
  • 车联网核心平台-物联网连接平台、大数据平台架构设计及优化
  • LLM Agent应用开发
  • 区块链应用开发
  • 大数据开发挖掘经验
  • 推荐系统项目

目前主攻市级软件项目设计、构建服务全社会的应用系统。 ”

参考:

  • 编程严选网

写在最后

编程严选网http://www.javaedge.cn/ 专注分享软件开发全生态相关技术文章视频教程资源、热点资讯等,全站资源免费学习,快来看看吧~

e2f76bfee1880d7a42906e3a956906f9.png
e2f76bfee1880d7a42906e3a956906f9.png
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-05-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 0 前言
  • 1 TCP包头格式
    • 1.1 TCP头
      • ① 源、目标端口号
      • ② 包的序号
      • ③ 确认序号
      • ④ 状态位
      • ⑤ 窗口大小
  • 2 三次握手
    • 2.1 为啥三次,两次不够吗?
      • 网络延迟或包丢失
    • 2.2 TCP包的序号问题
      • 为啥序号不都从1开始?
    • 2.3 连接建立过程中的双方状态变化时序图
  • 3 四次挥手
    • 3.1 断开连接时的状态时序图
    • MSL
  • 4 TCP状态机
  • 写在最后
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档