面试准备——计算机网络(TCP的三次握手和四次挥手)
一、TCP的报文结构
红色圈标出的是在讨论三次握手和四次挥手时会用到的首部字段:

- 顺序号(seq):TCP对从网络层传下来的数据报文进行分组,分成一段一段的TCP报文段,并对这些报文段进行编号。seq为该TCP报文段的序号。
- 应答号(ack):期望收到的对方的报文段的序号,用来对已经收到的报文进行确认,如果ack=a+1,那么表示seq<=a的报文都已经收到了。
- ACK:应答号有效性标志(只有当ACK=1时,ack字段才有效)。一旦一个连接建立起来,该标志总被置为1。
- SYN:同步序号标志(建立连接时候使用)。
- FIN:传输数据结束标志(断开连接时使用)。
二、建立TCP连接——三次握手
2.1 三次握手的过程
通信双方都可以发起连接。但是在Client-Server模型中,一般都是客户端主动向服务器发起连接,所以这里以这样的例子做演示。

0. 最初服务器处于LISTEN状态。
1.(第1次握手) 客户端向服务器发送连接请求报文(SYN=1, ACK=0, seq=0),之后客户端进入SYN-SENT状态。
- ACK=0表示该报文中的ack字段是无效的,因为这个报文是用来请求建立一个新的连接的,并不是对ack所指报文段的应答。
- seq=0表示这是新会话中客户端发出的的第一个报文段。
2. (第2次握手)服务器收到连接请求报文后,如果同意建立连接,则发出确认连接报文(SYN=1, ACK=1, seq=0, ack=1),之后服务器进入SYN-RECEIVED状态。
- seq=0表示这是新会话中服务器发出的第一个报文段。
- ack=1表示服务器已经收到客户端发出的序号为0的报文了。(ACK=1表示这个ack是有效的)
3. (第3次握手)客户端收到服务器的确认连接报文之后,还要再发出一个确认 收到服务器的确认连接报文的 确认报文(ACK=1, seq=1, ack=1),之后客户端进入ESTABLISHED状态,即客户端认为连接已经建立了。
4. 服务器收到来自客户端的确认报文之后进入ESTABLISHED状态,表示服务器认为连接已经建立了。
2.2 为什么要进行3次握手?
理解角度一:
三次握手的目的是建立可靠的通信信道,所谓可靠的通信信道最基本的要求就是通信双方的数据发送和接收都是正常的。
因此,三次握手的主要目的是双方确认自己与对方的发送和接收都是正常的。
| 客户端 | 服务器 | |
| 第1次握手 | —— | 客户端发送了一个报文并且我收到了-->客户端发送正常;服务器接受正常(从客户端到服务器的单向连接正常) |
| 第2次握手 |
服务器发送了一条确认报文说明我刚才发的服务器收到了-->客户端发送正常;服务器接受正常(从客户端到服务器的单向连接正常) 服务器发的这条报文我收到了-->客户端接收正常;服务器发送正常(从服务器到客户端的单向连接正常) |
—— |
| 第3次握手 | —— | 客户端发送了一条确认报文说明我刚才发的客户端收到了-->客户端接收正常;服务器发送正常(从服务器到客户端的单向连接正常) |
从这个角度理解“为什么一定要3次握手,如果是2次握手会怎样?”,答案是:如果只有两次握手,对服务器来说,只验证了从客户端到服务器的单向连接正常,而从服务器到客户端的单向连接是否正常还不知道,需要通过第3次握手来获知。
理解角度二:
通信的某一方进入ESTABLISHED状态的条件该方已经获知以下信息:①双方都确认要建立连接(双方都有建立连接的意向),②对方知道 我已经确认建立连接(对方确实会建立连接)。
注意:只知道对方有建立连接的意向是不够的,一定要确实知道他真的会建立连接才可以。而如果对方知道我已经确认连接就可以认为对方确实会建立连接。
| 客户端 | 服务器 | |
| 第1次握手 | 客户端有建立连接的意向 |
客户端发来了请求连接报文 --> 客户端有建立连接的意向; |
| 第2次握手 |
服务器发回了确认连接报文-->服务器有建立连接的意向; 服务器能发回报文说明服务器已经收到我的请求连接报文了,即服务器已经知道我有建立连接的意向了 --> 服务器确实会建立连接 |
我返回给客户端确认连接报文 --> 服务器有建立连接的意向 |
| 第3次握手 | 客户端发出了确认 收到服务器的确认连接报文的 确认报文,说明客户端收到了我发出的确认连接报文,即客户端已经知道我确认建立连接了 --> 客户端确实会建立连接 |
从这个角度理解“为什么一定要3次握手,如果是2次握手会怎样?”,答案是:如果只有两次握手,对服务器来说,并不能确定客户端确实会建立连接,所以服务器是不会贸然进入ESTABLISHED状态的。
2.3 实例

