JavaEE: 深入探索TCP网络编程的奇妙世界(三)
文章目录
- TCP核心机制
- TCP核心机制三: 连接管理
- 建立连接(三次握手)
- 断开连接(四次挥手)
- 三次握手/四次挥手 流程简图
TCP核心机制
书接上文~
TCP核心机制三: 连接管理
建立连接(三次握手),断开连接(四次挥手).
这里的次数指的是网络通信的次数,挥手/握手是形象的比喻(handshake,计算机中的常见术语)
网络中的握手,发送不携带业务数据(没有载荷,只有报头)的数据包,但是能起到"打招呼"的作用.
建立连接(三次握手)
要想搞懂三次握手,那就要知道三次握手要解决啥问题,为啥要三次握手,三次握手的意义何在?
答:
-
投石问路: 初步的验证通信的链路是否畅通.(这是进行可靠传输的"前提条件")
-
确认通信双方各自的发送能力和接收能力是否都正常
举个例子:
我和朋友连麦打游戏.
把麦克风看成发送能力,耳机看成接收能力,这就是确认的过程~为啥是三次握手?
- 四次是否可行?
- 可行的,中间的一次,拆成两次,也可以.但是没必要这么做.
- 两次是否可行?
- 不可行~
这样就无法完成通信双方针对各自发送能力和接收能力的验证~
- 不可行~
针对TCP来说,必须是三次握手.
其他协议,握手过程可不一定是三次~ - 四次是否可行?
-
让通信双方在进行通信之前,先对通信过程中需要用到的一些关键参数进行协商.
TCP通信时,起始数据的序号,就是通过三次握手,来协商确定的~(换而言之,TCP序号,并不是从1开始的)
每次建立连接,TCP的起始序号都不同(而且故意差别很大)这么约定的意义,在于避免出现"前朝的剑,斩本朝的官".
具体解释一下~
A和B建立连接了.
A和B传输业务数据.
在上面的过程中,可能有某个数据包"迷路"了,绕了一个大圈最终才到达对端~
当他到达的时候,已经"改朝换代"了.针对这样的"迟到"的数据包,就需要把它丢弃掉,不能按照正常的流程来处理这里的数据了.
A和B断开连接
过了一会,A和B又重新建立连接~
虽然还是 A B 两个主机的连接,但是可能是不同的应用程序.对于B来说,就需要区分当前收到的数据是"本朝"的还是"前朝"的.
如何区分呢?
答:给每个连接,都协商不同的起始的序号,如果发现收到的数据,和起始序号以及和最近收到的数据序号,都差别很大的话,那么就视为这个数据就是"前朝"的数据,把它丢掉.
小小的总结一下.
进行TCP三次握手的原因:
- 投石问路,验证通信路径上是否畅通.
- 验证通信双方的发送能力和接收能力是否正常.
- 协商重要的参数,比如TCP连接中的起始序号.
断开连接(四次挥手)
四次挥手:(优雅地)断开连接.
前面谈到"超时重传"的时候,说到了"单方面释放连接".
而这里的四次挥手则是双方各自把对端的信息删除掉.
断开连接,不一定是"客户端主动",服务器也可以主动断开.
通信双方,各自给对方发送"FIN",各自给对方返回"ACK".
三次握手,之所以是三次,是因为中间两次的交互,合并在一起了.
对于四次挥手来说,中间两次,不一定能合并(大概率不能).
对于三次挥手来说,中间的两次,ACK+SYN,都是在系统内核中,由操作系统负责进行的,时机都是在收到SYN之后,就可以合并了.
对于四次挥手来说.
ACK是内核控制的,但是FIN的触发,则是通过应用程序,调用close/进程退出,来触发的.
代码中针对socket.close() => 系统内部,就是发送FIN.
三次握手/四次挥手 流程简图
通过看图,我们可以看到:
- TCP是有状态的.
- TCP的状态转换.
这些东西很复杂,我们只要能够认识这里面的几个关键状态就可以了.
-
LISTEN
服务器进入的状态
服务器把端口绑定好,相当于进入了listen状态了.
此时服务器就已经初始化完毕,准备好随时迎接客户端了.类似于手机开机,信号良好,随时可以有人来打电话了.
-
ESTABLISHED
客户端和服务器都会进入的状态.
表示TCP连接建立完成(保存了对方的信息了)
接下来就可以进行业务数据的通信了.类似于电话接通,可以说话了.
-
CLOSE_WAIT
被动断开连接的一方,会进入这个状态.
先收到FIN的一方,“等待代码执行close方法”如果发现,服务器这端,存在在大量的CLOSE_WAIT状态的TCP连接,说明什么?
此时说明服务器代码可能有bug.
排查close是否写了,以及是否及时执行到了. -
TIME_WAIT
主动断开连接的一方,会进入这个状态.
此处的TIME_WAIT按照时间来等待,达到一定时间之后,连接也就释放了.为啥不直接释放,而是要等待一定时间呢?
就是为了防一手最后的ACK丢包~
TIME_WAIT存在的时间,称为MSL(MSL => 数据报在网络传输中消耗的最大时间)
MSL在不同的系统不一样,都是可配置的.
比如Linux默认的值为60s.正常来说,网络数据,是不会消耗这么久的时间的.
最后再给一张TCP状态转换的一个汇总(简单了解一下就行):
本文到这里就结束啦~