一、概述


在HTTP应用中,存在一个问题,SERVER由于某种原因关闭连接,如KEEPALIVE的超时,这样,作为主动关闭的SERVER一方就会进入 FIN_WAIT2状态,但TCP/IP协议栈有个问题,FIN_WAIT2状态是没有超时的(不象TIME_WAIT状态),所以如果CLIENT不关闭,这个FIN_WAIT_2状态将保持到系统重新启动,越来越多的FIN_WAIT_2状态会致使内核crash。 对于一般Linux来说,系统PROC文件tcp_fin_timeout的值默认为60秒,内核中对应的变量为init_net.ipv4.sysctl_tcp_fin_timeout,默认值为TCP_FIN_TIMEOUT,即(60 * HZ)。此值表示一个不再被应用层使用(执行了close调用)的TCP连接处于FIN_WAIT_2状态的时长,如果在此时间内未能接收到对端的FIN结束报文,内核将复位此连接。但是除此之外,如果应用层是执行shutdown(SHUT_WR)操作关闭了套接口的发送,TCP连接还可进行接收操作,此种情况下的TCP连接处于FIN_WAIT_2状态不受tcp_fin_timeout的时间限制,将会永久的等待对端去关闭连接或者本地使用close关闭或者重启OS。

出现fin_wait_2一般为客户端,如果为服务端出现,则表明是服务端主动发起的断开。

二、可能产生原因及处理:

1。常连接并且当连接一直处于IDLE状态导致SERVER CLOSE时,CLIENT编程缺陷,没有向SERVER 发出FIN和ACK包 ;

2、.客户端状态迁移(主动结束连接)CLOSED->SYN_SENT->ESTABLISHED->FIN_WAIT_1->FIN_WAIT_2->TIME_WAIT->CLOSED,会出现;
3、服务器状态迁移CLOSED->LISTEN->SYN收到->ESTABLISHED->CLOSE_WAIT->LAST_ACK->CLOSED

【处理】:

1、windows OS中:
开始->运行->输入regedit,打开如下注册表
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters
在详细信息窗格中双击 TCPFinWait2Delay,没有的话新建: REG _ DWORD值,输入一个十进制超时值(s);控制 TCP 链接以前等待的秒数它被强制关闭, 关闭 (s,SD_SEND) 函数调用以后。 在默认值为 240 秒。 此值范围是 30 到 300。 必须手动建立此注册表值。 否则就使用默认值。

2、Linux中:
netstat –ant|grep FIN_WAIT2|wc –l //查看该状态连接数
sysctl -a|grep net.ipv4.tcp_fin_timeout //输出如下
sysctl: reading key “net.ipv6.conf.all.stable_secret”
net.ipv4.tcp_fin_timeout = 60 //可看到这里默认为60s,可调小该值,加快系统关闭处于FIN_WAIT2状态的TCP连接
sysctl: reading key “net.ipv6.conf.default.stable_secret”
sysctl: reading key “net.ipv6.conf.eth0.stable_secret”
sysctl: reading key “net.ipv6.conf.eth1.stable_secret”
sysctl: reading key “net.ipv6.conf.lo.stable_secret”

或执行:cat /proc/sys/net/ipv4/tcp_fin_timeout //输出结果同上,tcp_fin_timeout默认为60秒
60

vim /etc/sysctl.conf //添加或修改
#表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭
net.ipv4.tcp_syncookies = 1
#表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间
net.ipv4.tcp_fin_timeout = 30
#表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数
net.ipv4.tcp_max_syn_backlog = 8192
#表示系统同时保持TIME_WAIT套接字的最大数量,如果超过这个数字,TIME_WAIT套接字将立刻被清除并打印警告信息。默认为180000,改为5000
net.ipv4.tcp_max_tw_buckets = 5000
#表示TCP状态中TIME_WAIT的过期时间,默认情况是60秒
net.ipv4.tcp_tw_timeout = 10
#表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭
net.ipv4.tcp_tw_reuse = 1
#表示开启TCP连接中TIME-WAIT sockets的快速回收,NAT环境可能导致DROP掉SYN包。默认为0,表示关闭
net.ipv4.tcp_tw_recycle = 1
#表示TCP连接不再检查请求数据包的时间戳,默认为1,表示开启
net.ipv4.tcp_timestamps = 0
#表示用于向外连接的端口范围。缺省情况下过窄:32768到61000,改为1024到65535
net.ipv4.ip_local_port_range = 1025 65500
#表示防火墙最大跟踪的TCP连接数。请注意,此限制值要尽量小,以节省对内核内存的占用
net.ipv4.nf_conntrack_max = 102400
#表示socket最大的读缓存,单位是byte
net.core.rmem_max = 16777216
#表示socket最大的写缓存,单位是byte
net.core.wmem_max = 16777216
#表示为每个TCP连接分配的读缓冲区内存大小,单位是byte
#第一个数字表示,为TCP连接分配的最小内存,
#第二个数字表示,为TCP连接分配的缺省内存,
#第三个数字表示,为TCP连接分配的最大内存(net.core.rmem_max可覆盖该值)
net.ipv4.tcp_rmem = 4096 87380 16777216
#表示为每个TCP连接分配的写缓冲区内存大小,单位是byte
#第一个数字表示,为TCP连接分配的最小内存,
#第二个数字表示,为TCP连接分配的缺省内存,
#第三个数字表示,为TCP连接分配的最大内存(net.core.wmem_max可覆盖该值)
net.ipv4.tcp_wmem = 4096 65536 16777216
#表示启用支持更大的TCP窗口。如果TCP窗口最大超过65535(64KB),必须设置该数值为1
net.ipv4.tcp_window_scaling = 1
#表示启用转发应答,可以进行有选择应答(SACK)从而减少拥塞情况的发生
net.ipv4.tcp_sack = 1
#表示当每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许发送到队列的数据包的最大数目,默认是1000。
#对重负载服务器而言,该值太低,可调整到262144
net.core.netdev_max_backlog = 262144
#表示一个tcp连接关闭后,把这个连接曾经有的参数,比如慢启动门限snd_sthresh、拥塞窗口snd_cwnd、srtt等信息保存到dst_entry中,
#只要dst_entry没有失效,下次新建立相同连接的时候就可以使用保存的参数来初始化这个连接。通常情况下是关闭的
net.ipv4.tcp_no_metrics_save = 1
#表示系统同时发起的TCP连接数,一般默认值为128
net.core.somaxconn = 262144
#用于设定系统中最多允许存在多少tcp套接字不被关联到任何一个用户文件句柄上。如果超过这个数字,没有与用户文件句柄关联的tcp套接字符将立即被复位,同时给出警告信息。
#这个限制只是为了防止简单的DoS工具。一般在系统内存比较充足的情况下,可以增大这个参数的赋值。
net.ipv4.tcp_max_orphans = 262144
#用于设置内核放弃TCP连接之前向客户端发送SYN+ACK包的数量,为2表示最多发送2次
net.ipv4.tcp_synack_retries = 2
#用于设置内核放弃建立连接之前发送SYN包的数量,为2表示最多发送2次
net.ipv4.tcp_syn_retries = 2
#表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为20分钟。
net.ipv4.tcp_keepalive_time = 1200

简单调整:
sysctl -w net.ipv4.tcp_keepalive_time=30
sysctl -w net.ipv4.tcp_keepalive_probes=2
sysctl -w net.ipv4.tcp_keepalive_intvl=2
sysctl -w net.ipv4.tcp_fin_timeout = 30
完成后:sysctl -p

3、Apache、Nginx等web
1)Apache禁止KeepAlive :编辑你的httpd.conf并把"KeepAlive On"改成"KeepAlive Off";
2)Nginx减少TIME_WAIT套接字数量;
3)Squid,配置 tcp_max_tw_buckets 限制TIME_WAIT套接字的最大数量;

【内核配置参考】:

1)4.1内核:
tcp_fin_timeout <= 3, FIN_WAIT_2 状态超时时间为 tcp_fin_timeout 值。
3<tcp_fin_timeout <=60, FIN_WAIT_2状态超时时间为 tcp_fin_timeout值+定时器精度(以7秒为单位)误差时间。
tcp_fin_timeout > 60, FIN_WAIT_2状态会先经历keepalive状态,持续时间为tmo=tcp_fin_timeout-60值, 再经历timewait状态,持续时间为 (tcp_fin_timeout -60)+定时器精度,这里的定时器精度根据(tcp_fin_timeout -60)的计算值,会最终落在上述两个精度范围(1/8秒为单位或7秒为单位)。

2)4.3+内核:
tcp_fin_timeout <=60, FIN_WAIT_2 状态超时时间为 tcp_fin_timeout 值。
tcp_fin_timeout > 60, FIN_WAIT_2 状态会先经历 keepalive 状态,持续时间为 tmo=tcp_fin_timeout-60 值 , 再经历 timewait 状态,持续时间同样为 tmo= tcp_fin_timeout-60 值。

三、TCP通信建立连接回顾

TCP三次握手/四次挥手发生在传输层,该层主要为两台主机上的应用程序提供端到端的通信;在TCP/IP协议族中,有两个互不相同的传输协议:TCP(传输控制协议)和UDP(用户数据报协议)。TCP为两台主机提供高可靠性的数据通信。他所作的工作包括把应用程序交给它的数据分成合适的小块交给下面的网络层,确认接收到的分组,设置发送最后确认分组的超时时钟等。由于运输层提供了高可靠性的端到端通信,因此应用层可以忽略所有这些细节。而另一方面,UDP则为应用层提供一种非常简单的服务。它只是把称作数据报的分组从一台主机发送到另一台主机,但并不保证该数据报能到达另一端。任何必须的可靠性必须由应用层来提供。应用层主负责处理特定的应用程序细节;


TCP 报文格式(rfc793) :


各个Field说明如下:

