00. 目录


文章目录


01. 三次握手

在 TCP/IP 协议中,TCP 协议提供可靠的连接服务,采用三次握手建立一个连接。

【Linux网络编程】TCP三次握手和四次挥手_TCP三次握手

三次握手的过程:

第一步:​ A 的 TCP 向 B 发出连接请求报文段,其首部中的同步位 SYN = 1,并选择序号 seq = x,表明传送

数据时的第一个数据字节的序号是 x。

第二步​ B 的 TCP 收到连接请求报文段后,如同意,则发回确认。B 在确认报文段中应使 SYN = 1,使 ACK = 1,

其确认号ack = x + 1,自己选择的序号 seq = y。

第三步​ A 收到此报文段后向 B 给出确认,其 ACK = 1,确认号 ack = y + 1。A 的 TCP 通知上层应用进程,连接已经建立。

B 的 TCP 收到主机 A 的确认后,也通知其上层应用进程:TCP 连接已经建立。

通过这样的三次握手,客户端与服务端建立起可靠的双工的连接,开始传送数据。 三次握手的最主要目的是保证连接是双工的,可靠更多的是通过重传机制来保证的。但是为什么一定要进行三次握手来保证连接是双工的呢,一次不行么?两次不行么?

同理对于TCP为什么需要进行三次握手我们可以一样的理解:

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

02. 四次挥手

​由于 TCP 连接是全双工的,因此每个方向都必须单独进行关闭​​。这好比,我们打电话(全双工),正常的情况下(出于礼貌),通话的双方都要说再见后才能挂电话,​保证通信双方都把话说完了才挂电话​。

第一步

【Linux网络编程】TCP三次握手和四次挥手_四次握手_02

数据传输结束后,通信的双方都可释放连接。现在 A 的应用进程先向其 TCP 发出连接释放报文段,并停止再发送数据,主动关闭 TCP 连接。

A 把连接释放报文段首部的 FIN = 1,其序号seq = u,等待 B 的确认。

第二步

B 发出确认,确认号 ack = u + 1,而这个报文段自己的序号 seq = v。

TCP 服务器进程通知高层应用进程。

从 A 到 B 这个方向的连接就释放了,TCP 连接处于半关闭状态。

B 若发送数据,A 仍要接收

第三步

若 B 已经没有要向 A 发送的数据, 其应用进程就通知 TCP 释放连接。

第四步

A 收到连接释放报文段后,必须发出确认。

在确认报文段中 ACK = 1,确认号 ack = w + 1,自己的序号 seq = u + 1。

03. 三次握手和四次挥手原因

为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?

这是因为服务端的 LISTEN 状态下的 SOCKET 当收到 SYN 报文的建连请求后,它可以把 ACK 和 SYN(ACK 起应答作用,而 SYN 起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的 FIN 报文通知时,它仅仅表示对方没有数据发送给你了,但是你还可以给对方发送数据,也有这么种可能,你还有一些数据在传给对方的途中,所以你不能立马关闭连接,也即你可能还需要把在传输途中的数据给对方之后,又或者,你还有一些数据需要传输给对方后,(再关闭连接)再发送FIN 报文给对方来表示你同意现在可以关闭连接了,所以它这里的 ACK 报文和 FIN 报文多数情况下都是分开发送的。

04. 2MSL

为什么 TIME_WAIT 状态还需要等 2MS L后才能返回到 CLOSED 状态?

这是因为虽然双方都同意关闭连接了,而且握手的 4 个报文也都协调和发送完毕,按理可以直接回到 CLOSED 状态(就好比从 SYN_SEND 状态到 ESTABLISH 状态那样);但是因为我们必须要假想网络是不可靠的,你无法保证你最后发送的 ACK 报文会一定被对方收到,因此对方处于 LAST_ACK 状态下的 SOCKET 可能会因为超时未收到 ACK 报文,而重发 FIN 报文,所以这个 TIME_WAIT 状态的作用就是用来重发可能丢失的 ACK 报文。(里面涉及的状态是什么意思,详情请看《TCP 通信过程中各步骤的状态》)

05. 附录

5.1​​【Linux】一步一步学Linux网络编程教程汇总​