一个连接从开始建立到断开,经历了一连串的状态变化,这次主要分析下它的状态变迁图,首先上经典的状态变迁图
CLOSED:这个状态不是一个真正的状态,是图中假想的一个起点或者是终点
LISTEN: 服务器等待连接过来的状态
SYN_SENT: 客户端发起连接(主动打开),变成此状态,如果SYN超时,或者服务器不存在直接CLOSED
SYN_RCVD:服务器收到SYN包的时候,就变成此状态,
ESTABLISHED:完成三次握手,进入连接建立状态,说明此时可以进行数据传输了
FIN_WAIT_1:客户端执行主动关闭,发送完FIN包之后便进入FIN_WAIT_1状态
FIN_WAIT_2:客户端发送FIN包之后,收到ACK,即进入此状态,其实就是半关闭的状态
TIME_WAIT:这个状态从图上看,有3中情况,从FIN_WAIT_2进入,客户端收到服务器发送过来的FIN包之后进入TIME_WAIT状态,有CLOSING状态进入,这是同时关闭的状态,同时发起FIN请求,同时接收并做了ACK的回复,从FIN_WAIT_1进入,收到对端的FIN,ACK,并回复ACK,这个地方感觉是,FIN和ACK是一块来的.
CLOSE_WAIT:接收到FIN之后,被动的一方进入此状态,并回复ACK
LAST_ACK:被动的一端发送FIN包之后 处于LAST_ACK状态
CLOSING:两边同时发出FIN请求
第一次:客户端向服务器发送一个SYN(同步)报文段,随机生成seq=x。这个报文段的作用是告诉服务器,客户端希望建立一个TCP连接,并请求服务器进行同步,此时客户端进入SYN_SENT(同步已发送)状态。
第二次:服务器收到客户端的SYN报文段后,会向客户端发送一个SYN+ACK(同步确认)报文段,ACK = x+1,表示已经成功收到客户端的SYN请求,同时,服务器也会生成一个自己的初始化序列号seq = y。发送后,进入SYN_RCVD(同步收到)状态。
第三次:客户端收到服务器的SYN+ACK报文段,会向服务器发送一个ACK报文段,该报文段的确认号为服务器的序列号加1,即ack = y +1,seq = x + 1,客户端发送这个报文段后进入ESTABLISHED(已建立连接)状态,服务器收到客户端的ACK报文段后,也进入ESTABLISHED(已建立连接)状态,TCP连接建立成功,双方可以开始数据传输。
第一次:主动关闭方(通常是客户端)发送一个FIN(结束)报文段,用来表示主动关闭方已经没有数据需要发送,希望关闭连接。此时,主动方进入FIN_WAIT_1(终止等待1)状态
第二次:被动关闭方(通常是服务器)收到FIN报文段后,会发送一个ACK(确认)报文段。这个ACK报文段的作用是告诉主动关闭方,已经接收到关闭连接的请求,被动关闭方发送完ACK报文后,进入CLOSE_WAIT(关闭等待)状态。而主动关闭方收到ACK报文后,进入FIN_WAIT_2状态
第三次:
当被动关闭方也没有数据要发送,它也会向主动关闭方发送一个FIN报文段,请求关闭,进入LAST_ACK(最后确认)状态
第四次:
主动关闭方收到FIN报文段,会发生一个ACK报文段进行确认,然后主动关闭方进入TIME_WAIT(时间等待)状态,在经过一段时间后(通常是2倍的MSL,即最长报文段寿命)后,主动方会彻底关闭连接,被动方收到ACK报文后,就会关闭连接。
握手的原因:
确认双方发送和接受的能力:第一次握手,客户端发送 SYN 报文,服务器能知道客户端的发送能力正常;第二次握手,服务器发送 SYN+ACK 报文,客户端能知道服务器的接收和发送能力正常;第三次握手,客户端发送 ACK 报文,服务器能知道客户端的接收能力正常。这样通过三次握手,双方都确认了彼此的发送和接收能力。
防止重复连接初始化:如果没有第三次握手,当网络中存在延迟的旧 SYN 报文段到达服务器时,服务器会误认为是客户端的新连接请求而建立连接,造成资源浪费。有了第三次握手,客户端可以识别出旧的 SYN 报文段对应的连接是无效的,不会进行确认,从而避免了这种情况。
挥手的原因:
确保数据完整传输:TCP 连接是全双工的,即双方都可以同时发送和接收数据。当一方(如客户端)发送 FIN 报文请求关闭连接时,只是表示它不再发送数据,但还可能继续接收数据。服务器收到 FIN 报文后,需要先发送 ACK 确认,确保客户端知道它已收到关闭请求,然后服务器需要时间来处理未完成的数据发送,处理完后再发送 FIN 报文给客户端请求关闭连接,客户端最后发送 ACK 进行确认。
保证连接可靠关闭:四次挥手能让双方都有机会处理完各自的数据发送和接收,然后再正式关闭连接,避免数据丢失或传输不完整的情况发生。例如,如果没有第四次握手,服务器发送 FIN 后直接关闭连接,客户端可能还没来得及处理服务器发送的最后一些数据,就会导致数据丢失
持续时间:通常是2倍的MSL(Maximum Segment Lifetime,最长报文段寿命)。
原因:
确保最后一个ACK能够被对方收到:客户端发送的最后一个 ACK 报文段有可能在网络中丢失,如果服务器没有收到这个 ACK,就会重发 FIN 报文段。客户端处于 TIME - WAIT 状态可以保证在这段时间内能够接收并重传丢失的 ACK,避免服务器因为没有收到 ACK 而一直处于等待状态,从而确保连接能够可靠地关闭。
避免新旧连接混淆:在关闭连接后,可能会有延迟的旧报文段在网络中继续传输。如果新的连接使用了与旧连接相同的端口号等信息,这些延迟的旧报文段可能会被误认为是新连接的一部分,导致数据混乱。TIME - WAIT 状态的存在使得旧连接的所有报文段都有足够的时间从网络中消失,避免了新旧连接的混淆,保证了新连接的正常通信。
超时重传:发送方在发送数据后,会启动一个定时器。如果在定时器超时之前没有收到接收方对于该数据的确认(ACK),发送方就会认为数据传输出现了问题,将重新发送该数据。
超时时间设置:超时时间一般需要根据网络状况动态调整。典型的做法是通过测量往返时间(RTT)来估计合适的超时时间。通常将超时时间设置为略大于平均 RTT,以适应网络中的延迟波动。如果超时时间设置得过短,会导致不必要的重传,增加网络负载;设置得过长,则会在数据丢失时导致较长的等待时间,影响传输效率。
快速重传:快速重传是基于接收方的反馈来触发重传,而不是依赖于超时定时器。当接收方发现接收到的数据出现了乱序,即收到了序号大于期望序号的数据段时,它会立即向发送方发送重复的 ACK,指示期望接收的下一个数据段的序号。如果发送方收到了一定数量(通常是 3 个)的重复 ACK,就会认为该数据段丢失了,不等超时定时器超时,就会立即重传丢失的数据段。
优点:快速重传快速重传能够更快地检测到数据丢失并进行重传,相比超时重传,可以显著减少数据传输的延迟,提高网络的传输效率,尤其在网络拥塞不严重的情况下,能够快速恢复丢失的数据,避免因等待超时导致的性能下降。
TCP 首部长度通常为 20 字节,但在有选项字段时会变长。TCP 首部包含以下字段:
限制连接请求队列长度:当服务器调用listen
函数后,处于监听状态的服务器会维护一个未完成连接队列和一个已完成连接队列。backlog
参数决定了这两个队列的总和最大长度。例如,若backlog
设置为 5,那么未完成连接队列和已完成连接队列中最多只能有 5 个连接请求。当队列已满时,新的连接请求会被拒绝,客户端会收到连接被拒绝的错误信息。
影响系统资源占用和性能:
backlog
值可以允许更多的连接请求在队列中等待处理,这在高并发场景下可能有助于减少连接被拒绝的情况,但也会占用更多的系统资源,如内存等,因为每个等待连接都需要一定的资源来维护其状态信息。backlog
值则限制了同时处理的连接数量,可能导致一些连接请求被拒绝,但可以减少系统资源的占用,适用于连接处理速度较快且并发量不是特别高的场景。与客户端连接行为的关系:客户端发起连接请求后,会根据服务器的响应来决定后续行为。如果服务器的连接队列已满,客户端可能会收到连接超时或连接被拒绝的错误,客户端需要根据具体情况进行重试或其他处理。
backlog
参数需要根据服务器的处理能力、系统资源以及预计的并发连接数等因素进行合理设置,以平衡系统性能和资源利用。
服务器在完成第三次握手,收到客户端的 ACK 报文段后,accept
函数才会返回,将已完成连接的套接字返回给应用程序,应用程序可以开始通过这个套接字进行数据传输。也就是说,accept
函数是在三次握手成功完成,连接建立之后被调用并发挥作用的。
SYN 泛洪(SYN Flood)是一种常见的 DDoS(分布式拒绝服务)攻击手段。
攻击原理:
解决方法:
优化服务器TCP参数:
backlog
参数值:可以增加服务器未完成连接队列的长度,使服务器能够容纳更多的连接请求,减少因队列满而导致的连接拒绝。但这只是一种缓解措施,不能完全解决问题,且过大的backlog
值会消耗过多系统资源。采用负载均衡技术:
采用SYN Cookie技术: