TCP的三次握手与四次挥手
TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的通信协议,数据在传输前要建立连接,传输完毕后还要断开连接。
客户端在收发数据前要使用 connect() 函数和服务器建立连接。建立连接的目的是保证IP地址、端口、物理链路等正确无误,为数据的传输开辟通道。
TCP数据报结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DXmW4nlp-1581085715249)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1581077086032.png)]
几个重要的字段:
- 序号:Seq( Sequence Number ) 序号占32位,用来标识从计算机A发送到计算机B的数据包的序号,计算机发送数据时对此进行标记。
- 确认号:Ack( Acknowledge Number ) 确认号占32位,客户端和服务器端都可以发送,Ack = Seq + 1。
- 标志位:每个标志位占用1Bit,共有6个, 分别为 URG、ACK、PSH、RST、SYN、FIN,具体含义如下:
- URG:紧急指针(urgent pointer)有效。
- ACK:确认序号有效。
- PSH:接收方应该尽快将这个报文交给应用层。
- RST:重置连接。
- SYN:建立一个新连接。
- FIN:断开一个连接。
对英文字母缩写的总结:Seq 是 Sequence 的缩写,表示序列;Ack(ACK) 是 Acknowledge 的缩写,表示确认;SYN 是 Synchronous 的缩写,愿意是“同步的”,这里表示建立同步连接;FIN 是 Finish 的缩写,表示完成。
TCP三次握手,建立连接
TCP建立连接时要传输三个数据包,俗称三次握手(Three-way Handshaking)
- [Shake 1] 套接字A:“你好,套接字B,我这里有数据要传送给你,建立连接吧。”
- [Shake 2] 套接字B:“好的,我这边已准备就绪。”
- [Shake 3] 套接字A:“谢谢你受理我的请求。”
使用 connect() 建立连接时,客户端和服务器端会相互发送三个数据包,请看下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sKEKR57C-1581085715250)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1581077527135.png)]
1)第一次握手:建立连接,客户机发出SYN包,随机生成一个x,使seq=x,并进入SYN-SENT状态,等待服务器确认
2)第二次握手:服务器收到SYN包,也创建一个SYN包,并2置SYN和ACK位为1,再随机生成一个y,使seq=y,将ack=x+1,发送给客户端,进入SYN-RECV状态
3)第三次握手:客户端收到数据包,检测SYN和ACK位,知道是服务器发送的确认包,同时创建一个ack=y+1的数据包发送给服务器,进入ESTABLISED状态,表示连接已经建立
4)服务器收到数据包,检查ack字段是否为y+1,也进入ESTABLISED状态
**重要说明:**三次握手的关键是要确认对方收到了自己的数据包,这个目标就是通过“确认号(Ack)”字段实现的。计算机会记录下自己发送的数据包序号 Seq,待收到对方的数据包后,检测“确认号(Ack)”字段,看Ack = Seq + 1
是否成立,如果成立说明对方正确收到了自己的数据包。
TCP四次握手断开连接
建立连接需要三次握手,断开连接需要四次握手,可以形象的比喻为下面的对话:
- [Shake 1] 套接字A:“任务处理完毕,我希望断开连接。”
- [Shake 2] 套接字B:“哦,是吗?请稍等,我准备一下。”
- 等待片刻后……
- [Shake 3] 套接字B:“我准备好了,可以断开连接了。”
- [Shake 4] 套接字A:“好的,谢谢合作。”
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PTK9XsEX-1581085715251)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1581078272993.png)]
1)客户端发出连接释放报文,即FIN数据包,进入FIN_WAIT_1状态(seq=u)
2)服务器收到数据包,检测FIN标志位后,先发送“确认包”,seq=v,ack=u+1,进入CLOSE_WAIT状态
注意:服务器收到请求后并不是立即断开连接,而是先向客户端发送“确认包”,告诉它我知道了,我需要准备一下才能断开连接。
3)客户端收到数据包后进入FIN_WAIT_2状态,等待服务器再次发送数据包
4) 等待片刻后,服务器准备完毕,可以断开连接,于是再主动向客户端发送 FIN 包, seq=w,ack=u+1,然后进入LAST_ACK状态
5)客户端收到FIN包后,再次发送ACK包,ack=w+1,seq=u+1,然后进入TIME_WAIT状态
TIME_WAIT状态需要等待2MSL才会进入CLOSE状态,目的:为了保证服务器收到ACK包,如果服务器未收到,会重传上一个FIN包(在2MSL内)
6)服务器收到ACK包,就断开连接,进入CLOSE状态
常见的面试问题
【问题1】为什么连接的时候是3次握手,关闭的时候是4次握手?
答:因为当Server端收到Client端的SYN连接请求时,可以直接发送SYN和ACK报文,其中ACK用来应答,SYN用来同步。但在关闭连接时,当Server端收到FIN,可能并不会立即关闭SOCKET,所以只能回复一个ACK报文表示收到了SYN包,等待Server端的所有数据发送完了,才会发送FIN报文,因此需要四次握手。
【问题2】为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
答:**MSL就是报文最大生存时间, 2MSL就是一个发送和一个回复所需的最大时间。 ** TCP 是面向连接的传输方式 , 必须保证数据能够正确到达目标机器,不能丢失或出错 ,而网络是不可靠的,有可能最后一个ACK丢失了,TIME_WAIT状态就是用来重发丢失的报文的。在Client发送出最后ACK确认包后,如果Server未收到,就会不断地重复发送FIIN数据包。Client会设置一个计时器,等待2MSL的时间,在该时间段内再次收到FIN包,那么Client会重发ACK并再次等待2MSL。 如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。
【问题3】 为什么不能用两次握手进行连接?
答:三次握手需要完成2个重要的功能,既要双方做好发送数据的准备工作(双发都知道彼此已准备好了),也要允许双方就初始序列号进行协商,这个seq需要在握手过程中发送和确认。
如果改成2次握手连接可能会发生死锁。当C给S发送一个SYN包,S收到并发送确认应答包。如果只有2次,此时S认为连接已经建立就开始发送数据,但这个确认应答包如果在传输过程中丢失了,C没有收到便无法知道S是否已经准备好,不知道S建立怎样的序列号甚至怀疑S释放收到自己的连接请求。在这种情况下,C认为连接未成功,将忽略S发来的任何数据包,只等待确认应答包,而S在发出数据包超时后,重复发出同样的分组,这就形成了死锁。
【问题4】如果已经建立了连接,但是Client突然出现故障了怎么办?
TCP还设有保活计时器,如果客户端出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2个小时,若两个小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没有反应,服务器就认为客户端出了故障,接着就关闭连接。