程序员面试之必考题(二):TCP连接的建立和拆除
( 1 )要使通信的双方能够确知对方的存在。
( 2 )要允许双方协商通信过程中的相关参数。
( 3 )能够对传输实体的资源进行分配。
TCP 连接的建立采用客户 / 服务器方式。主动发起连接建立的应用进程为客户,被动等待连接建立的应用进程为服务器。 TCP 连接的建立过程可以形象的描述为“三次握手”,其过程如下(如图 1 所示):
第一次握手:客户端作为连接建立发起端,选择客户端初始序列号 x ,向服务器发送( SYN=1 , seq=x )的 SYN 段。客户状态由 LISTEN 进入 SYN SEND 状态,等待服务器确认。
第二次握手:服务器收到客户发送的 SYN 段后,选择服务器初始序列号 y ,向客户发送( SYN=1 , ACK=1 , seq=y , ack_seq=x+1 )的 SYNACK 段。同时,服务器状态也由 LISTEN 进入 SYN RCVD 状态。
第三次握手:客户端收到服务器的 SYNACK 段后,向服务器发送( ACK=1 , seq=x , ack_seq=y+1 )的 ACK 段,同时,客户端状态进入 ESTABLISHED 状态,客户端确认连接已建立;当服务器收到 ACK 段后,其状态也进入 ESTABLISHED 状态,也确认连接已建立。至此,双方均确认连接建立成功。
在 TCP 连接建立过程中的第一次握手与第二次握手发送的 SYN 段和 SYNACK 段,均不封装应用层数据,即数据字段为空,但这两个端均需消耗一个序列号。也就是说,客户端的初始序列号 x 被 SYN 用掉,服务器端的初始序列号 y 被 SYNACK 段用掉。客户向服务器发送的应用层数据的第一个字节序号是 x+1 ,服务器发送给客户的应用层数据的第一个字节序号是 y+1 。另外,第三次握手的 ACK 段是可以封装应用层数据的,但是单纯的 ACK 段(即未封装应用层数据)是不消耗序列号的。
TCP 的连接建立完成后,就可以进行数据传送。数据传送结束后,还需要进行连接拆除, TCP 的连接拆除需要经过四次握手过程
(1) 当客户向服务器发送完最后一个数据段后,可以发送一个FIN段(FIN=1,seq=u),请求断开客户到服务器的(半)连接。FIN段不封装应用层数据,但是要消耗掉一个序列号(类似于SYN段),其状态由ESTABLISHED进入FIN_WAIT_1,在这一状态下,只能接收服务器发送过来的数据,而不再发送数据。
(2) 服务器收到客户的FIN段后,向客户发送一个ACK段(ACK=1,seq=v,ack_seq=u+1),ACK段可以封装应用层数据(如果有)。服务器状态ESTABLISHED进入CLOSE_WAIT,在这一状态下,服务器仍然可以发送数据,而不再接收数据。当客户收到ACK段后,其状态由FIN_WAIT_1进入FIN_WAIT_2,仍然可以接收来自服务器的数据。此时的TCP连接已经关闭了客户向服务器方向的数据传输,故也称为半关闭。
(3) 当服务器向客户发送完最后一个数据段后,服务器向客户 FIN 段( FIN=1 ,ACK=1,seq=w,ack_seq=u+1),同样,该FIN段也不携带应用层数据。服务器状态则由CLOSE_WAIT进入LAST_ACK,此时服务器也不再发送数据。
(4) 当客户收到服务器发送的FIN段后,向服务器发送ACK段(ACK=1,seq=u+1,ack_seq=w+1),其状态由FIN_WAIT_2进入TIME_WAIT,等待2MSL(Maximum Segment Lifetime)时间,然后进入CLOSED状态,最终释放连接;服务器在收到最后一次ACK段后,状态由LAST_ACK进入CLOSED,最终释放连接。
MSL 是最大段生存时间,是任何 TCP 段被丢弃前在网络内 “ 存活 ” 的最长时 间。TCP协议规范(RFC793)规定的MSL是2分钟,但实际系统在实现TCP时设定的时间有所不同,比如BSD/386为30秒、Soloris 2.2为1分钟。客户在TIME_WAIT状态之所以要等待2MSL时间才真正释放连接,一方面是为了以防最后一次ACK段丢失,会导致服务器重发FIN,这样在客户真正释放连接前还可以再次发送ACK段,尽可能确保服务器尽快释放连接,另一方面是为了保证在2MSL期间,该连接的本地端点地址(IP地址和端口号)不被再次使用,避免可能的较早连接请求到达,被误解为新连接请求。