[date: 2022-03-13 11:58] [visits: 21]

TCP连接建立与断开

TCP概述与报文格式中介绍了OSI/RM网络模型与TCP报文格式,这篇文章紧接前篇介绍TCP连接的建立与断开过程以及其中可能的异常场景。

TCP连接状态机

TCP连接状态的转移过程一般使用如下图片进行表述:

图中带箭头的粗实线表示客户端状态转移,带箭头的粗虚线表示服务端状态转移,带箭头的细线表示不常见事件,如复位、同时打开、同时关闭等。各状态含义如下:

TCP连接建立

TCP是面向连接的,每次数据通信前需要进行连接建立,一般称为三次握手:

笔者曾认为学习TCP三次握手知道上述程度就可以了,但现在改变了观点:仅知道上述过程不足以在实际工作中解决具体问题或是困难问题,作为技术从业者不能仅局限于上述程度的了解,还要更深入掌握具体细节。

建立连接过程

TCP连接建立过程是TCP连接状态机状态变化的一部分,正常如下:

可能出现的异常

在连接建立过程的每一步,都有可能发生异常,知道这些异常的处理才是真的理解三次握手,就像平时代码量主要花在处理异常流程而不是正常流程。

Client SYN包丢包

Server SYN+ACK包丢包

Client ACK包丢包

Client与Server同时主动发起连接

Client发送SYN包但故意不回复SYN+ACK包

Server将处于一种半连接的状态,此时Server会认为自己的SYN+ACK包出现丢包并触发重传,但如果大量的连接需要重传SYN+ACK包则会消耗Server资源,因此存在被攻击的可能性(SYN FLOOD)

Client向Server发送SYN包后Server拒绝连接

Server未收到SYN+ACK包所对应的Client ACK包,但收到Client数据包

此时连接可正常进入EASTABLISHED

TCP连接断开

与三次握手对应,TCP连接断开被称为四次挥手:

建立连接只需要三次交互但断开连接需要四次,这是因为TCP连接内数据传输是全双工的,断开连接时每个方向必须单独关闭,这称为TCP的半关闭特性。相互打完招呼开始沟通后,你不说话了,我还可以说即使如此。

连接断开的过程

TCP连接断开过程是TCP连接状态机中的一个子过程,假设Client作为主动方进行连接断开:

可能出现的异常

主动方FIN丢包

被动方ACK包丢包

被动方FIN+ACK包丢包

主动方ACK包丢包

双方同时断开连接

主动方FIN包后未收到ACK包,但收到了FIN+ACK包

状态从FIN_WAIT_1=>TIME_WAIT,主动方回复ACK包,跳过FIN_WAIT_2状态

上述可能异常只是普通异常,更多异常可能发生在所有超时重传的位置,每个超时重传位置都有可能无法符合预期行为。另外这些有超时重试的位置都有可能被攻击,攻击方不按约定行为交互而故意使对方做无效超时重试消耗资源。

TIME_WAIT与2MSL

MSL是Maximum Segment Lifetime英文的缩写,中文可以译为“报文最大生存时间”,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。

在连接断开的最后一个阶段,主动方从TIME_WAIT=>CLOSED需要等待2MSL时间,这个2MSL是一个超时重传窗口时间用来保证被动方连接正确进入CLOSED状态,因为断开连接主动方最后一个ACK包无法知晓是否达到对方,因而需要留下一个时间窗口让对方重发FIN+ACK包。

2MSL除了留给对方超时重试FIN+ACK以保证连接正常关闭,同时也可以避免网络中此连接遗留的在途数据包导致连接状态异常,如果双方商议连接断开不等待2MSL就完成,部分因为网络延迟导致的重传包(可能是FIN+ACK或其他)可能继续达到,这样将干扰新连接的可靠传输。

建立连接的目的

一般而言建立连接的目的是为了可靠传输,但为了可靠传输是否一定要先建立连接呢,答案是未必,可以这么论证:“在建立连接之前不存在连接,而建立连接过程中的通信同样需要可靠性,因此连接并不是可靠传输的必要条件”。

大家知道UDP协议是面向无连接的,假设我们使用UDP协议做内容传输,并在其之上设计一种类似TCP握手阶段数据传输的方式进行数据传输,那么这种数据传输过程同样可靠。TCP建立连接的目的并非仅是为了可靠传输中的“可靠”,而是为达到可靠传输目的采用建立连接这种方式最高效,这个高效即是理论成果也与实践相符。

Ref