源端口(Source Port):长度为16 bits(2个字节)。源端口。
目的端口(Destination Port):长度为16 bits(2个字节)。目的端口。
序列号(Sequence Number):长度为32 bits(4个字节)。指定了当前数据分片中分配给**第一字节数据**的序列号。在TCP传输流中每一个字节为一个序号。如果TCP报文中flags标志位为SYN,该序列号表示**初始化序列号(ISN)**,此时第一个数据应该是从序列号ISN+1开始。
确认序列号(Acknowledgment Number):长度为32bits(4个字节)。表示TCP发送者期望接受下一个数据分片的序列号。该序号在TCP分片中Flags标志位为ACK时生效。序列号分片的方向和流的方向同方向,而确认序列号分片方向和流方向反方向。数据偏移或首部长度(Data Offset/Header Length): 长度为4bits。数据偏移也叫首部长度。因为首部长度实际也说明了数据区在分片中的起始偏移值。它表示TCP头包含了多少个32-bit的words。因为4bits在十进制中能表示的最大值为15,32bits表示4个字节,那么Data Offset的最大可表示15*4=60个字节。所以TCP报头长度最大为60字节。如果options fields为0的话,报文头长度为20个字节。
预留字段(Reserved field):长度为6bits。值全为零。预留给以后使用。
标志位(Flags): 长度为6bits。表示TCP包特定的连接状态。一个标签位占一个bit,从低位到高位值依次为FIN,SYN,RST,PSH,ACK,URG。新定义的TCP头还扩展了ECE,CWR,NS.
窗口(Window):长度16bits(2个字节)。表示滑动窗口的大小,用来告诉发送端接收端的buffer space的大小。接收端buffer大小用来控制发送端的发送数据数率,从而达到流量控制。最大值为65535.
校验和(Checksum):长度16bits(2个字节)。用来检查TCP头在传输中是否被修改。
紧急指针(Urgent pointer):长度为16bits(2个字节)。表示TCP片中第一个紧急数据字节的指针。只有当URG标志置1时紧急指针才有效。
选项和填充(Option和pading):可变长度。表示TCP可选选项以及填充位。当选项不足32bits时,填充字段加入额外的0填充。
数据(Data):长度可变。用来存储上层协议的数据信息。可以为空。比如在连接建立和连接中止时。

1)TCP三次握手
所谓三次握手 (Three-way Handshake) ,是指建立一个 TCP连接时,需要客户端和服务器总共发送 3 个包。三次握手的目的是连接服务器指定端口, 建立 TCP连接 , 并同步连接双方的序列号和确认号并交换 TCP 窗口大小信息 . 在 socket 编程中,客户端执行 connect() 时。将触发三次握手。

第一次握手:
客户端发送一个TCP的SYN标志位置1的包,指明客户打算连接的服务器的端口,以及初始序号X,保存在包头的序列号(Sequence Number)字段里;TCP规定SYN=1时不能携带数据,但要消耗一个seq,所以声明自己的seq=x;注意:发送端首先发送一个带SYN(synchronize)标志的数据包给接收方【第一次的seq序列号(ISN)是随机产生的,这样是为了网络安全,如果不是随机产生初始序列号,黑客将会以很容易的方式获取到你与其他主机之间的初始化序列号,并且伪造序列号进行攻击】

第二次握手:
服务器发回确认包(ACK)应答。即SYN标志位和ACK标志位均为1,同时,将确认序号(Acknowledgement Number)设置为客户的ISN加1以.即X+1。

第三次握手:
客户端再次发送确认包(ACK), SYN标志位为0,ACK标志位为1.并且把服务器发来ACK的序号字段+1,放在确定字段中发送给对方.并且在数据段放写ISN的+1;

上图所示的三次握手整个过程中有五种TCP状态,对应如下:

CLOSED: 初始状态,表示TCP连接是"关闭着的"或"未打开的",客户端和服务端都出现;

LISTEN:出现在服务端,表示服务器端的某个SOCKET处于监听状态,可以接受客户端的连接

SYN_RCVD:出现在服务端,表示服务器接收到了来自客户端请求连接的SYN报文。这个状态是在服务端的,但是它是一个中间状态,很短暂,平常我们用netstat或ss的时候,不太容易看到这种状态,但是遇到SYN flood之类的SYN攻击时,会出现大量的这种状态,即收不到三次握手最后一个客户端发来的ACK,所以一直是这个状态,不会转换到ESTABLISHED

SYN_SENT:出现在客户端,这个状态与SYN_RCVD状态相呼应,它是TCP连接客户端的状态,当客户端SOCKET执行connect()进行连接时,它首先发送SYN报文,然后随机进入到SYN_SENT状态,并等待服务端的SYN和ACK,该状态表示客户端的SYN已发送

ESTABLISHED:表示TCP连接已经成功建立,开始传输数据,客户端和服务端都出现。

在三次握手过程中,服务器(第2次)发送SYN-ACK之后,收到客户端的ACK之前的TCP连接称为半连接(half-open connect),此时服务器处于Syn_RECV状态.当收到ACK后,服务器转入ESTABLISHED状态。RFC793明确规定,除了第一个握手报文SYN除外,其它所有报文必须将ACK = 1。

在半连接这个间隙,当客户端在短时间内伪造大量不存在的IP地址,向服务器不断地发送syn包,服务器回复确认包,并等待客户的确认,由于源地址是不存在的,服务器需要不断的重发直至超时,这些伪造的SYN包将长时间占用未连接队列,导致正常的SYN请求被丢弃,目标系统运行缓慢,严重者引起网络堵塞甚至系统瘫痪。这就是我们说的Syn攻击

Syn攻击是一个典型的DDOS攻击。检测SYN攻击非常的方便,当你在服务器上看到大量的**半连接状态(Syn_RECV状态)**时,特别是源IP地址是随机的,基本上可以断定这是一次SYN攻击。

在Linux中,可以用如下命令检测是否被Syn攻击:netstat -n -p TCP | grep SYN_RECV

TCP的连接的拆除需要发送四个包,即多一项断开连接请求发送,因此称为四次挥手(four-way handshake)。客户端或服务器均可主动发起挥手动作,在socket编程中,任何一方执行close()操作即可产生挥手操作。

三次握手的关键是要确认对方收到了自己的数据包,这个目标就是通过“确认号(Ack)”字段实现的。计算机会记录下自己发送的数据包序号Seq,待收到对方的数据包后,检测“确认号(Ack)”字段,看Ack = Seq + 1是否成立,如果成立说明对方正确收到了自己的数据包。

