TCP协议实践过程中现象解释

目录

1 实验现象

1.1 发送端与接收端已建立连接,发送端socket连接状态已消失,接收端socket依然处于ESTAB状态且长时间未消失

1.2 模拟RecvServer 未收到FIN或ACK丢失

1.3 模拟RecvServer接收窗口过小

1.4 模拟RecvServer不接收数据

2 知识拓展


以下文章篇幅主要借鉴其他大神博客,博客原文地址:https://blog.csdn.net/dog250/article/details/81697403。文章中加入自己的见解和实验结果,加深自己的理解。

工程实践中经常遇到各种稀奇古怪的网络问题。以下文章中记录遇到的各种问题:

发送客户端简称SendClient,接收客户端简称RecvServer。默认客户端主动断开。

TCP假连接:TCP的一端已经逃逸出了TCP状态机,而另一端却不知道

1 实验现象

1.1 发送端与接收端已建立连接,发送端socket连接状态已消失,接收端socket依然处于ESTAB状态且长时间未消失

现象:SendClient与RecvServer三次握手已建立成功前提下,SendClient端对应socket连接已释放,但RecvServer端对应socket连接处于ESTABLISH状态,且长时间不释放

原因:SendClient端连接已释放,但未通知RecvServer端,而RecvServer socket也未设置keepalive,导致假连接一直存在。FIN_WAIT_1状态持续1个RTT周期。客户端发送FIN命令起始,到接收到ACK响应,刚好一个周期。

1.2 模拟RecvServer 未收到FIN或ACK丢失

若客户端已发送FIN命令,连接状态切换成FIN_WAIT_1,但服务端ACK丢失或未收到客户端FIN。后续连接如何变化呢?如果TCP没有做任何保护机制,可以预料的是客户端SendClient连接将永远保持FIN_WATI1状态。

模拟步骤:

1)SendClient执行:nc -l -p 11111.运行nc命令监听本地11111端口

2)RecvServer执行:cat /dev/zero | nc xxx.xxx.xxx.xxx 11111.将cat输出文件内容发送到xxx.xxx.xxx.xxx IP的端口11111

此时SendClient与RecvServer进程已建立连接并持续传输数据

3)SendClient:iptables -A INPUT -p tcp --tcp-flags ACK,FIN ACK -j REJECT

检查tcp协议输入的ACK,FIN状态,但拒绝ACK命令。SendClient因拒绝ACK命令,所以未收到ACK

4)SendClient:kill nc 进程。ss -tnp | grep 11111 查看连接状态。

实验结果:

SendClient状态由ESTABLISH变为FIN_WAIT_1状态,稍等片刻后,SendClient连接断开,通过netstat -st可看到因超时导致连接断开相关信息,但RecvServer一直处于ESTABLISH状态。

原因:

SendClient kill 进程后,用户态调用close()系统调用发FIN给对端,此时FIN和数据一起在发送缓冲区中排队,SendClient状态立即变为FIN_WAIT_1状态,随后因本端socket无法收到数据ACK,所以队尾的FIN实际没有发送出去(通过tcpdump抓包观察)。此时SendClient因无法收到数据ACK,尝试重新发送,尝试N次退避算法后,终止SendClient FIN_WAIT_1状态,但RecvServer一直处于ESTABLISH状态。表现为一端有连接信息,一端无连接信息。注:两端都未设置keepalive(默认关闭),默认keepalive两小时生效

注:设置iptables是为了保证FIN前数据发送出去后一直收不到ACK返回,尝试重新发送(RTO退避机制)。如果未设置iptables,TCP状态机运行正常。因超时机制导致连接被强制断开,可通过netstat -st查看TCPExt,可看到1 connections aborted due to timeout消息。

结论:

主动断开端调用了close关掉了进程或kill -9 杀死进程,都会发送FIN,进入FIN_WAIT_1状态,如果它再也收不到ACK,无论是针对pending在发送缓冲的数据还是FIN,它都会尝试重新发送,在收到ACK前会尝试N次退避,该N由tcp_orphan_retries参数控制,超过尝试次数后,强制断开连接。但接收端因无法收到FIN,且未设置keepalive选项,一直处于ESTABLISH状态。(orphan孤儿)

1.3 模拟RecvServer接收窗口过小

模拟步骤:

1)RecvServer:sysctl -w net.ipv4.tcp_rmem="16 32 32"。设置接收缓冲区大小

2)RecvServer:nc -l -p 11111

