当我们在客户端调用connect()函数的时候, 三次握手就自动进行了, 我们来看一下具体过程
序列号:在建立连接时由计算机生成的随机数作为其初始值,通过 SYN 包传给接收端主机,每发送一次数据,就「累加」一次该「数据字节数」的大小。用来解决网络包乱序问题。
确认应答号:指**下一次「期望」收到的数据的序列号**,发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。用来解决丢包的问题。
控制位:
1
时,「确认应答」的字段变为有效,TCP 规定除了最初建立连接时的 SYN
包之外该位必须设置为 1
。1
时,表示 TCP 连接中出现异常必须强制断开连接。1
时,表示希望建立连接,并在其「序列号」的字段进行序列号初始值的设定。1
时,表示今后不会再有数据发送,希望断开连接。当通信结束希望断开连接时,通信双方的主机之间就可以相互交换 FIN
位为 1 的 TCP 段。Tcp连接是双向连接,客户端和服务器需要分别向对方发送连接请求,并且建立连接,三次握手成功之后,二者之间的双向连接也就成功建立了。如果要保证三次握手顺利完成,必须要满足以下条件:
看一下具体流程:
一开始,客户端和服务端都处于 CLOSE
状态。先是服务端主动监听某个端口,处于 LISTEN
状态
客户端会随机初始化序号(client_isn
),将此序号置于 TCP 首部的「序号」字段中,同时把 SYN
标志位置为 1
,表示 SYN
报文。接着把第一个 SYN 报文发送给服务端,表示向服务端发起连接,**该报文不包含应用层数据,**之后客户端处于 SYN-SENT
状态。
服务端收到客户端的 SYN
报文后,首先服务端也随机初始化自己的序号(server_isn
),将此序号填入 TCP 首部的「序号」字段中,其次把 TCP 首部的「确认应答号」字段填入 client_isn + 1
, 接着把 SYN
和 ACK
标志位置为 1
。最后把该报文发给客户端,该报文也不包含应用层数据,之后服务端处于 SYN-RCVD
状态。
客户端收到服务端报文后,还要向服务端回应最后一个应答报文,首先该应答报文 TCP 首部 ACK
标志位置为 1
,其次「确认应答号」字段填入 server_isn + 1
,最后把报文发送给服务端,这次报文可以携带客户到服务端的数据,之后客户端处于 ESTABLISHED
状态。
服务端收到客户端的应答报文后,也进入 ESTABLISHED
状态。
从上面的过程可以发现**第三次握手是可以携带数据的,前两次握手是不可以携带数据的**,这也是面试常问的题。
一旦完成三次握手,双方都处于 ESTABLISHED
状态,此时连接就已建立完成,客户端和服务端就可以相互发送数据了。
四次挥手是断开连接的过程,需要双向断开,关于由哪一端先断开连接是没有要求的。通信的两端如果想要断开连接就需要调用close()函数,当两端都调用了该函数,四次挥手也就完成了。
FIN
标志位被置为 1
的报文,也即 FIN
报文,之后客户端进入 FIN_WAIT_1
状态。ACK
应答报文,接着服务端进入 CLOSE_WAIT
状态。ACK
应答报文后,之后进入 FIN_WAIT_2
状态。FIN
报文,之后服务端进入 LAST_ACK
状态。FIN
报文后,回一个 ACK
应答报文,之后进入 TIME_WAIT
状态ACK
应答报文后,就进入了 CLOSE
状态,至此服务端已经完成连接的关闭。2MSL
一段时间后,自动进入 CLOSE
状态,至此客户端也完成连接的关闭。你可以看到,每个方向都需要一个 FIN 和一个 ACK,因此通常被称为四次挥手。
这里一点需要注意是:主动关闭连接的,才有 TIME_WAIT 状态。
TCP 建立连接时,通过三次握手能防止历史连接的建立,能减少双方不必要的资源开销,能帮助双方同步初始化序列号。序列号能够保证数据包不重复、不丢弃和按序传输。
不使用「两次握手」和「四次握手」的原因:
FIN
时,仅仅表示客户端不再发送数据了但是还能接收数据。FIN
报文时,先回一个 ACK
应答报文,而服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送 FIN
报文给客户端来表示同意现在关闭连接。从上面过程可知,服务端通常需要等待完成数据的发送和处理,所以服务端的 ACK
和 FIN
一般都会分开发送,因此是需要四次挥手。
但是在特定情况下,四次挥手是可以变成三次挥手的