三次握手
SYN:请求建立连接
- 首先客户端向服务端发出连接请求的报文段,其中会把同步位SYN置为1,并选择序号seq=x,表明传送数据时第一个数据字节的序号是x
TCP 连接请求报文段首部中的同步位SYN被设置为1,,表明这是一个tcp连接请求报文段。
序号字段seq被设置了一个初始值x作为TCP客户进程所选择的初始序号。
- 服务端收到连接请求报文段之后,如果同意,则发回确认。服务器在确认的报文段当中,同样也设SYN同步位为1,同时把ACK确认位设为1,而确认号ack设置为x+1,表示之前x序号的报文段已经正确地收到,期待收到x+1序号的报文段(第三次客户端发送的seq)。同时自己来选择序号seq设为y
该报文段首部中的同步位SYN和确认位ACK 都设置为1,表明这是一个TCP连接请求。 序号字段seq被设置了一个初始值y,作为TCP服务器进程所选择的初始序号。 确认号字段ack的值被设置成了x+1,这是对TCP客户进程所选择的初始序号seq的确认。
- 客户端在收到了确认报文段之后,还需要再一次给服务端发出确认,其中ACK确认位设为1,确认号ack设为y+1,同样表明之前的过来序号是y的报文已经正确地接收到了,至此三次握手的过程结束。
该报文段首部中的确认位ACK被设置为1,表明这是一个普通的TCP确认报文段 。 序号字段seq被设置为x+1,这是因为TCP客户进程发送的第一个TCP报文段的序号为x,并且不携带数据,因此第二个报文段的序号为x +1。 确认号字段ack被设置为y + 1,这是对TCP服务器进程所选择的初始序号的确认。
四次挥手
FIN:希望断开连接
- 这个时候客户端会把连接释放报文段的首部的FIN设为1,序号seq设为u,等待服务端的确认。
标记位为 FIN,表示请求释放连接;
序号为 Seq = u;
随后客户端进入 FIN-WAIT-1 阶段,即半关闭阶段,并且停止向服务端发送通信数据。
- 服务端针对这个挥手的报文会发出确认,其中确认号ack设为u+1,表示之前你要进行连接释放的报文已经正确收到了。而报文段自己的序号seq设为v(u和v仍然是相互独立的),意味着客户端这个时刻不一定需要立刻去释放TCP的连接,因为它仍然有可能有数据要去发送。
这个时候,从客户端到服务端这个方向的连接已经释放掉了,TCP连接是处于一个半关闭的状态(如果服务端仍然有数据要发送的时候,客户端仍然是需要接收的),这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
标记位为 ACK,表示接收到客户端释放连接的请求;
序号为 Seq = v;
确认号为 Ack = u + 1,表示是在收到客户端报文的基础上,将其序号值加 1 作为本段报文确认号Ack 的值;
随后服务器开始准备释放服务器端到客户端方向上的连接。
客户端收到服务器发送过来的 TCP 报文后,确认服务器已经收到了客户端连接释放的请求,随后客户端结束 FIN-WAIT-1 阶段,进入FIN-WAIT-2 阶段。
- 当服务端把自己的数据发送完毕之后,没有要再向客户端发送的数据了,应用进程就可以通知TCP释放连接,仍需要把FIN的标志位设为1
标记位为 FIN 和 ACK,表示已经准备好释放连接了;
序号为 Seq = w;
确认号 Ack = u + 1,表示是在收到客户端报文的基础上,将其序号 Seq 的值加 1 作为本段报文确认号 Ack 的值。
随后服务器端结束 CLOSE-WAIT 阶段,进入 LAST-ACK 阶段。并且停止向客户端发送数据。
- 客户端在收到连接释放报文段之后,必须发出确认。在报文段当中,设置ACK确认位设为1,确认号ack设为w+1。客户端在发送这个确认报文段当中,还会设置字节的序号seq为u+1。
标记位为 ACK,表示接收到服务器准备好释放连接的信号;
序号为 Seq= u + 1,表示是在已收到服务器报文的基础上,将其确认号 Ack 值作为本段序号的值;
确认号为 Ack= w + 1,表示是在收到了服务器报文的基础上,将其序号 Seq 的值作为本段报文确认号的值。
随后客户端开始在 TIME-WAIT 阶段等待 2 MSL(2倍最长报文段寿命)。服务器端收到从客户端发出的 TCP 报文之后结束LAST-ACK 阶段,进入 CLOSED 阶段。由此正式确认关闭服务器端到客户端方向上的连接。客户端等待完 2 MSL 之后,结束 TIME-WAIT 阶段,进入 CLOSED 阶段,四次挥手结束。
面试问题
1、TCP客户进程在发送完最后一个确认报文后,为什么不直接进入关闭状态?而是要进入时间等待状态,2MSL后才进入关闭状态,这是否有必要呢?
答:之所以要设两倍的SML时间,主要是出于两方面的考虑: 第一是为了保证客户端发送的最后一个ACK报文段能够顺利地到达服务端。 第二是为了防止已失效的连接请求报文段,出现在本连接当中。假如客户端在发送完最后一个ACK报文段之后,再经过时间2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样就可以使本次连接持续时间内所产生的所有报文段都从网络中消失,这样就可以使下一个新的TCP连接中,不会出现旧连接中的报文段。
2、如果已经建立了连接,但是客户端突然出现故障了怎么办?(保活机制)
答:TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接
3、为什么连接的时候是三次握手,关闭的时候却是四次挥手?
答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。 但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,“你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送,故需要四次挥手。
4、为什么不能用两次握手进行连接?
答:3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。
现在把三次握手改成仅需要两次握手,比如计算机S和C之间的通信,假定C给S发送一个连接请求分组,S收到了这个分组,并发 送了确认应答分组。按照两次握手的协定,S认为连接已经成功地建立了,可以开始发送数据分组。可是,C在S的应答分组在传输中被丢失的情况下,将不知道S 是否已准备好,不知道S建立什么样的序列号,C甚至怀疑S是否收到自己的连接请求分组。在这种情况下,C认为连接还未建立成功,将忽略S发来的任何数据分 组,只等待连接确认应答分组。而S在发出的分组超时后,重复发送同样的分组。
|