网络通信(四):TCP 三次握手与四次挥手

1、概述

TCP 提供面向有连接的通信传输。面向有连接是指在数据通信开始之前先做好两端之间的准备工作。

所谓三次握手是指建立一个 TCP 连接时需要客户端和服务器端总共发送三个包以确认连接的建立。在socket编程中,这一过程由客户端执行connect来触发。

为了保证服务端能收接受到客户端的信息并能做出正确的应答而进行前两次(第一次和第二次)握手,为了保证客户端能够接收到服务端的信息并能做出正确的应答而进行后两次(第二次和第三次)握手。

2、三次握手过程

ACK : TCP协议规定,只有ACK=1时有效,也规定连接建立后所有发送的报文的ACK必须为1

SYN(SYNchronization) : 在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文。对方若同意建立连接,则应在响应报文中使SYN=1和ACK=1. 因此, SYN置1就表示这是一个连接请求或连接接受报文。

FIN (finish)即完成、终结的意思, 用来释放一个连接。当 FIN = 1 时,表明此报文段的发送方的数据已经发送完毕,并要求释放连接。

在这里插入图片描述
(1)第一次握手:创建连接请求
客户端发送创建连接请求报文段,
该报文段的头部中SYN=1,seq=x。请求发送后,客户端便进入SYN-SENT状态。

SYN=1 表示该报文段为创建连接请求报文。

x 为本次TCP通信的字节流的初始序号

(2)第二次握手:服务器收到SYN报文段

服务端收到连接请求报文段后,如果同意连接,则会发送一个应答:SYN=1,ACK=1,seq=y,ack=x+1。 此时服务器进入SYN_RECV状态。

SYN=1,ACK=1 表示该报文段为同意创建连接的应答报文。

seq=y 表示服务端作为发送者时,发送字节流的初始序号。

ack=x+1 表示服务端希望下一个数据报发送序号从x+1开始的字节。

(3)第三次握手:客户端收到服务器的SYN+ACK报文段

当客户端收到创建连接同意的应答后,还要向服务端发送一个确认报文段,告诉服务端发来的连接同意应答已经成功收到。

该报文段的头部为:ACK=1,ack=y+1。

客户端发完这个报文段后便进入ESTABLISHED状态,服务端收到这个确认应答后也进入ESTABLISHED状态,此时连接的建立完成。

3、四次挥手过程

TCP连接是双向的,因此在四次挥手中,前两次挥手用于断开一个方向的连接,后两次挥手用于断开另一方向的连接。

(1)第一次挥手:客户端向服务端发送一个FIN报文段:FIN=1,seq=u,客户端进入FIN_WAIT_1状态,表示客户端没有数据要发送给服务端了。

FIN=1,表示要关闭一个方向的连接

(2)第二次挥手:服务端收到了客户端发送的FIN报文段,向客户端回一个ACK报文段,ACK=1,ack=u=1,服务端告诉客户端同意关闭请求,客户端进入FIN_WAIT_2状态。

(3)第三次挥手:服务端向客户端发送FIN报文段,请求关闭连接,同时服务端进入LAST_ACK状态;

(4)第四次挥手:客户端收到服务端发送的FIN报文段,向服务端发送ACK报文段,然后客户端进入TIME_WAIT状态;服务端收到客户端的ACK报文段以后,就关闭连接;此时,客户端等待2MSL后依然没有收到回复,则证明Server端已正常关闭,客户端也可以关闭连接了。

4、为什么连接建立需要三次握手,而不是两次握手?

防止失效的连接请求报文段被服务端接收,从而产生错误。

失效的连接请求:若客户端向服务端发送的连接请求丢失,客户端等待应答超时后就会再次发送连接请求,此时,上一个连接请求就是失效的。

如果建立连接只需两次握手,客户端并没有太大的变化,仍然需要获得服务端的应答后才进入ESTABLISHED状态,而服务端在收到连接请求后就进入ESTABLISHED状态。

如果网络拥塞,客户端发送的连接请求迟迟到不了服务端,客户端便超时重发请求,如果服务端正确接收并确认应答,双方便开始通信,通信结束后释放连接。

如果那个失效的连接请求抵达了服务端,由于只有两次握手,服务端收到请求就会进入ESTABLISHED状态,等待发送数据或主动发送数据。但此时的客户端早已进入CLOSED状态,服务端将会一直等待下去,这样浪费服务端连接资源。

5、为什么连接的时候是三次握手,关闭的时候却是四次握手?

创建连接时,当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。

关闭连接时,当Server端收到FIN报文时,并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,“FIN报文我收到了”。只有等到Server端所有的报文都发送完了,才能发送FIN报文,因此不能一起发送。故需要四步握手。

6、为什么客户端要先进入TIME-WAIT状态,等待2MSL(最大报文段生存时间))时间后才进入CLOSED状态?

为了保证服务端能收到客户端的确认应答,如果客户端发完确认应答后直接进入CLOSED状态,那么如果该应答丢失,服务端等待超时后就会重新发送连接释放请求,但此时客户端已经关闭了,不会作出任何响应,因此服务端永远无法正常关闭。

7、TCP 报文格式

在这里插入图片描述

(1)序号:seq序号,占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记。

(2)确认序号:ack序号,占32位,只有ACK标志位为1时,确认序号字段才有效,ack=seq+1。

(3)标志位:共6个,即URG、ACK、PSH、RST、SYN、FIN等,具体含义如下:

(A)URG:紧急指针(urgent pointer)

  (B)ACK:确认序号

  (C)PSH:接收方应该尽快将这个报文交给应用层

  (D)RST:重置连接

  (E)SYN:发起一个新连接

  (F)FIN:释放一个连接

需要注意的是:

不要将确认序号ack与标志位中的ACK搞混了

确认方ack=发起方seq+1

8、TCP 状态说明

LISTEN :监听来自远方TCP端口的连接请求。
SYN-SENT :在发送连接请求后等待匹配的连接请求。
SYN-RECEⅣED :在收到和发送一个连接请求后等待对连接请求的确认。
ESTABLISHED :代表一个打开的连接,数据可以传送给用户。
FIN-WAIT-1 :等待远程TCP的连接中断请求,或先前的连接中断请求的确认。
FIN-WAIT-2 :从远程TCP等待连接中断请求。
CLOSE-WAIT :等待从本地用户发来的连接中断请求。
CLOSING :等待远程TCP对连接中断的确认。
LAST-ACK :等待原来发向远程TCP的连接中断请求的确认。
TIME-WAIT :等待足够的时间以确保远程TCP接收到连接中断请求的确认。
CLOSED :没有任何连接状态。