实验模拟TCP连接的各种异常情况(三次握手丢包,两端异常)

news2024/9/20 16:31:50

实验模拟TCP连接的各种异常情况(三次握手丢包,两端异常)

环境搭建

秋招结束,闲来无事,正好把计算机网络一些协议实验过一遍,于是用vmware搭建了一个两机通信的环境,在建立环境的过程中遇到了一些坑点,现记录以作备忘使用。

现环境演示如下:

HostA:				  
Name:razer			  
IP:192.168.200.129	  
MAC:00:0c:29:87:7e:42
OS:Ubuntu20.04 LTS   
Interface:ens33

HostB:
name:aaronVM
IP:192.168.200.130
MAC:CentOS7
Interface:ens33

验证TCP三次握手丢包

Ubuntu机(192.168.200.129)作为客户端,CentOS机(192.168.200.130)作为服务端。

第一次握手SYN丢包
模拟第一次丢包可模拟服务端down掉的情况,将服务端的网线断掉,然后客户端终端1开 tcpdump后终端2开curl,设置如下
tcpdump -i ens33 tcp and host 192.168.200.130 and port 80 -w tcp_sys_timeout.pcap

date;curl http://192.168.200.130;date

大约1分钟后客户端超时:

image-20221204190956965

将产生的文件提取出来wireshark抓包:

image-20221204191122083

可以看到服务端第一次连接之后重传了5次,每一次重传时间的间隔都是上一次重传间隔的两倍,控制tcp重传次数的内核参数是/proc/sys/net/ipv4/tcp_syn_retries,我查了自己客户端的参数值是5, 也就是 SYN 最大重传次数是 5 次。

cat /proc/sys/net/ipv4/tcp_syn_retries
5

通过实验一的实验结果,我们可以得知,当客户端发起的 TCP 第一次握手 SYN 包,在超时时间内没收到服务端的 ACK,就会在超时重传 SYN 数据包,每次超时重传的 RTO 是翻倍上涨的,直到 SYN 包的重传次数到达 tcp_syn_retries 值后,客户端不再发送 SYN 包。

SYN 超时重传

做这个实验注意服务端关连接之前至少要让客户端成功连接服务端一次,不然客户端会有记忆机制,达不到重传次数就会快速返回连接失败。

第二次握手 SYN、ACK 丢包

模拟客户端收不到服务端第二次握手可通过给客户端加上防火墙拦截的方法实现,直接简单粗暴的把从服务端收到的包都丢弃。

在ubuntu机配置防火墙如下

iptables -I INPUT -s 192.168.200.130 -j DROP

随后做法同第一次试验,提出pcap文件,得:

tcpdump -i ens33 tcp and host 192.168.200.130 and port 80 -w tcp_synack_timeout.pcap
date;curl http://192.168.200.130;date

image-20221204200908879

可以发现,客户端发送syn报文,服务端收到syn报文后回发syn-ack报文,由于客户端的防火墙会拦截并丢弃服务端的syn-ack报文,因此客户端一段时间得不到回应便会自动重发syn,且每次发送间隔是上一次间隔的2倍,服务端收到客户端syn报文后其超时定时器并不会重置,在隔到指定时间后仍会重发synack报文,且报文的重发间隔也是上一次间隔的两倍。

最后客户端和服务端在接受不到应有的回答后各自重传均达到5次后断开连接。

客户端重传第一次握手由tcp_syn_retries控制,服务端重传第二次握手由tcp_synack_retries控制,

我查了自己两个虚拟机的这两个内核参数都是5,对应就是图中客户端和服务端在接受不到应有的回答后各自重传均达到5次后断开连接。

将客户端tcp_syn_retries改为1,服务端tcp_synack_retries不变仍为5,再次实验结果:

echo 1 > /proc/sys/net/ipv4/tcp_syn_retries

可以看到不到3秒客户端显示断连,但是不要马上中断tcpdump抓包,等上大概1多分钟,不然服务端达不到重传次数:

image-20221204202942400

最终的结果如下:

image-20221204203128741

可以看到,客户端只重传了1次,服务端之后重传了5次,分别符合各自的内核参数tcp_syn_retriestcp_synack_retries

总结:

通过实验二的实验结果,我们可以得知,当 TCP 第二次握手 SYN、ACK 包丢了后,客户端 SYN 包会发生超时重传,服务端 SYN、ACK 也会发生超时重传。

客户端 SYN 包超时重传的最大次数,是由 tcp_syn_retries 决定的,默认值是 5 次;服务端 SYN、ACK 包时重传的最大次数,是由 tcp_synack_retries 决定的,默认值是 5 次。

实验完毕,开始下一次实验,不要忘了还原现场:

iptables -F	#清除所有规则
iptables -L #列举所有规则 用于检查
#客户端
echo 5 > /proc/sys/net/ipv4/tcp_syn_retries
#服务端
echo 5 > /proc/sys/net/ipv4/tcp_synack_retries
第三次握手ACK丢包

模拟第三次握手丢包,可对服务端设置防火墙规则,拦截来自客户端的ack:

iptables -I INPUT -s 192.168.200.129 -p tcp --tcp-flag ACK ACK -j DROP

随后客户端架tcpdump,用telnet连接客户端80端口,就会出现如下情景:

image-20221204211108250

可见服务端由于一直收不到客户端的ACK报文停留在SYN_RECV状态,而客户端由于收到服务端的第二次握手SYN-ACK,已处于ESTABLISH状态。

服务端得不到ACK将会重传SYN_ACK报文,过了一分钟,服务端达到最大重传次数,便中断连接,客户端仍保持ESTABLISH状态

image-20221204211548752

客户端用telnet向服务端发送数据,由于服务端已下线,客户端将一直处于阻塞状态。

root@razer:/home/aaron# telnet 192.168.200.130 80
Trying 192.168.200.130...
Connected to 192.168.200.130.
Escape character is '^]'.
123456

中断tcpdump查看抓包结果:

image-20221204212402744

可以发现客户端的ACK到达服务端后被拦截丢弃,致使服务端无法收到客户端响应,启动超时重传机制重发SYNACK,达到内核tcp_synack_retries的5次重传上限后断连退出,而客户端仍保持在ESTABLISH状态,使客户端发送数据,由于服务端已CLOSE无法响应,客户端便会一直试图重传,且重传的时间间隔越来越大,但并不是超时重传的每次乘2倍的算法。

客户端重传数据的内核参数由tcp_retries2控制,默认为15,据此上述客户端并不会一直重传下去,而且在重传次数达到15次后退出,这个过程大概花费半小时的时间,碍于时间关系就不验证了。

root@razer:/mnt/hgfs/AARON# cat /proc/sys/net/ipv4/tcp_retries2
15

如果客户端一直不发送数据,TCP也有机制断开其连接,负责这个任务的是tcp的保活机制,这个机制的原理是这样的:

定义一个时间段,在这个时间段内,如果没有任何连接相关的活动,TCP 保活机制会开始作用,每隔一个时间间隔,发送一个「探测报文」,该探测报文包含的数据非常少,如果连续几个探测报文都没有得到响应,则认为当前的 TCP 连接已经死亡,系统内核将错误信息通知给上层应用程序。

在 Linux 内核可以有对应的参数可以设置保活时间、保活探测的次数、保活探测的时间间隔,以下都为默认值:

net.ipv4.tcp_keepalive_time=7200
net.ipv4.tcp_keepalive_intvl=75  
net.ipv4.tcp_keepalive_probes=9
  • tcp_keepalive_time=7200:表示保活时间是 7200 秒(2小时),也就 2 小时内如果没有任何连接相关的活动,则会启动保活机制
  • tcp_keepalive_intvl=75:表示每次检测间隔 75 秒;
  • tcp_keepalive_probes=9:表示检测 9 次无响应,认为对方是不可达的,从而中断本次的连接。

