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

这篇具有很好参考价值的文章主要介绍了实验模拟TCP连接的各种异常情况(三次握手丢包,两端异常)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

实验模拟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分钟后客户端超时:

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

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

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

可以看到服务端第一次连接之后重传了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、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

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

可以发现,客户端发送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多分钟,不然服务端达不到重传次数:

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

最终的结果如下:

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

可以看到,客户端只重传了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端口,就会出现如下情景:

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

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

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

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

客户端用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查看抓包结果:

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

可以发现客户端的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 秒才可以发现一个「死亡」连接。

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

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

实验三的小结如下:

在建立 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查看指令,可看到双方当前连接仍然存在:

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

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

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

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

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

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

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

ADD: 刚对了小林Coding和小林大佬聊了聊,发现“服务端有数据传输,客户端主机崩溃”这一实验的描述并不准确,主机崩溃指的是主机断电,用虚拟机模拟不了,我得找两个台式电脑连接,在服务端通信的时候再将客户端断电,而我这里拔线和重启的情况分别属于一端超时和进程崩溃。。。。学到了,下面这个实验大家姑且当做一端进程崩溃的情况看吧

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

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

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

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

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

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

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

tcpdump抓包全过程如下:

由抓包过程可见,如果连接过程中客户端发生了断电等情况导致进程崩溃,那么客户端就会向服务端发送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文章来源地址https://www.toymoban.com/news/detail-429566.html

到了这里,关于实验模拟TCP连接的各种异常情况(三次握手丢包,两端异常)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • TCP连接管理(三次握手,四次挥手)

    源端口号 (Source Port):16 位字段,表示发送方的端口号。 目的端口号 (Destination Port):16 位字段,表示接收方的端口号。 序列号 (Sequence Number):32 位字段,表示发送方发送的字节流的序列号。用于实现数据的可靠传输和顺序传递。 确认号 (Acknowledgment Number):32 位字

    2024年02月13日
    浏览(52)
  • 网络连接管理除了TCP三次握手,还有TCP四次挥手

    网络通信 建立连接 ,TCP会进行三次握手,三次握手主要是两个主机之间建立连接,和其他没有什么关系,那么两个主机之间是如何进行三次握手的呢?他们又会使用什么操作来建立连接呢? 这里我们先了解一下TCP的报文结构: 三次握手主要是理解成客户端与服务器经过三次

    2024年02月07日
    浏览(63)
  • 从TCP/IP到TCP连接过程(详解三次握手

    概述 OSI七层模型是一个概念(标准),TCP/IP是一种更加简化和实用的模型 TCP/IP四层模型各层作用 四层模型 功能 协议 应用层 负责处理用户和应用程序之间的通信。例如数据交换的格式和规则,以便不同的应用程序能够相互通信。 HTTP:超文本传输协议,用于Web页面的传输。

    2024年04月17日
    浏览(39)
  • 【TCP 协议】连接管理之 “三次握手,四次挥手”

    哈喽,大家好~我是你们的老朋友: 保护小周ღ    本期为大家带来的是网络编程中的 TCP 传输控制协议保证数据可靠性传输的机制 之一的—— 连接管理 ,通信双方采用 “三次握手” 来建立连接,采用 “四次挥手” 会断开连接,如何进行 ”握手” 和 “挥手” 操作,本文

    2024年02月07日
    浏览(51)
  • TCP的连接和建立(三次握手和四次挥手)

    ​ 1.TCP连接的建立 ​ 连接的建立,通常称为三次握手。 ​ ​ 建立连接前服务器处在收听状态。 ​ 第一步:客户机的TCP向服务器的TCP发送连接请求报文段。同步位 = 1。这时客户进程进入同步已发送状态。 ​ 第二步:服务器TCP收到连接请求报文段后,如同意建立连接,向客

    2024年02月16日
    浏览(45)
  • 深入理解TCP三次握手:连接可靠性与安全风险

    导言 TCP简介和工作原理的回顾 TCP三次握手的目的和步骤 TCP三次握手过程中可能出现的问题和安全风险 为什么TCP三次握手是必要的? 是否可以增加或减少三次握手的次数? TCP四次挥手与三次握手的异同点         在网络通信中,TCP(Transmission Control Protocol)作为一种可靠

    2024年02月14日
    浏览(43)
  • 【网络原理】TCP连接管理机制(三次握手四次挥手)

    🥊作者:一只爱打拳的程序猿,Java领域新星创作者,CSDN、阿里云社区优质创作者。 🤼专栏收录于:计算机网络原理 在使用TCP协议进行网络交互时,TCP会进行三次握手即建立连接,TCP四次挥手即断开连接。三次握手与四次挥手后就完成了网络交互,这样的操作也叫TCP的连接

    2024年02月09日
    浏览(48)
  • 深入理解TCP三次握手与四次挥手过程以及抓包实验

    最近,我正好在做socket相关的实验,发现现在对计算机网络知识有一点点模糊,借此机会,熟悉一下TCP连接过程并利用WireShark工具进行测试。 源端口号:占16比特,写入源端口号,用来 标识发送该TCP报文段的应用进程。 目的端口号:占16比特,写入目的端口号,用来 标识接

    2024年02月08日
    浏览(44)
  • linux【网络编程】TCP协议通信模拟实现、日志函数模拟、守护进程化、TCP协议通信流程、三次握手与四次挥手

    Tcp通信模拟实现与Udp通信模拟实现的区别不大,一个是面向字节流,一个是面向数据报;udp协议下拿到的数据可以直接发送,tcp协议下需要创建链接,用文件描述符完成数据的读写 1.1.1 接口认识 1.1.1.1 listen:监听socket 1.1.1.2 accept:获取连接 通信就用accept返回的文件描述符,

    2024年02月06日
    浏览(54)
  • (学习笔记-TCP连接建立)TCP 为什么是三次握手?不是两次、四次?

    常规回答:“因为三次握手才能保证双方具有接收和发送的能力” 三次握手的 首要原因是为了防止旧的重复连接初始化造成混乱 。 假设:客户端先发送了SYN(seq=90)报文,然后客户端宕机了,而且这个SYN报文还被网络阻塞了,服务端并没有收到,接着客户端重启后,又重新向

    2024年02月17日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包