四次挥手的状态:


如上图所示,各状态概述如下:

FIN_WAIT_1:这个状态在实际工作中很少能看到,当客户端想要主动关闭连接时,它会向服务端发送FIN报文,此时TCP状态就进入到FIN_WAIT_1的状态,而当服务端回复ACK,确认关闭后,则客户端进入到FIN_WAIT_2的状态,也就是只有在没有收到服务端ACK的情况下,FIN_WAIT_1状态才能看到,然后长时间收不到ACK,通常会在默认超时时间60s(由内核参数tcp_fin_timeout控制)后,直接进入CLOSED状态

FIN_WAIT_2:这个状态相比较常见,也是需要注意的一个状态,FIN_WAIT_1在接收到服务端ACK之后就进入到FIN_WAIT_2的状态,然后等待服务端发送FIN,所以在收到对端FIN之前,TCP都会处于FIN_WAIT_2的状态,也就是,在主动断开的一端发现大量的FIN_WAIT_2状态时,需要注意,可能时网络不稳定或程序中忘记调用连接关闭,FIN_WAIT_2也有超时时间,也是由内核参数tcp_fin_timeout控制,当FIN_WAIT_2状态超时后,连接直接销毁

CLOSE_WAIT:表示正在等待关闭,该状态只在被动端出现,即当主动断开的一端调用close()后发送FIN报文给被动端,被动段必然会回应一个ACK(这是由TCP协议层决定的),这个时候,TCP连接状态就进入到CLOSE_WAIT;

LAST_ACK:当被动关闭的一方在发送FIN报文后,等待对方的ACK报文的时候,就处于LAST_ACK的状态,当收到对方的ACK之后,就进入到CLOSED状态了

TIME_WAIT:该状态是最常见的状态,主动方在收到对方FIN后,就由FIN_WAIT_2状态进入到TIME_WAIT状态,但需要注意是:主动关闭连接的,才有 TIME_WAIT 状态。关于TIME_WAIT 需等待 2 倍的 MSL断开,MSL 是 Maximum Segment Lifetime,报文最大生存时间,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。因为 TCP 报文基于是 IP 协议的,而 IP 头中有一个 TTL 字段,是 IP 数据报可以经过的最大路由数,每经过一个处理他的路由器此值就减 1,当此值为 0 则数据报将被丢弃,同时发送 ICMP 报文通知源主机。MSL 与 TTL 的区别: MSL 的单位是时间,而 TTL 是经过路由跳数。所以 MSL 应该要大于等于 TTL 消耗为 0 的时间,以确保报文已被自然消亡。TIME_WAIT 等待 2 倍的 MSL,比较合理的解释是: 网络中可能存在来自发送方的数据包,当这些发送方的数据包被接收方处理后又会向对方发送响应,所以一来一回需要等待 2 倍的时间。2MSL 的时间是从客户端接收到 FIN 后发送 ACK 开始计时的。如果在 TIME-WAIT 时间内,因为客户端的 ACK 没有传输到服务端,客户端又接收到了服务端重发的 FIN 报文,那么 2MSL 时间将重新计时。在 Linux 系统里 2MSL 默认是 60 秒,那么一个 MSL 也就是 30 秒。Linux 系统停留在 TIME_WAIT 的时间为固定的 60 秒。

CLOSING:这个状态是一个比较特殊的状态,也比较少见,正常情况下不会出现,但是当双方同时都作为主动的一方,调用close()关闭连接的时候,两边都进入FIN_WAIT_1的状态,此时期望收到的是ACK包,进入FIN_WAIT_2的状态,但是却先收到了对方的FIN包,这个时候,就会进入到CLOSING的状态,然后给对方一个ACK,接收到ACK后直接进入到CLOSED状态

综述:TCP的11种状态分别对应TCP三次握手过程的5种状态和TCP四次挥手断开过程中的6种状态:

下图为TCP数据包与wireshark抓取数据的对照图:

TCP三次握手对应源IP—目标IP之间3次网络流量包传输,第四次就可以完成应用层协议通信了;如下所示:


客户端向服务器发送连接请求包,标志位SYN(同步序号)置为1,序号为X=0。

上图中标识错误,请忽略,参考如下:

FIN - 结束; 结束会话 ;SYN - 同步; 表示开始会话请求 ;RST - 复位;中断一个连接 ; PSH - 推送; 数据包立即发送 ;ACK - 应答 ;URG - 紧急 ;ECE - 显式拥塞提醒回应 ; CWR - 拥塞窗口减少

TCP FLAG 标记占1.5个byte,12bit(4bit+8bit,前半个byte与Header Length公用)。新版TCP Flags字段:


Flags值说明:

FIN: "finished"简写。表示发送者以及发送完数据。通常用在发送者发送完数据的最后一个包中。发送方完成任务,今后不会有数据发送,希望断开连接。当通信结束希望断开连接,通信双方的主机之间就可以相互交换FIN位置为1的TCP段。每个主机又对对方的FIN包进行确认应答以后就可以断开连接。不过,主机收到FIN设置为1的TCP段以后不必马上回复一个FIN包,而是可以等到缓冲区中的所有数据都已成功发送而被自动删除之后再发FIN包。Flags: 0x002 (SYN)中02表明该数据包发送的是一个同步序号,用来发起一个新的连接。
SYN: "Synchronisation"简写。表示三次握手建立连接的第一步,在建立连接时发送者发送的第一个包中设置flag值为SYN。SYN为1表示希望建立连接,并在其序列号的字段进行序列号初始值的设定。(Synchronize本身有同步的意思。也就是意味着建立连接的双方,序列号和确认应答号要保持同步)
RST: "reset"简写。重置连接标志,用于重置由于主机崩溃或其他原因而出现错误的连接。或者发送包发送到一个不是期望的                  目的主机时,接收端发送reset 重置连接标志的包。​​​​​​​ 指明TCP连接中出现异常必须强制断开连接。例如,一个没有被使用的端口即使发来了连接请求,也无法通信。此时就可以返回一个RST设置为1的包。此外,程序宕掉或切断电源等原因导致主机重启的情况下,由于所有的连接信息将全部被初始化,所以原有的TCP通信也将不能继续进行。这种情况下,如果通信对方发送了一个设置为1的RST包,就会使通信强制被断开。
PSH: "push"简写。表示接收方应该尽快将这个报文交给应用层。表示需要将收到的数据立刻传给上层应用协议。通知接收端处理接收的报文,而不是将报文缓存到buffer中。当PSH为0,即普通情况下,不需要立即传,而是先进行缓存。当发送端将PSH置为1时,TCP会立即创建一个报文并发送。接受端收到PSH为1的报文后,首先将发送过来的PSH报文的数据加入到接收缓冲区,然后立即将接收缓冲区内数据向上交付给应用程序,而不是等待缓冲区满后再交付。当两个应用进程进行交互式通信时,有时客户发一个请求给服务器时希望立即能够收到对方的响应,这种情况下,客户应用程序通知TCP使用推送(push)操作,TCP就把PSH置为1,并立即创建一个报文段发送过去;服务器端收到一个设了PSH标志的报文段时就尽快将所有收到的数据立即提交给服务进程,而不在等到整个缓存都填满了再向上交付。
ACK: "Acknowledgment"简写。表示包已经被成功接收。
URG: "urgent"简写。通知接收端处理在处理其他包前优先处理接收到的紧急报文(urgent packets)。当URG = 1时表明紧急指针字段有效,告诉系统此报文段中有紧急数据,应尽快传送,而不要按原来的排队顺序来传送(进入缓存,等缓存区满再提交给应用),发送方的TCP就把紧急数据放到本报文段数据的最前面(因为紧急指针从头开始算)。若URG为0,则紧急指针没有意义、)。值得注意的是即使窗口为0时也可以发送紧急数据,紧急数据不进入接收缓冲区直接交给上层进程。详见RFC6093。
ECE: "ECN-Echo"简写。ECN表示Explicit Congestion Notification。表示TCP peer有ECN能力。详见RFC3168。
CWR: "Congestion Window Reduced"简写。发送者在接收到一个带有ECE flag包时,将会使用CWR flag。 详见RFC3168。
NS: "nonce sum"简写。该标签用来保护不受发送者发送的突发的恶意隐藏报文的侵害。详见 RFC 3540。

wireshark中传输层对应:


服务器收到客户端发过来报文,由SYN=1知道客户端要求建立联机。向客户端发送一个SYN和ACK都置为1的TCP报文,设置初始序号Y=0(seq=0),将确认序号(Acknowledgement Number)设置为客户的序列号加1,即SYN为X+1 = 0+1=1, 如下图。


客户端收到服务器发来的包后检查确认序号(Acknowledgement Number)是否正确,即第一次发送的序号加1(X+1=1),以及标志位ACK是否为1。若正确,客户端再次发送确认包,ACK标志位为1,SYN标志位为0。确认序号(Acknowledgement Number)=Y(seq)+1=0+1=1,发送序号为X+1=1。服务端收到后确认序号值ACK=1,则连接建立成功,可以传送数据了。



TCP断开连接时,会有四次挥手过程,且每个方向都需要一个 FIN 和一个 ACK,因此通常被称为四次挥手。如下图所示,wireshark截获到了四次挥手的四个数据包。


客户端向服务端发送 FIN 时,仅仅表示客户端不再发送数据了但是还能接收数据。

服务器收到FIN后,发回一个ACK(标志位ACK=1),确认序号为收到的序号加1,即X=X+1=2。序号为收到的确认序号=Z。

服务器收到客户端的 FIN 报文时,先回一个 ACK 应答报文,而服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送 FIN 报文给客户端来表示同意现在关闭连接。即服务端通常需要等待完成数据的发送和处理,所以服务端的 ACK 和 FIN 一般都会分开发送,从而比三次握手导致多了一次。

服务器关闭与客户端的连接,发送一个FIN。标志位FIN和ACK置为1,序号为Y=1,确认序号为X=2。


客户端收到服务器发送的FIN之后,发回ACK确认(标志位ACK=1),确认序号为收到的序号加1,即Y+1=2。序号为收到的确认序号X=2。

Tcpdump抓取响应flag值得报文:

因为TCP Flags 位于TCP头的第14个字节中。所有可以通过如下命令进行抓取:

eg:抓取FIN包: tcpdump -i eth0 “tcp[13] & 1” -ennnv
eg:抓取SYN包: tcpdump -i eth0 “tcp[13] & 2” -ennnv
eg:抓取RST包: tcpdump -i eth0 “tcp[13] & 4” -ennnv
eg: 抓取PSH包: tcpdump -i eth0 “tcp[13] & 8” -ennnv
eg:抓取ACK包: tcpdump -i eth0 “tcp[13] & 16” -ennnv
eg:抓取URG包: tcpdump -i eth0 “tcp[13] & 32” -ennnv