也就是说在 Linux 系统中,最少需要经过 2 小时 11 分 15 秒才可以发现一个「死亡」连接。

img

这个时间是有点长的,所以如果我抓包足够久,或许能抓到探测报文。

实验三的小结如下:

在建立 TCP 连接时,如果第三次握手的 ACK,服务端无法收到,则服务端就会短暂处于 SYN_RECV 状态,而客户端会处于 ESTABLISHED 状态。

由于服务端一直收不到 TCP 第三次握手的 ACK,则会一直重传 SYN、ACK 包,直到重传次数超过 tcp_synack_retries 值(默认值 5 次)后,服务端就会断开 TCP 连接。

而客户端则会有两种情况:

  • 如果客户端没发送数据包,一直处于 ESTABLISHED 状态,然后经过 2 小时 11 分 15 秒才可以发现一个「死亡」连接,于是客户端连接就会断开连接。(前提是客户端要打开keepalive选项,否则就会一直持续下去)
  • 如果客户端发送了数据包,一直没有收到服务端对该数据包的确认报文,则会一直重传该数据包,直到重传次数超过 tcp_retries2 值(默认值 15 次)后,客户端就会断开 TCP 连接。

两端异常

服务端有数据传输,客户端主机拔线

这个场景下找不到能用于模拟的服务端和指令,我自己写了一个回声服务端,为了刚好使得能在服务端传输数据之前使客户端异常,我在回声服务端的write函数前加入了sleep语句,这个新的服务端称为echo_server_slow,这个实验我让服务端监听8080端口:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<fcntl.h>
#include<sys/socket.h>
#include<sys/epoll.h>

#define MAX_CONNECTION 100
#define EPOLL_SIZE 1024


int tcp4Bind(int port){
    int servfd = socket(PF_INET,SOCK_STREAM,0);
    struct sockaddr_in serv_addr;
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(port);
    int optval = 1;
    if(setsockopt(servfd,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval)) == -1){
        perror("setsockopt() error");
        return -1;
    }  

    if(bind(servfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1){
        perror("bind() error");
        return -1;
    }
    if(listen(servfd,MAX_CONNECTION) == -1){
        perror("listen() error");
        return -1;
    }
    return servfd;
}

int main(){
    int servfd = tcp4Bind(8080);
    if(servfd == -1){
        printf("tcp4bind() error\n");
        return -1;
    }
    struct sockaddr_in clnt_addr;
    socklen_t clnt_addr_len;
    char rebuf[BUFSIZ];
	
    //创建红黑树,根节点为epfd
    //内核2.6.0之后 不用再实际指明监视数组的数量 参数不强制 仅给内核建议
    int epfd = epoll_create(1);
    //ev描述事件 events用于返回变化的fd集合
    struct epoll_event ev, events[EPOLL_SIZE];
    ev.data.fd = servfd;
    ev.events = EPOLLIN;
    epoll_ctl(epfd, EPOLL_CTL_ADD, servfd, &ev);


    while(1){
        printf("\nlistening......\n");
        //epoll_wait会注册变化的fd集合到events并返回变化的事件数
        int event_cnt = epoll_wait(epfd, events, EPOLL_SIZE, -1);
        if(event_cnt == -1){
            perror("error");
            break;
        }
        else{
            //和select和poll不同的是 epoll是由内核管理fd, 监测会返回一个只有变化fd的数组, 这是其优势
            for(int i = 0; i < event_cnt; i++){
                int sockfd = events[i].data.fd;
                if(events[i].data.fd == servfd){
                    int sockfd = accept(servfd,(struct sockaddr *)&clnt_addr, &clnt_addr_len);
                    printf("new sockfd %d connected from %s:%d",sockfd, inet_ntoa(clnt_addr.sin_addr), ntohs(clnt_addr.sin_port));
                    //更新监控fd的数据结构
                    ev.data.fd = sockfd;
                    ev.events = EPOLLIN;
                    epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&ev);
                }
                else{
                    int rlen = recv(sockfd, rebuf, sizeof(rebuf),0);
                    if(!strncmp(rebuf,"quit",4)){
                        epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL);
                        close(sockfd); 
                    }
                    else{
                    	//设置睡眠,使得服务端发数据前有时间对客户端动手脚
                    	sleep(5);
                        send(sockfd,rebuf,rlen,0);
                        memset(rebuf,0,sizeof(rebuf));
                    }
                }
            }
        }

    }

    close(servfd);
    close(epfd);
    
    return 0;
}

