TCP四次挥手和TIME_WAIT

TCP四次挥手和TIME_WAITFIN_WAIT_1:FIN_WAIT_1和FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报文。而这两种状态的区别是:FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET即进入到FIN_WAIT_1状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2状态,当然在实际的正常情…

大家好,又见面了,我是你们的朋友全栈君。

TCP四次挥手和TIME_WAIT

 

TCP四次挥手和TIME_WAIT

FIN_WAIT_1 : FIN_WAIT_1和FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报文。而这两种状态的区别是: FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET即进入到FIN_WAIT_1状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2状态,当然在实际的正常情况下,无论对方何种情况下,都应该马上回应ACK报文,所以FIN_WAIT_1状态一般是比较难见到的,而FIN_WAIT_2状态还有时常常可以用netstat看到。(主动方)

FIN_WAIT_2 :实际上FIN_WAIT_2状态下的SOCKET,表示半连接 ,也即有一方要求close连接,但另外还告诉对方,我暂时还有点数据需要传送给你,稍后再关闭连接。

TIME_WAIT: 表示收到了对方的FIN报文,并发送出了ACK报文 ,就等2MSL后即可回到CLOSED可用状态了。如果FIN_WAIT_1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。(主动方)

CLOSING(比较少见) : 这种状态比较特殊,实际情况中应该是很少见。正常情况下,当你发送FIN报文后,按理来说是应该先收到(或同时收到)对方的 ACK报文,再收到对方的FIN报文。但是CLOSING状态表示你发送FIN报文后,并没有收到对方的ACK报文,反而却也收到了对方的FIN报文。如果双方几乎在同时close一个SOCKET的话,那么就出现了双方同时发送FIN报文的情况,也即会出现CLOSING状态,表示双方都正在关闭SOCKET连接。

CLOSE_WAIT : 表示在等待关闭。当对方close一个SOCKET后发送FIN报文给自己,你系统毫无疑问地会回应一个ACK报文给对方,此时则进入到CLOSE_WAIT状态。接下来呢,实际上你真正需要考虑的事情是察看你是否还有数据发送给对方,如果没有的话,那么你也就可以 close这个SOCKET,发送FIN报文给对方,也即关闭连接。所以你在CLOSE_WAIT状态下,需要完成的事情是等待你去关闭连接。

LAST_ACK: 这个状态还是比较容易好理解的,它是被动关闭一方在发送FIN报文后,最后等待对方的ACK报文。当收到ACK报文后,也即可以进入到CLOSED可用状态了。

CLOSED: 表示连接中断。

 

同时关闭

TCP四次挥手和TIME_WAIT

 

 

 

 

为什么需要四次挥手?

那可能有人会有疑问,在tcp连接握手时为何ACK是和SYN一起发送,这里ACK却没有和FIN一起发送呢。原因是因为tcp是全双工模式,接收到FIN时意味将没有数据再发来,但是还是可以继续发送数据(解决全双工问题)。

 

 

 

TIME_WAIT

TIME_WAIT状态会持续2MSL的时间才会转换到CLOSE状态,一般是1-4分钟(现在对于linux是30秒)。MSL(最大分段生存期):指明TCP报文在Internet上最长生存时间。

 

只有主动关闭的一方才会进入TIME_WAIT状态。那么端口不够用就是文件描述符不够用了,因为文件描述符只有在从TIME_WAIT状态转换到CLOSE状态后才会真正被系统收回。

 

 

问题

 

TCP四次挥手和TIME_WAIT

如果执行主动关闭的一方HOST1不进入到TIME_WAIT状态就关闭连接那会发生什么呢?

当重传的FIN消息到达时,因为TCP已经不再有连接的信息了,所以就用RST(重新启动)消息应答,导致HOST2进入错误的状态而不是有序终止状态(如果主动关闭的一方又开启了一个新的链接,则重发的FIN会将新连接给关闭掉),如果发送最后ACK消息的一方HOST1处于TIME_WAIT状态并仍然记录着连接的信息,它就可以正确的响应HOST2的FIN消息了。(最后一个ACK可能丢失并导致HOST2重新发送FIN消息,导致老连接的包会干扰新连接

 

 

TIME_WAIT占用的资源

1、TW会占用内存,但是占用内存很小(1W的TW连接占用1M左右)

2、对CPU也是有影响的,比如TW的端口太多,导致选择可用端口时,需要很多次选择才能成功;但这个影响也是很小的。

3、TW连接会占用大量端口。

4、被占用的是一个五元组:(协议,本地IP,本地端口,远程IP,远程端口)。对于Web 服务器,协议是TCP,本地IP通常也只有一个,本地端口默认的80或者443。只剩下远程 IP和远程端口可以变了。如果远程IP相同的话,就只有远程端口可以变了。这个只有几万个(net.ipv4.ip_local_port_range),所以当同一客户端向服务器建立了大量连接之后,会耗尽可用的五元组导致问题。

 

https://www.zhihu.com/question/29354418?sort=created

http://www.cnblogs.com/lulu/p/4149312.html

 

 

 

TIME_WAIT优化

tcp_timestamps

只有该选项开启了,tcp_tw_reuse和tcp_tw_recycle才能起作用。另外tcp_timestamps可以解决PAWS和计算RTT的问题。

 

tcp_tw_reuse

需要开启tcp_timestamps时才有效。针对于一个连接,如果开启了该开关,即便该连接处于TW状态,收到SYN包之后,也能建立一条新的连接。该连接跟之前的TW连接5元组相同。但是需要满足下面条件的其中一个:

1)初始化序列号比老的TW序列号大。