三、释放TCP连接——四次挥手
3.1 四次挥手的过程
通信双方都可以释放连接,这里以客户端先发起释放连接的申请为例。

1. (第1次挥手)客户端发出释放连接报文(FIN=1, seq=u),声明自己已经没有数据要发送了,之后进入FIN-WAIT-1状态(等待对方对FIN的回复的状态)。
2. (第2次挥手)服务器收到释放连接报文,发出确认报文(ACK=1, seq=v, ack=u+1),之后服务器进入CLOSE-WAIT状态。在这一状态下,服务器如果还有数据要发,就继续发送数据给客户端。因为这时客户端已经不会再发数据了,所以这些从服务器发出去的数据的ack字段都是u+1。
3. (第3次挥手)服务器也没有要发的数据了,就发出释放连接报文(FIN=1, ACK=1, seq=w, ack=u+1)对此进行声明,之后服务器进入LASK-ACK状态(等待对方对FIN的回复的状态)。
4. (第4次挥手)客户端收到服务器发来的释放连接报文后,发出确认报文(ACK=1, seq=u+1, ack=w+1),之后进入TIME-WAIT状态等待2MSL(Maximum Segment Lifetime)后进入CLOSED状态。
5. 服务器收到客户端发来的确认报文后进入CLOSED状态。
3.2 为什么要4次挥手?
四次挥手的目的是断开连接,那么什么时候就可以断开连接了呢?当这条通信线路已经没有用了,即这条通信线路上不会再有数据的时候就可以断开连接了。
可以认为FIN报文是用来告诉对方我已经没有数据要发了,收到FIN报文的确认报文意味着对方也知道我不会再发数据了。一次FIN报文的交互意味着“一方将不再发数据”这一信息传播到了通信线路两端。之前有一个错误的理解,认为一次FIN报文的交互意味着单向连接的断开,这是不对的。因为这是一个全双工的连接,而不是两个单工连接。所以认为第1、2次挥手之后就断开了从客户端到服务器的连接就关闭了是不正确的,这只是说客户端告诉服务器:“我已经没有数据要发了,看你还有没有数据要发,要是没了我们就断开连接吧。”纠结这一个细节的原因在于:如果认为此时单向连接断开了,那就无法合理解释第4次挥手了,因为这样的话第4次挥手的确认报文服务器是不可能收到的。
因此需要进行2次FIN报文的交互,也就是4次挥手,来保证“双方都不会再发数据”这一消息传播到了通信线路两端。
3.3 为什么第4次挥手之后还要等2MSL?
第4次挥手之后,客户端已经知道“双方都不会再发数据”,那为什么还要再等2MSL呢?这个2MSL是在等什么?
设想这样一种情形:如果第4次挥手的报文丢失了会怎么样?
如果第4次挥手的报文丢失了,第3次挥手发出的释放连接报文在一段时间内没有得到回复,服务器必然会再发一次释放连接报文。如果是这样的话,客户端就得要保证能收到重传的报文并对其做出回复(并重置2MSL计时器)。
要保证能收到重传报文,客户端需要再等多长时间?
客户端发出第4次挥手的确认报文后,如果能到在MSL时间内肯定就到了;如果到不了,MSL时间内服务器也会发出重传的报文,这个重传的报文最多需要MSL时间就能收到。所以客户端需要再等2MSL时间。这个2MSL时间是在等如果可能有的重传报文。
3.4 实例


浙公网安备 33010602011771号