四、附录:其他相关

4.1、一起异常网络通信


SYN: (Synchronize sequence numbers)用来建立连接,在连接请求中,SYN=1,ACK=0。连接响应时,SYN=1。ACK=1。即。SYN和ACK来区分Connection Request和Connection Accepted。其中小写ack指Acknowledge Number,是对对端seq的确认,ack=seq+1,表确认seq前的数据成功接收。ISN是SYN的第一个字节的序列号,即初始化序列号,每一个字节流都会分配一个一个序列号,后续的字节序列号为ISN+1,
RST: (Reset the connection)用于复位因某种原因引起出现的错误连接,也用来拒绝非法数据和请求。

RST: (Reset the connection)用于复位因某种原因引起出现的错误连接,也用来拒绝非法数据和请求。

4.2、TCP 连接相关概念

用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括Socket序列号窗口大小称为连接。

什么三次握手才可以初始化Socket、序列号和窗口大小并建立 TCP 连接。

1)RFC 793 指出的 TCP 连接使用三次握手的首要原因:The principle reason for the three-way handshake is to prevent old duplicate connection initiations from causing confusion.即,三次握手的首要原因是为了防止旧的重复连接初始化造成混乱。网络环境是一个复杂的界面,先发送的数据包,不一定先到达目标主机,可能会由于网络拥堵等乱七八糟的原因,会使旧的数据包,先到达目标主机,而三次握手可避免这种情况,如下图所示:

如上所示,当客户端连续发送多次 SYN 建立连接的报文,在网络拥堵等情况下:当一个「旧 SYN 报文」比「最新的 SYN 」 报文早到达了服务端;那么此时服务端就会回一个 SYN + ACK 报文给客户端;客户端收到后可以根据自身的上下文,判断这是一个历史连接(序列号过期或超时),那么客户端就会发送 RST 报文给服务端,表示中止这一次连接。如果是两次握手连接,就不能阻止历史连接。主要是因为在两次握手的情况下,「被动发起方」没有中间状态给「主动发起方」来阻止历史连接,导致「被动发起方」可能建立一个历史连接,造成资源浪费。

这样,两次握手的情况下,「被动发起方」在收到 SYN 报文后,就会进入 ESTABLISHED 状态,意味着这时可以给对方发送数据给,但是「主动发起方」此时还没有进入 ESTABLISHED 状态,假设这次是历史连接,主动发起方判断到此次连接为历史连接,那么就会回 RST 报文来断开连接,而「被动发起方」在第一次握手的时候就进入 ESTABLISHED 状态,所以它可以发送数据的,但是它并不知道这个是历史连接,它只有在收到 RST 报文后,才会断开连接。


上面这种场景下,「被动发起方」在向「主动发起方」发送数据前,并没有阻止掉历史连接,导致「被动发起方」建立了一个历史连接,又白白发送了数据,浪费了「被动发起方」的资源。因此,要解决这种现象,最好就是在「被动发起方」发送数据前,也就是建立连接之前,阻止掉历史连接,这样就不会造成资源浪费,而要实现这个功能,就需要三次握手。在三次握手的情况下, 可以在服务端建立连接之前,可以阻止掉了历史连接,从而保证建立的连接不是历史连接。

如果是历史连接(序列号过期或超时),则第三次握手发送的报文是 RST 报文,以此中止历史连接;如果不是历史连接,则第三次发送的报文是 ACK 报文,通信双方就会成功建立连接;

2)第2个重要原因:同步双方初始序列号

TCP 协议的通信双方, 都必须维护一个「序列号」, 序列号是可靠传输的一个关键因素,它的作用:

接收方可以去除重复的数据;
接收方可以根据数据包的序列号按序接收;
可以标识发送出去的数据包中, 哪些是已经被对方收到的;

当客户端发送携带「初始序列号」的 SYN 报文的时候,需要服务端回一个 ACK 应答报文,表示客户端的 SYN 报文已被服务端成功接收,那当服务端发送「初始序列号」给客户端的时候,依然也要得到客户端的应答回应,这样一来一回,就确保双方的初始序列号能被可靠的同步。而两次握手只保证了一方的初始序列号能被对方成功接收,没办法保证双方的初始序列号都能被确认接收。

3)第3个原因:避免资源浪费
如果只有「两次握手」,当客户端的 SYN 请求连接在网络中阻塞,客户端没有接收到 ACK 报文,就会重新发送 SYN ,由于没有第三次握手,服务器不清楚客户端是否收到了自己发送的建立连接的 ACK 确认信号,所以每收到一个 SYN 就只能先主动建立一个连接,这会造成大量连接资源浪费。即客户端的 SYN 阻塞后,重复发送多次 SYN 报文,那么服务器在收到请求后就会建立多个冗余的无效链接,造成不必要的资源浪费。

4.3、四次挥手的TIME-WAIT 状态

如果 TIME-WAIT 等待足够长的情况就会遇到两种情况:
1>服务端正常收到四次挥手的最后一个 ACK 报文,则服务端正常关闭连接。
2>服务端没有收到四次挥手的最后一个 ACK 报文时,则会重发 FIN 关闭连接报文并等待新的 ACK 报文。然后重复1动作;

