TCP三次握手、四次挥手

RceWxP.md.jpg

简介:本文介绍了TCP报文结构、TCP三次握手、四次挥手的机制。传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。TCP是面向连接的,无论哪一方向另一方发送数据之前,都必须先在双方之间建立一条连接。在TCP/IP协议中,TCP协议提供可靠的连接服务,连接是通过三次握手进行初始化的。三次握手的目的是同步连接双方的序列号和确认号并交换 TCP窗口大小信息。

关键词:TCP; 三次握手;四次挥手;计算机网络;

前置知识

​ IP协议工作在网络层,是计算机与计算机之间的通信,IP协议通过IP地址识别不同的计算机;TCP协议工作在传输层,是应用程序与应用程序之间的通信,TCP协议通过端口号来识别同一台计算机上的不同程序,所示使用TCP协议提供传输服务,必须指明端口号。

IP地址 + 端口号 = socket套接字

TCP协议的学习,最基本需要掌握以下知识:

  • TCP报文结构
  • 建立连接 断开连接
  • 可靠传输
  • 流量控制
  • 拥塞控制

下面就一一介绍以上知识点。

TCP报文结构

报文结构.png

三次握手和四次挥手阶段,主要应该关心的是序号seq,确认号,标志位ACK、FIN。

TCP三次握手

客户端的疑惑:服务端能不能收到我的信息?(客户端发出去一个信息,并且收到服务端回复,才能确定服务端能收到自己的信息)

服务端的疑惑:客户端能不能收到我的信息?(同上,服务端发一个消息,并收到客户端的回复,才能确定客户端能收到自己的消息)

解决上面的疑惑就需要三次握手:

img

过程分解:

第一次握手:客户端请求建立连接(syn = 1),并随机生成序号seq,这里假定10000,这个序号一会儿用于服务器确认。

第二次握手:这时候服务器需要做,并且做了两件事:

  1. 一个是给客户端确认自己能收到,一个是服务器不知道客户端能不能收到自己的信息,它要问问。对于第一件事,服务器确认信息的做法是: ACK = 1,表示这次要确认一下上次你发的东西。确认什么呢,确认的就是第一次客户端发来的序号seq,确认的方法就是将客户端发来的seq + 1,然后弄到【确认序号】这个数据域。ACK标志位为 1 表示接收到这个TCP报文之后需要看一下 【确认序号】数据域。客户端接收以后,看见ACK是1,就去看seq,发现是自己第一次发的建立连接的请求的seq 又加了 1 ,就能确定得到正确的回复了。

​ 也即,客户端接收到服务器返回的 ACK = 1seq = 10001,就能确认:服务器能接收到我的信息。

  1. 但是服务器不知道客户端能不能接收自己的消息,所以它要干第二件事。

​ 它也要像客户端第一次发送请求一样,问问客户端能不能建立连接。于是,它自己也要发一次 syn = 1 来表示请求建立连接,自己也随机初始化一个序号,假如20000,用来给客户端一会儿确认用,如果一会儿客户端发来确认信息( ACK = 1,seq = 20001 ) ,服务器就开心了,因为他能确认客户端能收到自己的消息了。

这两件事可以放在一个TCP报文里面做,于是这次握手,服务器发给客户端的是:

ACK = 1, seq = 10001 (用于确认客户端的建立连接的请求)

Syn = 1,seq = 20000 (用于向请求客户端建立连接)

第三次握手

第二次以后,客户端接收到了服务器发来的 ACK = 1, seq = 10001 ,就知道服务器能接收消息了,此时客户端已经开心了,但是服务器还不开心。

但是它发现服务器也发来一个建立连接的请求 Syn = 1,seq = 20000 ,所以这次,客户端也赶紧给 服务器发送一个确认信息 : ACK = 1, seq = 20001 ,用来告诉服务器自己能接收到。

由于服务器上一次发了 Syn = 1,seq = 20000 ,正等着客户端应答呢,这时它收到一个客户端发来的

ACK = 1, seq = 20001 ,服务器就知道客户端能收到自己的请求了。于是服务器也开心了。

至此,他们俩一开始的困惑都解决了,互相向对方发送了试探信号,也都收到了应答,就可以开始通信了。

细节:

  • 用于试探的消息是: SYN = 1 ,再带一个随机的序号 seq

  • 用于答复对方的试探的消息是: ACK = 1 ,再加一个 【确认序号ack】,确认序号的值是对方的试探的seq + 1.试探就是请求建立连接。

  • ACK:只有当ACK=1时,确认序号字段才有效

  • 第二次握手,服务器一下子做了两件事:又确认,又试探。

TCP四次挥手

就像请求建立连接一定要 syn=1,断开连接一定要Fin=1.

过程和上面一样,接收到了FIN以后,也要给对方一个ACK = 1,ack= seq+1.应答。

注意这里的seq就不是随机的了,而是在通信中一路增长下来的。

图片.png

这里和三次握手是大同小异的,都是需要一方发送断开连接请求,另一方答复,但是为什么是4次不是3次,为什么第二次和第三次不合并呢,就像三次握手一样?

是因为客户端发送断开请求的时候,客户端一定是把所有请求都发完了,以后不会再给服务器发了。但是服务器接收到以后,不能确保自己的事情也做完了,自己可能还会给客户端发东西。所以第二次挥手,只能告诉客户端:行了我知道你要断开了,但是我还不一定要断开。

客户端收到服务端的ACK之后,不会立即断开,它也知道服务端可能还会发数据过来,所以等。直到收到服务端的FIN之后,客户端做个ACK应答之后,客户端才会真的关闭连接。

而服务端在第二次给客户端发 ACK 以及第三次给客户端发FIN之间的时间里,可能还会给客户端发一些没干完的活儿。等服务端的活都干完了,才给客户端发FIN告诉客户端自己不会再发消息了。

这就是为什么第二次和第三次不合并,因为服务器可能还有未发完的数据。

四次挥手小结

  1. 客户端的FIN只是通知对方“自己不会再发数据了”,并不能直接断开连接,因为不知道服务器还有没有消息,还要等服务端的FIN,才确定服务器该发过来的都发了。

  2. 为什么服务器给客户端发完FIN,不用等客户端的FIN?因为客户端第一次就发了FIN,服务器已经知道客户端不会再有请求了。

Q&A

【问题1】为什么连接的时候是三次握手,关闭的时候却是四次握手?

答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,”你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。

【问题2】为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?

答:虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。

【问题3:为什么需要第四次应答ACK,而不是】

注意,由于网络不可靠,每一步的数据包都可能丢失。比如挥手,第三次挥手,服务器给客户端发送FIN,丢了,客户端就总也关闭不了,因为客户端只有等到服务器的FIN才会关闭。服务器不知道自己的FIN人家有没有收到,所以需要客户端返回一个ACK说收到了。如果第三次之后,服务端没收到ACK,很可能就是FIN丢了,所以会再给客户端发一次FIN。第四次应答就是告诉服务器,刚才你的FIN没丢,我准备关闭了。

但是第四次发送的ACK也可能丢,若是ACK丢了,服务器发完FIN之后迟迟收不到应答,就以为FIN也丢了,会再次发送FIN。所以第四次的ACK之后客户端还要TIMEWAIT才能关闭,因为它怕服务端由于ACK丢失,再发个FIN过来。如果第四次之后客户端直接关闭,ACK还丢了,服务器就一直给客户端发FIN,服务器无法关闭连接。

三次握手四次挥手就先讲完了,剩下的可靠传输、流量控制、拥塞控制,下一篇再讲


参考:

简书

B站