为什么是三次握手而不是两次握手(三次握手和四次挥手到底是个什么鬼东西)
之前总有是有面试官喜欢问,你知道什么是三次握手么?什么是四次挥手么?为什么握手需要三次,挥手需要四次呢?今天我们就来详细的聊一下这个。
1.什么是TCPTCP协议,简单称呼一下的话,那就是传输控制协议,为什么这么称呼它呢,因为这个协议就是用来对数据的传输进行控制的一个协议,这个大家肯定也都是没有异议的。
TCP有时候你会在很多书中看它们称之为“套接字”,其实这就是翻译,在原著中的意思可能就是 a place on a surface or machine with holes for connecting a piece of electrical equipment.,然后经过翻译的手,翻译过来就是套接字的意思,其实大家心里清楚就行,问这个的几乎不怎么存在。所以大家就是知道就可以了。
我们也都知道网络协议是分层的,7层(5层),可以分为不标准的7层,也可以分为标准的五层(也有人说是4层,区别不大,就少了一个物理层面),实际上这个标准阿粉个人感觉还是笼统的和细微的差别就像这个图。
而这个分层的概念则是不同的,如果说你是按照OSI七层模型结构体来分,那就是7层,如果是按照TCP/IP,那么就是4层。在这里的TCP,就是在我们的数据传输层里面,因为毕竟阿粉之前就说了,传输控制协议嘛。
2.TCP协议的报文而在TCP/IP的分层中,就算是数据传输层,那也是有着不是TCP协议的存在的,比如说还有UDP,就像下图。
TCP和UDP是两种最为著名的运输层协议,二者都使用IP作为网络层协议。
尽管TCP使用的是不可靠的IP服务,但是它提供的传输层服务,却是更加可靠的。
那么我们就先来看看这个TCP协议的报头是什么样子的,把抽象的东西具体化一点,才能更加的加深理解。
TCP数据被封装在一个IP数据报中,就像上图所示,而我们需要分析的,就是其中的TCP数据报中。
每个TCP段都包含源端和目的端的端口号,用于寻找发端和收端应用进程。这两个值加上IP首部中的源端IP地址和目的端IP地址唯一确定一个TCP连接。
这个时候我们就得来看看里面都有些什么东西了,
- 16位源端口号和16位目的端口号:其实就相当于是一个插口,也可以称之为数据的来源进程和目的进程
- 32位序号:序号用来标识从T C P发端向TCP收端发送的数据字节流,它表示在这个报文段中的的第一个数据字节
- 4位首部长度:表示该tcp报头有多少个4字节(32个bit)
- 6位标志位:这个是重头戏
U R G 紧急指针有效
A C K 确认序号有效
P S H 接收方应该尽快将这个报文段交给应用层
R S T 重建连接
S Y N 同步序号用来发起一个连接。这个标志和下一个标志将在第 1 8章介绍
F I N 发端完成发送任务
- 16位窗口大小:窗口大小为字节数,起始于确认序号字段指明的值,这个值是接收端正期望接收的字节。
- 16位紧急指针:主要是看什么数据是紧急的
- 16位检验和:16位检验和覆盖了整个的TCP报文段:TCP首部和TCP数据。这是一个强制性的字段,一定是由发端计算和存储,并由收端进行验证。
既然我们文章要说的是TCP的三次握手,和四次挥手,那么肯定是说的连接,也不是说的其他的。那么它这个连接的过程说的是什么呢?
我们还是从图中理解,这样比较好理解,
- TCP第一次握手:服务端的TCP进程先创建传输控制块TCB,准备接受客户端进程的连接请求,然后服务端进程处于LISTEN状态,等待客户端的连接请求,向服务端发出连接请求报文段,该报文段首部中的SYN=1,ACK=0,同时选择一个初始序号 seq=i。TCP规定,SYN=1的报文段不能携带数据,但要消耗掉一个序号。这时,TCP客户进程进入SYN—SENT(同步已发送)状态。
简单的来说SYN—SENT状态,同步已发送状态,这是第一次握手的时候的状态。
- TCP第二次握手:服务端收到客户端发来的请求报文后,如果同意建立连接,则向客户端发送确认。确认报文中的SYN=1,ACK=1,确认号ack=i 1,同时为自己 选择一个初始序号seq=j。同样该报文段也是SYN=1的报文段,不能携带数据,但同样要消耗掉一个序号。这时,TCP服务端进入SYN—RCVD(同步收到)状态
这个第二次握手就会进入到同步收到状态。
- TCP第三次握手:客户端进入ESTABLISHED(已建立连接)状态,TCP客户端进程收到服务端进程的确认后,还要向服务端给出确认。确认报文段的ACK=1,确认号ack=j 1,而自己的序号为seq=i 1。TCP的标准规定,ACK报文段可以携带数据,但如果不携带数据则不消耗序号,因此,如果不携带数据,则下一个报文段的序号仍为seq=i 1。
而当第三次握手连接完成的时候,已经标志了现在是已经完全的建立了连接,而这个时候就可以进行数据传递了。
有时候就有人会问了,为什么是三次握手而不是两次,也不是四五次呢?一般情况下问这种问题的都会是面试官,如果你在面试过程中已经把这个三次握手说完了之后,他有时候就会问这种问题,让你谈谈你自己的理解,那么为什么呢?
在RFC 793 指出的 TCP 连接使用三次握手的首要原因:he principle reason for the three-way handshake is to prevent old duplicate connection initiations from causing confusion.
翻译过来就是三次握手的主要原因是防止旧的重复连接启动引起混淆。
也就是说,如果客户端连续发出多个SYN建立连接的报文的话,在网络拥堵的情况就会出现,一个「旧 SYN 报文」比「最新的 SYN 」 报文早到达了服务端,那么此时服务端就会回一个 SYN ACK 报文给客户端,客户端收到后可以根据自身的上下文,判断这是一个历史连接(序列号过期或超时),那么客户端就会发送 RST 报文给服务端,表示中止这一次连接。
而如果是两次握手,那么完蛋了,这时候判断不出这个连接是不是历史连接,中断还是不中断,这就没办法处理了,而三次握手就可以在客户端进行第三次发送报文的时候,有足够的上下文来判断这个连接到底是否属于历史连接。
那么为什么不是四次连接呢?大家可以继续翻到上面的图,如果是四次连接,那么也就是说,把ACK和SYN进行了分开,seq=y和ack=x 1这两步进行了分开,虽然四次握手也能够完成这一步,但是为了省事,人家还是三部就做完了,这样一来,也能确保双方的初始序列号能被可靠的同步,何必在多费一步操作呢?
4.TCP连接的四次挥手既然我们TCP连接的时候进行了三次握手,为什么要中断的时候,我们要进行四次挥手呢?这还是得从图中来理解这个事情。
- 客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送
- 服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1(报文段5)。和SYN一样,一个FIN将占用一个序号。
- 服务器B关闭与客户端A的连接,发送一个FIN给客户端A
- 客户端A发回ACK报文确认,并将确认序号设置为收到序号加1
那么为什么是四次呢?之前阿粉面试别人的时候,有个哥们给我了一句话,由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这话比较笼统,但是没解释清楚为啥要做四次挥手,这是最尴尬的,然后问详细的样子是什么样的,他也解答的不错。
那么为什么呢?
这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。
也就是说,在关闭的时候,为了确认是否关闭连接,ACK的报文和FIN的报文是进行分开发送,而这时候,挥手的次数也就从三次变成了4次,这样是不是就好理解一点了。
5. 文章参考
- 《TCP/IP详解》
- 《详解 TCP 连接的“ 三次握手 ”与“ 四次挥手 ”》
- 《TCP-IP详解卷一:协议》
- 《JAVA网络编程(第3版)》
免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com