客户端在 TIME-WAIT 状态等待 2MSL 时间后,就可以保证双方的连接都可以正常的关闭。

TIME_WAIT 过多时,如果服务器有处于 TIME-WAIT 状态的 TCP,则说明是由服务器方主动发起的断开请求。过多的 TIME-WAIT 状态主要的危害有两种:

第一是内存资源占用;

第二是对端口资源的占用,一个 TCP 连接至少消耗一个本地端口;第二个危害是会造成严重的后果的,要知道,端口资源也是有限的,一般可以开启的端口为 32768~61000,也可以通过如下参数设置指定net.ipv4.ip_local_port_range如果发起连接一方的 TIME_WAIT 状态过多,占满了所有端口资源,则会导致无法创建新连接。

优化 TIME-WAIT 的几个方式,各有利弊:

1、打开 net.ipv4.tcp_tw_reuse 和 net.ipv4.tcp_timestamps 选项;

2、net.ipv4.tcp_max_tw_buckets

3、程序中使用 SO_LINGER ,应用强制使用 RST 关闭

内核开启:

net.ipv4.tcp_tw_reuse = 1 #使用这个选项,还有一个前提,需要打开对 TCP 时间戳的支持,即
net.ipv4.tcp_timestamps=1(默认即为 1)

开启后,就可以复用处于 TIME_WAIT 的 socket 为新的连接所用。有一点需要注意的是,tcp_tw_reuse 功能只能用客户端(连接发起方),因为开启了该功能,在调用 connect() 函数时,内核会随机找一个 time_wait 状态超过 1 秒的连接给新的连接复用。

其中,时间戳的字段是在 TCP 头部的「选项」里,用于记录 TCP 发送方的当前时间戳和从对端接收到的最新时间戳。由于引入了时间戳,我们在前面提到的 2MSL 问题就不复存在了,因为重复的数据包会因为时间戳过期被自然丢弃。

方式二:net.ipv4.tcp_max_tw_buckets这个值默认为 18000,当系统中处于 TIME_WAIT 的连接一旦超过这个值时,系统就会将所有的 TIME_WAIT 连接状态重置。这个方法过于暴力,而且治标不治本,带来的问题远比解决的问题多,不推荐使用。

方式三:程序中使用 SO_LINGER我们可以通过设置 socket 选项,来设置调用 close 关闭连接行为。struct linger so_linger;
so_linger.l_onoff = 1;
so_linger.l_linger = 0;
setsockopt(s, SOL_SOCKET, SO_LINGER, &so_linger,sizeof(so_linger));如果l_onoff为非 0, 且l_linger值为 0,那么调用close后,会立该发送一个RST标志给对端,该 TCP 连接将跳过四次挥手,也就跳过了TIME_WAIT状态,直接关闭。但这为跨越TIME_WAIT状态提供了一个可能,不过是一个非常危险的行为,不值得提倡。

4.4、什么是SYN洪范泛攻击?

SYN Flood利用TCP协议缺陷,发送大量伪造的TCP连接请求,常用假冒的IP或IP号段发来海量的请求连接的第一个握手包(SYN包),被攻击服务器回应第二个握手包(SYN+ACK包),因为对方是假冒IP,对方永远收不到包且不会回应第三个握手包。导致被攻击服务器保持大量SYN_RECV状态的“半连接”,并且会重试默认5次回应第二个握手包,大量随机的恶意syn占满了未完成连接队列,导致正常合法的syn排不上队列,让正常的业务请求连接不进来。【服务器端的资源分配是在二次握手时分配的,而客户端的资源是在完成三次握手时分配的,所以服务器容易受到SYN洪泛攻击】;当在服务器上看到大量的半连接状态时,特别是源IP地址是随机的,基本上可以断定这是一次SYN攻击。

4.5、TCP三次握手中,最后一次回复丢失,会发生什么?

如果最后一次ACK在网络中丢失,那么Server端(服务端)该TCP连接的状态仍为SYN_RECV,并且根据 TCP的超时重传机制依次等待3秒、6秒、12秒后重新发送 SYN+ACK 包,以便 Client(客户端)重新发送ACK包;

如果重发指定次数后,仍然未收到ACK应答,那么一段时间后,Server(服务端)自动关闭这个连接;

但是Client(客户端)认为这个连接已经建立,如果Client(客户端)端向Server(服务端)发送数据,Server端(服务端)将以RST包(Reset,标示复位,用于异常的关闭连接)响应,此时,客户端知道第三次握手失败。

4.6、TCP状态变迁图

下图中,粗的实线箭头表示正常的客户端状态变迁,粗的虚线箭头表示正常的服务器状态变迁。

1)第一次握手:客户端给服务端发一个 SYN 报文,并指明客户端的初始化序列号 ISN©。此时客户端处于 SYN_Send 状态
2)第二次握手:服务器收到客户端的 SYN 报文之后,会以自己的 SYN 报文作为应答,并且也是指定了自己的初始化序列号 ISN(s),同时会把客户端的 ISN + 1 作为 ACK 的值,表示自己已经收到了客户端的 SYN,此时服务器处于 SYN_REVD 的状态
3)第三次握手:客户端收到 SYN 报文之后,会发送一个 ACK 报文,当然,也是一样把服务器的 ISN + 1 作为 ACK 的值,表示已经收到了服务端的 SYN 报文,此时客户端处于 establised 状态
4)服务器收到 ACK 报文之后,也处于 establised 状态,此时,双方以建立起了链接。