理论上服务端在得不到回应时会重发数据包,CentOS机负责控制重发次数的内核参数tcp_retries2默认值是15,大概要半小时才会重发完15次退出连接,这个时间实在是太久了,故此我将CentOS机的这个参数改为3, 这样大概1分钟就能跑完连接:

echo 5 > /proc/sys/net/ipv4/tcp_retries2

在CentOS机部署echo_server_slow, 然后部署tcpdump(注意这次是在服务端部署了,因为之前握手丢包模拟用的是iptables收到后丢弃,tcpdump是能抓到重传的包的)

tcpdump -nn -i ens33 tcp and host 192.168.200.129 and 192.168.200.130 -w tcp_client_disconnect.pcap

用Ubuntu机telnet向服务端发数据,趁机在5秒内将Ubuntu机的连接断开,然后netstat查看指令,可看到双方当前连接仍然存在:

image-20221205201502484

约1分钟,服务端显示连接已消失,而客户端仍保持连接, 这里也可见到重连断开的机制是由内核设定的,并不是由应用程序主动设置的:

image-20221205201557802

这时候恢复客户端的连接,往telnet再输入数据,就会发现telnet连接被断开了,同时客户端的连接也消失了。

分析tcpdump的抓包结果,由抓包结果可知,如果客户端断连后服务端有数据传输,那客户端不会给服务端响应,这样服务端就会一直重发数据,直到达到最大重传次数后退出,而如果客户端在服务端退出后再进行数据传输,由于之前的连接已经CLOSE,那么服务端收到后就会回发RST报文关闭客户端连接。

image-20221205202301415

服务端有数据传输,客户端主机崩溃

模拟服务端在传输数据时客户端崩溃的做法是,在服务端数据送到之前立刻将客户端挂起。

由于挂机的切换时间较长,容易出现客户端还没有恢复服务端就到达最大重发次数的情况,故这里将重发次数调长了些:

echo 7 > /proc/sys/net/ipv4/tcp_retries2

开始实验,centOS同上一个实验部署tcpdump和回声客户端,Ubuntu机用telnet客户端连接,发送数据后立刻挂起,然后恢复,会发现之前的连接并未消失,客户端能够得到重发的数据,连接保持正常。

image-20221205204520883

但这并不能说明主机崩溃的情况下一定就能保存连接,现在直接让ubuntu机重启,再次telnet连接服务端,查看情况,可发现之前的连接已经消失了,重启后再telnet的连接是新连接:

image-20221205205314150

tcpdump抓包全过程如下:

image-20221205210004942 image-20221205210642631

由抓包过程可见,如果连接过程中客户端发生了断电等情况导致进程崩溃,那么客户端就会向服务端发送FIN断连,这样之前的连接就不存在了,下次客户端重新连接就会采用新的四元组连接。图中可见发送FIN的四次挥手不是完整的,不知道为什么,可能是客户端发完FIN之后就重启了,收不到了。

综上,对于挂起,拔线等没有导致进程崩溃的情况,只要客户端能在服务端的重传时间内重新连上服务端,就能够保持之前的连接,否则服务端一旦达到最大重传次数就会自动断开连接,之后客户端再次若尝试连上服务端,服务端便会无情地回复RST中断连接;

