文章目录
- 一、TCP 内部工作机制——连接管理
- 1. 连接(三次握手)
- (1).有连接和确认应答之间的关系
- (2). 通过客户端和服务器详细描述三次握手
- 2. 断开连接(四次挥手)
- (1)讨论“四次握手”中间步骤的合并问题。
- (2) 根据简单的 TCP 代码解释断开连接
- (3) 四次挥手中的两个重要的 TCP 状态
- 二、连接管理总结
一、TCP 内部工作机制——连接管理
在上一篇文章中,本人描述了保证 TCP 的可靠性的两个关键点:确认应答&超时重传。
在这篇文章中所描述的 连接管理 ,对应的就是 TCP 的核心特点——可靠传输。
1. 连接(三次握手)
所谓 “连接” ,就是建立连接,通信双方各自记录对方的信息,彼此之间要有相互认同。
-
TCP 建立连接的最初描述
TCP 的建立连接,还有一个名字叫做 三次握手
如上图所示,所谓的 三次握手 ,本质上是 “四次” 交互。
通信双方,各自需要向对方发起一个 “建立连接” 的请求。同时,在各自向对方回应一个 ack 。所以这里一共有四次信息交互。 -
TCP 建立连接的正确形式描述
上面的 “四次” 交互,是否可以将中间的两次信息交互合并成一个一次交互。从而构成“三次握手”,不合并是否可以? 如图: 答案是:必须合并! 我们要知道的是,这里的每一步都有着一个特点 “封装分用”。分装分用两次一定比分装分用一次,成本更高。 -
TCP 建立连接是使用 “两次握手” 情况的讨论
所谓 “两次握手” 图示如下:
此时,缺少了一次握手,我们就需要用不同的方式来思考这个问题。
要讨论这个问题,我们要将视角分为两部分。
1.对于情境中的 “我”。 我明确的点了一份炒面。 我得知了我需要等一会。
2.对于情境中的 “饭店老板”。 得知了我需要一份炒面。
不知道我是否愿意等待。
所以,在上面的两个情况的分析中,从 “饭店老板” 的视角中,“我” 可能就不愿意等待。
通过上面的问题的讨论和分析,我们还可以发现,三次握手除了建立连接之外。还有一个重要的作用——验证双方各自的信息发送和接受功能是否正常!
(1).有连接和确认应答之间的关系
对于这个问题,本人在这里直接说明。有没有连接,和是否确认应答,没有任何关系。
所谓有连接,有下面三点:
1.需要将连接先建立好,才能进行通信。
2.如果断开连接,此时就无法继续通信
3.连接建立过程中,通信双方都要各自保存好对方的信息。
确认应答,所体现的是 “可靠传输” 与是否有连接不相关。
例如,钉钉发送信息就不需要连接,当出现一个 “已读” 状态时。就表明传输成功了。
这里有一个说法,“TCP 中的三次握手,体现出了 TCP 的有连接”
这里正确的理解应该是,因为 TCP 是有连接的,所以,TCP 需要有能够建立连接 / 断开连接的功能。因而,其中的建立连接流程是 三次握手。
并不是说因三次握手才体现出的有连接。
(2). 通过客户端和服务器详细描述三次握手
在描述之前,我们要先知道客户端和服务器在这里的关系。
客户端是主动的一方。
服务器是被动的一方。
在上图中 红色 标注出来的是报文段
绿色 标注出来的是 TCP 的状态
-
红色标注解释
ACK: 确认号是否有效。
SYN: 请求建立连接,称之为同步报文段。 -
绿色标注解释
在建立连接阶段,主要需要认识两个状态:
1.LISTEN 服务器状态
这里表示服务器已经准备就绪,随时可以有客户端来建立连接。
也就是相当于,手机开机信号良好,可以打电话过来了
2.ESTABLISHED 客户端和服务器的状态。
连接建立完成,接下来就可以正常进行通信了。
相当于电话打过去,对方接通了。
2. 断开连接(四次挥手)
“挥手” 和 “握手” 一样都是形象的叫法。都是客户端和服务器之间的数据交互。
四次挥手 和 三次握手 非常类似。就是双方各自向对方发送一条断开连接的请求,在各自给对方一个回应。
通过上面的图像,我们一样可以提出一个问题,这里的“四次挥手”和前面我们开始时所讲的“三次握手” 两个的图像有很大的相似之处。
(1)讨论“四次握手”中间步骤的合并问题。
所以问题就是,四次握手这里的 中间两步 是否可以合并?
对于合并,这里有一个判断准则:两个数据发送的时机相同才能合并,若时机不同,就无法合并。
阐述三次握手时情况:
三次握手中间两次的合并,就是因为同一时机。具体的来讲,三次握手的这三次交互,是纯内核完成的 (应用层感知不到,也无法干预)
服务器的系统内核收到 syn 之后,就会立刻发送 ack 同时也发送 syn。
阐述四次挥手时情况:
- 对红色方框元素解释
FIN: 通知对方,本端将会关闭,该标识称之为结束报文段。
首先我们要明确下面的这一点:FIN 的发起,不是由内核进行控制的,而是由应用程序调用 socket 的 close 方法才会触发 FIN。
在上面的图中,服务器收到来自客户端的 FIN 请求后,会立即返回一个 ACK。但是对应返回的 FIN 就需要服务器执行到对应的 close 方法后才会触发返回的 FIN (两者中间会存在一个时间差)
(2) 根据简单的 TCP 代码解释断开连接
在之前的文章中,我向大家介绍了有关 TCP 的部分相关知识。
详见: JavaEE——网络编程(TCP流编程)
在这里,我将通过之前服务器代码对 “四次挥手” 中,中间的两部分应答操作进行简单解释。
- 观察服务器读取请求代码
注意上面代码中的 break 关键词。这里的 break 决定着是否要跳出 while 循环来停止服务器。
注意上面的 hasNext 关键字。这里要进入到下面的 break 就需要通过 hasNext 关键字来判断为 false。
但是这里为什么会成为 false ?是因为流对象读取到了 EOF(文件结束标记)
为什么会收取到 EOF?这是因为内核收取到了客户端发送过来的 FIN 数据报。
在前面我们提到过,当程序执行到对应的 close 方法时,才会触发 FIN。但是在我们的 客户端 代码中,并没有显式的写 close。但是客户端进程的退出,就会触发 socket.close,也就触发 FIN。
- 服务器关闭代码
如上面的代码所示,在循环执行完毕后,执行到了下面的 close 方法。此时,服务器就会向客户端发起一个 FIN。
整体的观察上述代码,我们发现,当前的循环一结束,就立刻有 close 发起 FIN,此时 ACK 和 FIN 之间的间隔较短。此时很有可能系统将这两个包合成到一块发送。
要注意的是,虽然时间间隔较短但是任然是不可以忽略的。所以为了可以更加明显一点,可以在 close 方法之前加上 sleep 方法进行短暂的休眠。
(3) 四次挥手中的两个重要的 TCP 状态
-
CLOSE_WAIT
出现在被动发起断开连接的一方。 等待关闭。(等待调用 close 方法关闭 socket)
建立连接一定是客户端主动发起请求。
断开连接,可能是客户端主动发起请求,也可能是服务器主动发起。 -
TIME_WAIT
出现在主动发起断开连接的一方。
假设客户端主动断开连接。当客户端进入 TIME_WAIT 状态时,相当于四次挥手已经挥完了。
此时这里的 TIME_WAIT 要保持当前的 TCP 连接状态不要立即释放。
为什么不要连接释放?为什么会以 TIME_WAIT 保留一会连接?
理由就是,此时最后一个 ACK 刚刚发送出去,还没有到达,万一出现 ACK 丢包呢?
TIME_WAIT 在这里会进行等待,如果等待一段时间后,还是没有收到重传的 FIN 呢么此时就会认为,最后一个 ACK 没有丢失,于是就会彻底放弃连接。
- 对于 三次握手 和 四次挥手 的超时重传
在 三次握手 和 四次挥手 的过程中,同样是存在超时重传的。
如果最后一个 ACK 丢包了,在服务器的角度来看,服务器不知道是因为 ACK 丢失,还是自己发送的 FIN 丢失,对此,这样的情况统一视为 FIN 丢了,统一进行重传操作。
大致情况如图:
既然服务器可能会重传 FIN,客户端就需要能对这个重传的 FIN 进行 ACK 响应。
既然服务器可能要重传 FIN ,客户端就需要能够针对这个重传的 FIN 进行 ACK 响应。所以,很明显如果刚才将连接彻底释放,这样的 ACK 就无法进行了。因此使用 TIME_WAIT 状态保留一定的时间,就是为了能够处理最后一个 ACK 丢包情况,能够在收到重传的 FIN 后,进行 ACK 响应。
二、连接管理总结
通过前面的描述,我们已经知道,TCP 作为一个有连接的协议。就需要建立连接和断开连接。
建立连接: 就是三次握手。
对于三次握手的意义,有下面的三点:
1.双方建立对对方的认同。(保存对方的信息)
2.验证通信双方的发送和接受能力。
3.协商一些关键参数。
断开连接: 就是四次挥手。
四次挥手,需要重点来理解当前的挥手是四次,理解 FIN 和 ACK 的传输时机,以及 TIME_WAIT 的意义和作用。
码子不易,您小小的点赞是对我最大的鼓励!!!