其中:三次握手的一个重要功能是客户端和服务端交换ISN(Initial Sequence Number), 以便让对方知道接下来接收数据的时候如何按序列号组装数据。如果ISN是固定的,攻击者很容易猜出后续的确认号,因此 ISN 是动态生成的。

其他参考:https://www.iamshuaidi.com/747.html

网络分析流量FIN_WAIT_2状态解释相关推荐

  1. 网络的FIN_WAIT_2状态解释和分析

    网络的FIN_WAIT_2状态解释和分析 一.总结 一句话总结:出现fin_wait_2一般为客户端,如果为服务端出现,则表明是服务端主动发起的断开 还是要系统的学,这样很有问题,学不到什么,系统看书 ...

  2. FIN_WAIT_2状态解释

    在HTTP应用中,存在一个问题,SERVER由于某种原因关闭连接,如KEEPALIVE的超时,这样,作为主动关闭的SERVER一方就会进入 FIN_WAIT2状态,但TCP/IP协议栈有个问题,FIN ...

  3. TCP套接口的FIN_WAIT_2状态超时

    PROC文件tcp_fin_timeout默认为60秒,内核中相应的变量为init_net.ipv4.sysctl_tcp_fin_timeout,不过其以jiffies表示,默认值为TCP_FIN_ ...

  4. 网站流量统计名词解释

    网站流量统计名词解释 访问者 访问者这一概念的设计是为了尽量真实地确定访问网站的不同个人的实际人数.当 然,从网站方面讲,无法了解是否有两个人共享一台计算机,访问跟踪系统用IP+Cookie保持对不同 ...

  5. 关于报工和生产订单的一些状态解释

    关于报工和生产订单的一些状态解释 工单报工也称为工单确认,而工单确认类型又有三种:完全确认.部分确认和自动最终确认.这三个有什么区别呢?完全确认是将工单的数量如数报完了,而部分确认只是将工单的一部分数 ...

  6. TIME_WAIT状态和FIN_WAIT_2状态

    文章目录 **TIME_WAIT状态** **FIN_WAIT_2状态** TCP的可靠传输 TIME_WAIT状态 TIME_WAIT 状态,又称为 2MSL 等待状态.只有主动关闭一方才能进入 T ...

  7. pod重启策略和状态解释

    一.重启策略:Pod在遇到故障之后重启的动作 1.always 2.never 3.onfailure 3.1 非0状态 3.2 为0状态 二.探针 附:pod各种状态解释: 1.Pod一直处于Pen ...

  8. TCP状态转换以及TIMEWAIT和FIN_WAIT_2状态

    tcp状态转换图 这张图需要分为三个部分去看 第一部分:粗实线部分,表示发起主动连接或主动关闭请求.一般先发起主动连接和断开请求的都为客户端,因此可以认为这部分是客户端的状态变化. 第二部分:虚线部分 ...

  9. K8S---Pod重启策略和状态解释

    目录 一.重启策略:Pod在遇到故障之后重启的动作 1.1  重启策略设置建议 1.2  always 1.3   never 1.4  onfailure 1.4.1  非0状态 1.4.2  为0 ...

最新文章

  1. Android App的启动过程
  2. android 左移动画_Android研究院之游戏开发Tween动画的实现(十九)
  3. Android平台Qt开发入门教程
  4. 展望二十一世纪第三个十年
  5. sql语句和java的关系_java中Statement 与 PreparedStatement接口之间的关系和区别
  6. angularjs -- 页面模板清除
  7. 语言压缩zip win_主流压缩软件挨个尝试后,我选择了没有短板的全能压缩Bandizip...
  8. Oracle数据库学习心得(一)
  9. 毕业设计论文选题系统系统用例图_基于UML的毕业设计管理系统的分析与设计
  10. 【原创】CSS3动画总结对比 / 带思维导图 / 启发小案例
  11. java tm 2已停止工作_Java(TM) 2 Platform Standard Edition binary 已停止工作
  12. 苹果妙控鼠标二代(Magic Mouse 2 )如何连接到 Window 10系统
  13. Python学习笔记-基础篇
  14. 带阵列卡的服务器如何装系统,带阵列卡的服务器能不能做ghost
  15. python找房源_python抓取链家房源信息(三)
  16. 光伏电站清扫机器人_光伏电站清扫机器人_雷曼科林
  17. 量子计算深化:大规模量子计算(相关论文108篇推荐)
  18. uni-app 对照设计稿还原不同屏幕像素适配(iPhone X)
  19. 从G1到冻酸奶Froyo
  20. 鸿蒙系统登lol一直闪退,win8.1系统玩lol英雄联盟出现闪退解决方法

热门文章

  1. 外卖平台乱象迭出!究竟谁该负责?
  2. 数字转罗马数字_理解罗马数字
  3. 计算机学院新生教育讲话,吴兴隆在数计学院2017级新生开学典礼上的讲话
  4. 软件工程——成本效益分析
  5. 有什么蓝牙耳机戴着比较舒服?佩戴舒适的蓝牙耳机推荐
  6. 反应式编程框架设计:如何使得程序调用不阻塞等待
  7. 性能篇 | 17 | jmeter | BeanShell内置变量prev的使用技巧
  8. self_drive car_学习笔记--第8课:定位算法
  9. 人类群星闪耀时——决定人类历史的10个瞬间
  10. bilibili服务器维护7月,GitHub - thestarweb/bilibilidan: 一个B站弹幕姬,就是不会在第一时间维护。。支持插件!!...