对于重启(包括关机再重启),SIGINT中断,kill等导致客户端进程崩溃的情况,客户端会向服务端发送FIN报文断开连接,将先前的连接清除,下次客户端再度重新连接服务端时,将采用新的四元组连接。

至于服务端没传数据的情况则更加简单,若客户端进程拔线,则原有连接不影响,服务端将在一定时间后启动tcp保活机制检测连接是否“活着”,若此时客户端仍不活跃,服务端将会清除该连接。而客户端主机宕机,进程崩溃的情况就是直接断连了,大家可以自己去验证。

参考

小林Coding

tcpdump抓包教程:https://www.cnblogs.com/XDU-Lakers/p/13149926.htmls.com/XDU-Lakers/p/13149926.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/64759.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HTTP Mime-Type对照表

HTTP Mime-Type对照表 : Content-Type&#xff08;Mime-Type&#xff09; 文件扩展名 小贴士&#xff1a;CtrlF 快速查找 Mime-Type类型 文件扩展名Content-Type(Mime-Type)文件扩展名Content-Type(Mime-Type).*&#xff08;二进制流&#xff0c;不知道下载文件类型&#xff09…

Java基础教程:多线程(4)-----线程的生命周期

任何对象都有生命周期&#xff0c;线程也不例外&#xff0c;它也有自己的生命周期。线程的整个生命周期分为六个阶段&#xff0c;分别是新建状态(New)、可运行状态(Runnable)、运行状态(Running)、阻塞状态(Blocked)、等待状态(Waiting)和死亡状态(Terminated)&#xff0c;线程…

1 基础知识

基础知识 1 汇编语言产生 1.1 机器指令 机器语言是机器指令的集合。机器指令展开来讲就是一台机器可以正确执行的命令。电子计算机的机器指令是一列二进制数字。计算机将之转变为一列高低电平&#xff0c;以使计算机的电子器件受到驱动&#xff0c;进行运算。 每一种微处理…

r语言绘制动态统计图:绘制世界各国的人均GDP,出生时的期望寿命和人口气泡图动画动态gif图

使用的数据 nations.csv 来自世界银行指标的数据。warming.csv 有关1880年至2017年全球年平均温度 。 yearvalue 全球平均温度&#xff0c;与1900-2000年的平均温度相比。 simulations.csv美国国家航空航天局&#xff08;NASA&#xff09;对历史温度的模拟数据&#xff0c;估计…

NetSuite Plug-In 101

进入了我的学术休假季&#xff0c;开始做一些自己喜欢的题目。今天就来扒一扒一个NetSuite落满灰尘的功能--Plug-In。 大家可能听到过一个叫做Email Approval的应用场景&#xff0c;可以让用户在不登录NetSuite系统的情况下&#xff0c;跟NetSuite产生交互。例如&#xff0c;通…

ArcGIS基础:字段的别名和字段属性域设置

【1】别名设置&#xff1a; 别名设置时需要注意的是在SHP格式里&#xff0c;进行设置仅仅是临时存贮&#xff0c;关闭属性表后&#xff0c;属性的别名就会丢失&#xff0c;需要将其存储到地理数据库里才可以永久包括。 存放在地理数据库后&#xff0c;找到数据&#xff0c;右…

Replugin 插件中MutliDex 分多包 后加载插件报错的问题解决

