TCP基础 —— 为什么建立连接需要三次握手,而断开连接则需要四次?能不能是三次?
一、TCP包概述
一个 segment 包含 header 和 data 两个部分,对于这篇文章需要理解的就是,Sequence number 和 Acknowledgement number) 这两个字段。TCP 的可靠传输就是基于这两个字段来实现的。 虽然文章的主旨是三次握手(three-way handshake)与四次挥手(four-way handshake),但不理解 Sequence number 和 Acknowledgement number 就无法真正的理解这两个过程。
Sequence number
- 当开始一个 TCP 会话时,此时 SYN 位为 1,会生成一个随机的 Sequence number,后续使用 Sequence number 则从 Sequence number + 1 开始。
- 其他时候,则表示为 data 部分第一位的位置,值为此位数据的 Sequence number ,即基于初始 Sequence number + 1 + offset。
其实很好理解,我们先抛开第一次生成的 Sequence number,后续的 TCP 头中的 Sequence number 都指的是 data 部分第一位的序号。比如:
- 我这次发送的 Sequence number 为 100,数据长度为 100,
- 那么我下一次发送的 Sequence number 就应该是 200,再假定数据长度为 50,
- 如果要进行第三次发送,那么 Sequence number 的值应为 250。
下面的图简单的表明了发送两个连续的 segment 的 Sequence number 变化情况,忽略了TCP头,TCP头 并不计入 Length
Acknowledgement number
- 回复收到的最大 Sequence number + 1,表示期望收到的 Sequence number 的值。
和上面的其实一样的道理,比如收到 Sequence number 为 100,数据长度为 100,那么我们就回复 Acknowledgement number = 200。
二、三次握手过程概述
有了上面的基础,我们再开始看握手过程,TCP连接三次握手的过程如下,为了方便描述:
- SEQ_NUM 代表 TCP header 中的 Sequence number
- ACK_NUM 代表 TCP header 中的 Acknowledgment number
- DATA_LEN 代表 segment 中 data 的长度
SYN(1) ACK(0) SEQ_NUM(0) ACK_NUM(0) DATA_LEN(0) ====> <==== SYN(1) ACK(1) SEQ_NUM(100) ACK_NUM(1) DATA_LEN(0) SYN(0) ACK(1) SEQ_NUM(1) ACK_NUM(101) DATA_LEN(0) ====>
- 连接发起方将 SYN 位设置为1,然后随机生成一个 SEQ_NUM_A,发送给被发起方。
- 被发起方回复 ACK(1) ACK_NUM(SEQ_NUM_A+1),同时也需将 SYN 位设置为1,然后自己也随机生成一个 SEQ_NUM_B。
- 连接发起方收到上个 segment 后,回复 ACK(1) ACK_NUM(SEQ_NUM_B+1),当被发起方收到这个 segment 后,连接建立成功。
三、可能存在的疑问
1、为什么要强调 Sequence number 和 Acknowledgement number?
假设同样是三次握手,但是很简单:
SYN(1) ACK(0) ====> <==== SYN(1) ACK(1) SYN(0) ACK(1) ====>
看起来没问题,但实际上由于网络传输是不可靠的,如果没有 Sequence number 我们无法保证此时收到的 segment 的顺序性,也无法得知是否丢失了某个 segment。同样是一个 SYN(1) ACK(1) segment,它有可能是上一次建立连接时被发送方误认为已经丢失的 segment,甚至更特殊的情况。
在数据传输的过程中也是如此,被重发的 segment,丢失的 segment,连续发送的 100 个 segment 如果没有 Sequence number 作为保障,他们到达(或者到达不了)接收方的排列组合方式可能千奇百怪。
2、那么在 Sequence number 和 Acknowledgement number 的保障下,如何保证自己的消息被对方收到呢?
这个其实很好理解,实际上就是自己发送出去的这部分 Sequence number 被 ack 了即可,即:一去一回(实际上可以多去一回,或者可能一去多回,但这里只说最简单的情况)。
SEQ_NUM(100) DATA_LEN(100) ====> <==== ACK(200)
如果没有收到对方的 ack,或者收到的 ack 非此 segment 的 ack,则代表对方没有收到自己的消息,比如下面这个例子,此时我们能得知,对方没有收到或者暂时还没收到我们发送过去的第二个 segment。
此时也可以区分出收到的 segment 是否属于本次连接,因为在建立连接后我们会生成一个新的 Sequence number。
SEQ_NUM(100) DATA_LEN(100) ====> <==== ACK(200) SEQ_NUM(200) DATA_LEN(100) ====> <==== ACK(200)
3、这就很好理解 TCP 连接握手为什么是三次了。
- 发起连接方发出SYN,并收到ACK,这就是两次网络传输了。
- 同样被连接方也发出SYN,且等待对方回复,这也是两次网络传输。
加起来难道不是四次吗?实际上被连接方将对连接方 SYN(1) 的回复和自己 SYN(1) 的请求合并了,所以建立一个 TCP 连接最少只需要经过三次网络传输。
4、那为什么 TCP 断开连接需要四次,而不是三次?
- 发起断开方发出FIN,并收到ACK,这就是两次网络传输了。
- 同样被断开方也发出FIN,且等待对方回复,这也是两次网络传输。
同样的逻辑分析下来,实际上也可以仅经过三次传输就断开此次连接,但为什么我们会说四次挥手呢?这是因为如果在收到FIN时,彼时还有数据未传输完,则先回复关于 FIN 的 ACK,告知对方我已经知道你要断开了。则等待传输完毕后,被断开方再发送 FIN,告知自己也已经可以断开连接。
但实际上完全可以是“三次挥手”,如果收到 FIN 时,已经没有数据要传输,则是“三次挥手”。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
海量数据下的注册中心 - SOFARegistry 架构介绍
SOFAStack ScalableOpen Financial Architecture Stack 是蚂蚁金服自主研发的金融级分布式架构,包含了构建金融级云原生架构所需的各个组件,是在金融场景里锤炼出来的最佳实践。 SOFARegistry 是蚂蚁金服开源的具有承载海量服务注册和订阅能力的、高可用的服务注册中心,最早源自于淘宝的初版 ConfigServer,在支付宝/蚂蚁金服的业务发展驱动下,近十年间已经演进至第五代。 Gitee 地址:https://gitee.com/alipay/sofa-registry 3月31日,蚂蚁金服正式开源了内部演进了近10年的注册中心产品-SOFARegistry。先前的文章介绍了SOFARegistry的演进之路,而本文主要对 SOFARegistry 整体架构设计进行剖析,并着重介绍一些关键的设计特点,期望能帮助读者对 SOFARegistry 有更直接的认识。 如有兴趣,也欢迎加入《剖析 |SOFARegistry实现原理》系列的共建,认领列表见文末。 服务注册中心是什么 不可免俗地,先介绍一下服务注册中心的概念。对此概念已经了解的读者...
- 下一篇
从 VantComponent 谈 小程序维护
在开发小程序的时候,我们总是期望用以往的技术规范和语法特点来书写当前的小程序,所以才会有各色的小程序框架,例如 mpvue、taro 等这些编译型框架。当然这些框架本身对于新开发的项目是有所帮助。而对于老项目,我们又想要利用 vue 的语法特性进行维护,又该如何呢? 在此我研究了一下youzan的 vant-weapp。而发现该项目中的组件是如此编写的。 import { VantComponent } from '../common/component'; VantComponent({ mixins: [], props: { name: String, size: String }, // 可以使用 watch 来监控 props 变化 // 其实就是把properties中的observer提取出来 watch: { name(newVal) { ... }, // 可以直接使用字符串 代替函数调用 size: 'changeSize' }, // 使用计算属性 来 获取数据,可以在 wxml直接使用 computed: { bigSize() { return this.da...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2全家桶,快速入门学习开发网站教程
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- 2048小游戏-低调大师作品
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS6,CentOS7官方镜像安装Oracle11G
- CentOS关闭SELinux安全模块