我们之前在 传输层协议TCP与UDP 中详细介绍了UDP协议和TCP协议格式以及他们各自的特点,我们知道TCP协议是面向连接的,那面向连接就得需要做建立连接,维护连接,断开连接这些工作。
服务器状态转换[CLOSED->LISTEN] 服务器调用listen后就进入LISTEN状态,等待客户端向自己发起连接
[LISTEN->SYN_RCVD] 一旦监听到连接请求(同步报文段),就会将该连接放入内核中维护的连接等待队列中,并向客户端发送SYN+ACK报文确认已收到看客户端的连接请求
[SYN_RCVD->ESTABLISHED] 服务器一旦收到了客户端的确认报文,就进入ESTABLISHED状态,就可以进行读写数据了
[ESTABLISHED->CLOSE_WAIT] 当客户端主动关闭连接(调用close),服务器收到客户端发送的结束报文段FIN,服务器向客户端返回确认收到关闭连接的报文后,就会进入CLOSE_WAIT
[CLOSE_WAIT->LAST_ACK] 当服务器进入CLOSE_WAIT状态时,说明服务器已经开始准备关闭连接(但是需要先处理完之前的数据),当服务器真正调用close关闭连接时,会向客户端发送FIN报文,自己则进入LAST_ACK状态,等待最后一个ACK的到来(这里的ACK是指客户端对于服务器发送的FIN的响应报文)
[LAST_ACK->CLOSED] 服务器收到了客户端对FIN报文的响应ACK,至此服务器关闭连接成功
客户端状态转换[CLOSED->SYN_SEND] 客户端调用connect,向服务器发送同步报文段,表示想与服务器建立连接,自己进入SYN_SEND状态等待服务器的响应
[SYN_SEND->ESTABLISHED] connect调用成功,客户端收到服务器的响应报文ACK,进入ESTABLISHED状态,可以读写数据
[ESTABLISHED->FIN_WIAT_1] 客户端主动调用close,向服务器发送结束报文段,自己进入FIN_WAIT_1状态,等待服务器的响应
[FIN_WAIT_1->FIN_WAIT_2]客户端收到服务器对结束报文段的确认,则进入FIN_WAIT_2状态,开始等待服务器的结束报文段
[FIN_WAIT_2 -> TIME_WAIT] 客户端收到服务器发来的结束报文段, 进入TIME_WAIT, 并发出对服务器发送的结束报文的的响应 LAST_ACK;
[TIME_WAIT -> CLOSED] 客户端要等待一个2MSL(Max Segment Life, 报文最大生存时间)的时间, 才会进入CLOSED状态(因为要防止服务器没有收到LASK_ACK,需要进行重发的情况).
TCP状态转换图 TCP中常见的面试题 为什么是三次握手,不是一次或者两次答:3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。
现在把三次握手改成仅需要两次握手,死锁是可能发生的。作为例子,考虑计算机S和C之间的通信,假定C给S发送一个连接请求分组,S收到了这个分组,并发送了确认应答分组。
按照两次握手的协定,S认为连接已经成功地建立了,可以开始发送数据分组。
可是,C在S的应答分组在传输中被丢失的情况下,将不知道S 是否已准备好,不知道S建立什么样的序列号,C甚至怀疑S是否收到自己的连接请求分组。
在这种情况下,C认为连接还未建立成功,将忽略S发来的任何数据分 组,只等待连接确认应答分组。而S在发出的分组超时后,重复发送同样的分组。这样就形成了死锁。
两次握手还可能造成服务器资源的浪费,怎么说呢,再举个栗子,假设今天C向S发送了一个连接请求,并等待S的响应,S给C发送ACK之后,就认为连接已经建立,我们知道连接建立之后是需要维护这个连接的,此时S的操作系统就需要分配资源和空间来维护这个连接,那假设S给C的响应丢失了,C并未收到响应则认为连接没有建立成功,不能正常通信,此时S维护的连接就是一个失败的连接,不能成功通信,那假设今天由100万个客户端向S这个服务器发送连接请求,结果都没有收到响应,此时S就维护了100万个无用的连接,浪费服务器的资源
为什么是三次握手,四次挥手答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。
但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,“你发的FIN报文我收到了”。