Caused by: java.lang.ClassNotFoundException: Didn’t find class “com.qihoo360.replugin.Entry” on path: DexPathList[[zip file “/data/app/com.topdraw.replugintest2-1/base.apk”],nativeLibraryDirectories[/data/app/com.topdraw.replugintest2-1/lib/x86, /syst…

图论算法(2)—————拓扑排序

在几天前写了一篇最小生成树的文章 今天再讲解一下图论的另一个算法&#xff1a;拓扑排序 注&#xff1a;今天只讲解kahn算法&#xff0c;各位如果对dfs算法有需求可联系我进行讲解 说到拓扑排序&#xff0c;不得不先了解下拓扑是个什么东东 拓扑&#xff0c;它是一种结构 …

Elasticsearch 进阶

一、核心概念 1.1 索引(Index) 一个索引就是一个拥有几分相似特征的文档的集合。比如说,你可以有一个客户数据的索引,另一个产品目录的索引,还有一个订单数据的索引。一个索引由一个名字来标识(必须全部是小写字母),并且当我们要对这个索引中的文档进行索引、搜索、更新…

[附源码]计算机毕业设计JAVA音乐交流平台

[附源码]计算机毕业设计JAVA音乐交流平台 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis M…

【数字逻辑】——逻辑函数及其简化(学习笔记)

&#x1f4d6; 前言&#xff1a;1849年英国数学家乔治&#xff0c;布尔&#xff08; George Boole &#xff09;首先提出了描述客观事物逻辑关系的数学方法&#xfe63;布尔代数。1938年克劳德&#xff0e;香农&#xff08; Claude E . Shannon &#xff09;将布尔代数应用到继…

深度学习-BN(Batch Normalization)

1. 介绍 Batch Normalization是2015年一篇论文中提出的数据归一化\color{blue}{数据归一化}数据归一化方法&#xff0c;往往用在深度神经网络中激活层之前。其作用可以加快模型训练时的收敛速度&#xff0c;使得模型训练过程更加稳定&#xff0c;避免梯度爆炸或者梯度消失。并且…

postgresql 数据库导出和导入

简介 本篇文章主要介绍PostgreSQL库和表的导出和导入以及常用的数据库管理工具的使用 一、数据库的导出和导入 1. 使用pgAdmin导出和导入数据库 导出 导入 新建数据库&#xff0c;右键Restore 2. 使用Navicate导出和导入数据库 导出 选择要导出的数据库&#xff0c;点…

【小f的刷题笔记】(JS)数组 - 差分数组 LeetCode1109 LeetCode1094

【数组】 差分数组&#xff1a; &#x1f31f; 频繁对原始数组的某个区间的元素进⾏增减 ✔ 把每个数与前一个数的差值计算出来存在一个新数组里&#xff0c;区间的加减通过把新数组中区间开始下标的值加/减&#xff0c;区间结束下标对应的值减/加&#xff08;反一下&#x…

软件工程笔记

软件工程软件工程:指导计算机软件系统开发和维护的工程学科:目的:有序,可控,可管理软件工程方法学要素:方法,工具,过程软件:程序数据文档软件分类:软件生命周期补充:软件工程方法:软件生命周期中使用的一整套技术评价标准:软件开发方法:软件工程:指导计算机软件系统开发和维护的…

蓝桥杯时间显示

题目描述 小蓝要和朋友合作开发一个时间显示的网站。 在服务器上&#xff0c;朋友已经获取了当前的时间&#xff0c;用一个整数表示&#xff0c;值为从 19701970 年 11 月 11 日 00:00:0000:00:00 到当前时刻经过的毫秒数。 现在&#xff0c;小蓝要在客户端显示出这个时间。…

[附源码]Python计算机毕业设计Django社区疫情防控信息管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

【有营养的算法笔记】快速排序

&#x1f451;作者主页&#xff1a;进击的安度因 &#x1f3e0;学习社区&#xff1a;进击的安度因&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;有营养的算法笔记 文章目录一、思路二、模板讲解三、模板测试四、加练 —— 第 K 个数今天正式开启算法笔记…

生产型外协管理系统:功能解析篇

随着经济全球化与电商的全面发展&#xff0c;生产型企业的生产订单是在逐年生产的。由于自身的生产能力有限&#xff0c;很多企业不得不将些许业务进行外包生产&#xff0c;也就是所谓的外协生产。既然需要外协生产的话&#xff0c;那么对外协生产进行管理也就尤为重要了。作为…

[附源码]计算机毕业设计软考刷题小程序Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…