三次握手与四次挥手
通过TCP/IP协议的学习,我们可以知道TCP协议是一种面向连接的、可靠的传输协议。其中,为了保证客户端与服务器连接的有效性,就有了本篇文章所要介绍的“三次挥手”;而“四次挥手”则是为了保证连接的正确断开。
1. TCP状态
首先,介绍一下TCP的几个状态:
- SYN —— 同步序列编号,在建立连接时发送
- ACK —— 确认信息,在确认SYN信息时发送,响应信息
- FIN —— 关闭连接
- RST —— 连接重置
- PSH —— 有数据传输
- URG —— 紧急指针字段值有效
2. 三次握手
建立TCP连接时会经过如下步骤:
- 服务器准备接收客户端连接(通过socket API),由于连接是由客户端激发的,因此称为
被动打开
- 客户端调用connect开始
主动打开
,并发送SYN(syn = i)
包,告诉服务器发送数据的序列号 - 服务器确认(ACK,ack = i+1)客户端发来的信息(SYN),并发送
SYN(syn = j)
,其中含有服务器发送数据的初始序列号。注:SYN
和ACK
是同时发送的,在一个数据包中 - 客户端确认服务器发送的SYN,发送
ACK(ack = j+1)
数据包
其中,因为SYN需要占据一个字节的序列号空间,因此ACK中确认号为发来的SYN序列号加1;类似,FIN的ACK确认号为该FIN序列号加1。
3. 四次挥手
- 某进程先调用close,该端TCP发送FIN(fin = i),表示数据发送完毕,需要关闭连接。称为
主动关闭
- 接收到FIN的对端执行
被动关闭
。进行确认(ACK, ack = i+1),在该进程接收的其他所有数据之后添加文件结束符(end-of-file) - 一段时间之后(因为要处理已经接收到的数据),接收到文件结束符的进程调用close关闭套接字,这导致它的TCP也发送FIN(fin = j)
- 主动关闭连接的一端确认(ACK, ack = j+1)最后发送的FIN
为什么建立连接要三次握手,而断开连接要四次挥手?
建立连接时,因为发起连接的一端在发出请求后,连接建立之前,就不会再发出任何数据,因此接收连接请求的对端可以将ACK、SYN放在一个数据包里发回给请求端,即需要三次数据发送。而断开连接时,在一端主动断开连接并发送FIN包后,对端接收到发来的FIN包,进行确认(ACK),然而此时服务器可能还在给另一端发送数据,只有在数据发送完后才能断开连接,发送FIN包(所以不能像三次握手时那样将ACK、SYN同时发送),另一端收到FIN后再进行确认并发送ACK,因此需要四次数据发送。
TCP建立的连接为全双工通道,可以双向传输数据,因此在建立连接、断开连接时,需要两端都要进行请求与确认。
为什么建立连接的ACK、SYN可以同时发送,而断开连接则需要分开发送?
建立、断开连接都需要两次请求与确认,只是建立连接时SYN、ACK可以同时发回,而在断开连接时,由于被动关闭的一端可能还会发送数据,只有数据发完后才能发送ACK,所以只能分开发送,也就造成了建立连接为三次,断开连接反而成了四次。(建立连接时由于握手期间连接未完全建立,故不能发送其他数据)
简单说,由于分组交换机制,由A发给B的数据所经过的链路
可能与B返回给A的数据所经过的链路
不同,因此即便在B接收到A的数据并返回给A应答后,仅能保证A-->B的链路
是通的,却不能保证A一定可以接收到B的应答,因此需要A再给B一个应答来确保链路两端都收到了相应的数据。