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