2)新来的连接的时间戳比老的TW的时间戳大。

 

tcp_tw_recycle

快速回收TW,应该是RTO时间内回收。需要开启tcp_timestamps时才有效。

即使快速回收之后,也保留了一些信息,保留的信息有:对端IP、最后的对端过来的时间戳等。对于新来的连接,同时满足下面3个条件时,连接会被拒绝;否则连接不会被拒绝:

1)来自该IP的TCP连接请求带有时间戳信息;

2)在MSL时间内,收到过该IP过来的数据;

3)新连接的时间戳小于保存的TW的时间戳;

 

tcp_max_tw_buckets

这个是控制并发的TIME_WAIT的数量,默认值是50几W,如果超限,那么,系统会把多的给destory掉,然后在日志里打一个警告。

 

SO_LINGER

struct linger {

    int l_onoff; /* 0 = off, nozero = on */

    int l_linger; /* linger time */

};

1)设置 l_onoff为0,则该选项关闭,l_linger的值被忽略,等于内核缺省情况,close调用会立即返回给调用者,如果可能将会传输任何未发送的数据。

2)设置 l_onoff为非0,l_linger为0,则套接口关闭时TCP夭折连接,TCP将丢弃保留在套接口发送缓冲区中的任何数据并发送一个RST给对方,而不是通常的四分组终止序列,这避免了TIME_WAIT状态;(说明如果没有数据被丢弃,也是正常的四次挥手;不是rst)

3)设置 l_onoff 为非0,l_linger为非0,当套接口关闭时内核将拖延一段时间(由l_linger决定)。

 

 

 

TCP关闭连接的方式

 

正常关闭

调用close()关闭socket、没close但进程正常结束(当然这是不应该的做法)、进程core掉、在shell 命令行中kill掉进程,都可抽象成“正常”关闭。因为即使core掉,内核也会马上帮应用程序回收(close)socket文件描述符。

 

不正常关闭

客户端崩溃了,此时肯定发不出FIN包了(当然啦,内核都没机会帮应用程序回收资源了)。这种情况,服务器端有如下两种情况。

1、服务器send数据,因为客户端已经崩溃,服务器收不到ACK自然会不停的重传。Berkeley的重传机制,重传8次,相对第一次传的15分钟后仍没收到ACK,则返回ETIMEDOUT或EHOSTUNREAC错误。如果服务器不理会这个错误,再次调用send,则立马返回Broken Pipe错误。

2、服务器不发任何数据了,那只有靠应用层心跳检测机制或Keepalive,来发觉TCP断连了。

 

参考资料

https://zhuanlan.zhihu.com/p/29334504

https://zhuanlan.zhihu.com/p/32386693

TCP四次挥手和TIME_WAIT

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/142632.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 淘宝免费ip地址查询导致服务堵死的坑

    淘宝免费ip地址查询导致服务堵死的坑

    2021年11月5日
    37
  • 自抗扰控制(ADRC)仿真系统(matlab/simulink)的搭建

    自抗扰控制(ADRC)仿真系统(matlab/simulink)的搭建一、现在关于自抗扰控制技术方面的研究已经比较成熟了,基本上了解结构以后都可以找到历程实现,今天简单总结一采用自抗扰控制的仿真系统搭建,不必畏惧,熟悉皆可达。1.首先自抗扰控制分为TD(跟踪微分器),非线性组合,ESO扩张状态观测器。而每部分的公式推导均可在《自抗扰控制技术-估计补偿不确定因素的控制技术》这本书中找到,很推荐,如果有些不了解的也可以搜索韩老师的论文看看。2.按照一般论文流程,先介绍整体结构,框图如下:其实结构也挺少,使用习惯以后,其实很好理解。3.接下来按照每个部分简单做一个介绍。

    2022年5月12日
    49
  • Eric6的基础使用方法「建议收藏」

    Eric6是Python编程语言的IDE程序,功能之强大,绝不输于Python平台下的任何IDE程序,占用内存低运行速度快足以令Eric6藐视群雄,最可贵的是与PyQt5结合的更是天衣无缝,简直就是开发GUI程序的绝配。PyQt5是赖以Python编程语言的外部GUI开发语言,其夯实的底层基础与强大的可视化界面设计让PyQt5成为Python语言GUI开发的佼佼者,更新速度之快,开发GUI程序的速度

    2022年4月17日
    81
  • HBase常见面试题[通俗易懂]

    HBase常见面试题[通俗易懂]1.HBase简单读写流程?读:找到要读数据的region所在的RegionServer,然后按照以下顺序进行读取:先去BlockCache读取,若BlockCache没有,则到Memstore读取,若Memstore中没有,则到HFile中去读。写:找到要写数据的region所在的RegionServer,然后先将数据写到WAL(Write-AheadLogging,预写日志系统)中,然后再将数据写到Memstore等待刷新,回复客户端写入完成。2.简述HBase的瓶颈HBase的瓶

    2022年5月31日
    30
  • java中的io模型_技术人才迁移到web3

    java中的io模型_技术人才迁移到web3原文地址:服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种:(1)同步阻塞IO(Blocking IO):即传统的IO模型。(2)同步非阻塞IO(Non-blocking IO):默认创建的socket都是阻塞的,非阻塞IO要求socket被设置为NONBLOCK。注意这里所说的NIO并非Java的NIO(New IO)库。(3)IO多路复

    2025年5月27日
    1
  • diff命令详解_linux对比两个文件内容指令

    diff命令详解_linux对比两个文件内容指令diff命令详解

    2022年4月20日
    47

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号