3)SendClient:cat /dev/zero | nc 127.0.0.1 11111。等待几秒,让发送缓冲区堆积数据。

4)SendClient:killall nc杀死进程。

此时使用ss -tnp可看到连接状态变为FIN_WAIT_1状态。

实验结果:

可看到发送缓冲区数据量逐渐减少,数据继续发送。在发送缓冲区数据未发送完毕情况下,接收端连接一直establish状态,待数据发送完毕接收端连接才消失。间接说明FIN命令在发送缓冲区数据队列末尾。

此时引申出一个问题。因接收端接收缓冲区过小,导致接收窗口为0,一直发送TCP Zero Windows消息通知发送方,发送方将暂缓数据发送,那数据发送方何时知道应该发送数据呢?

RecvServer接收窗口有空间时,RecvServer会主动发送消息告诉SendClient TCP Window update。不明白,接收端已经告诉发送端接收窗口大小为4字节,为什么发送端还发送16字节得数据?希望各位看官如果知道,下面评论答复。

1.4 模拟RecvServer不接收数据

模拟步骤:

1)RecvServer接收缓冲区恢复默认大小。

2)RecvServer:nc -l -p 11111

3)SendClient:cat /dev/zero | nc 127.0.0.1 11111。等待几秒,让发送缓冲区堆积数据。

4)RecvServer执行killall -STOP nc使进程暂停,但不退出,此时不接收数据。

发送端发送缓冲区数值大小受接收端接收缓冲区大小影响

实验结果:

本机测试时接收和发送端连接状态一直保持EATAB并未消失,与原文地址连接结果不相符。但如果使用SendClient执行kill -9命令使得SendClient状态进入FIN_WAIT_1状态,因为收不到ACK,再退避时间后,SendClient连接状态消失,但RecvServer状态一直存在。和现象一结果一致。

使用killal -STOP信息暂停接收端进程,导致发送端和接收端连接状态一直处于ESTAB状态。以上是模拟得情况,实际开发过程中可能遇到如下情况:

  • 接收端进程出现异常,或者接收端内核存在缺陷,导致进程挂死而软中断上下文的协议栈处理正常运行;
  • 接收端就是一个恶意的DDoS进程,故意不接收数据以诱导发送端在FIN_WAIT2状态(甚至ESTAB状态),发送数据不成后发送零窗口探测而不休止。

以上现象都会导致TCP状态机进入僵死,为避免此现象,引入控制层keepalive机制。

在具体实现上,防止状态机僵死的方法分为两类:

1)代码层次:用户进程设置keepalive机制.

2)内核层次:使用retries内核参数设置timeout.

[root@localhost ~]# sysctl -a|grep retries
net.ipv4.tcp_orphan_retries = 0
net.ipv4.tcp_retries1 = 3
net.ipv4.tcp_retries2 = 15
net.ipv4.tcp_syn_retries = 6
net.ipv4.tcp_synack_retries = 5
net.ipv6.idgen_retries = 3

如果主动断开端调用了close关掉了进程,它会进入FIN_WAIT1状态,如果接收端的接收窗口呈现关闭状态(零窗口,即接收端不接收数据),此时它会不断发送零窗口探测包。发送多少次呢?有两种实现:

  • 低版本内核(至少社区3.10及以下):永久尝试,如果探测ACK每次都返回,则没完没了。
  • 高版本内核(至少社区4.6及以上):限制尝试tcp_orphan_retries次,不管是否收到探测ACK

注意:以上标红部分未验证

2 知识拓展

1)iptables -A INPUT -i eth0 -s 192.168.100.0/24 -p tcp --dport 3128 -m state --state NEW,ESTABLISHED -j ACCEPT

       允许192.168.100.0/24网段的机器发送数据包从eth0网卡进入。如果数据包是tcp协议,且目的端口为3128,数据包状态是NEW或者ESTABLISHED的允许通过。(NEW代表tcp三段式握手的“第一握”,即客户端机器向服务器发出链接申请)

2)使用 –tcp-flags 选项可以根据tcp包的标志位进行过滤。
       #iptables -A INPUT -p tcp –tcp-flags SYN,FIN,ACK SYN
       #iptables -A FROWARD -p tcp –tcp-flags ALL SYN,ACK
        第一个表示SYN、ACK、FIN的标志都检查,但是只有SYN匹配。第二个表示ALL(SYN,ACK,FIN,RST,URG,PSH)的标志都检查,但是只有设置了SYN和ACK的匹配

3)sudo iptables -L

列出所有规则

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值