TCP协议的RST标志

news2025/1/8 4:40:40

下文中的内容多数来自【参考】中的文章,这边进行一个整理和总结,后续会慢慢增加出现各个 RST 包的测试代码,便于理解。

TCP的 “断开连接” 标志

  1. RST 标志

    Reset,复位标志,用于非正常地关闭连接。它是 TCP 协议首部里的一个标志位。发送 RST 包关闭连接时,直接丢弃缓冲区的包并发送 RST 包,而接收端收到 RST 包后,也不必发送 ACK 包来确认。

    TCP 套接字在任何状态下,只要收到 RST 包,即可进入 CLOSED 初始状态,不会有任何回应。至于是否通知上层应用,要根据应用程序是阻塞模式还是非阻塞模式:

    • 阻塞模型下,内核无法主动通知应用层出错,只有应用层主动调用 read() 或者 write() 这样的 IO 系统调用时,内核才会利用出错来通知应用层对端 RST。
    • 非阻塞模型下,select 或者 epoll 会返回 sockfd 可读,应用层对其进行读取时,read() 会报错 RST。
  2. FIN 标志

    发端完成发送任务标识。用来释放一个连接。FIN=1 表明此报文段的发送端的数据已经发送完毕,并要求释放连接。

  3. RST 和 FIN 的区别

    • 正常地关闭连接用 FIN 标志位,但 FIN 标志位不能用来处理异常情况;
    • RST 会导致连接立即终止,而在 FIN 中会得到确认。

TCP 出现 RST 包的情况

  1. 连接未监听的端口

    连接一个未监听的端口,则被连接方会发送一个 RST。也就是说主机传输层 TCP 程序接收到一个 SYN 包,而这个 SYN 包目的端口并没有 socket 监听,那么主机的协议栈会直接回复一个 RST。

  2. 向已关闭的连接发送数据

    顾名思义,主机传输层 TCP 协议程序接收到一条 TCP 数据段,而目的端口并没有 socket 监听,那么主机的协议栈会直接回复一个 RST。

  3. 向已关闭的连接发送 FIN

    主机传输层 TCP 协议程序接收到一条 FIN,而目的端口并没有 socket 监听,那么主机的协议栈会直接回复一个 RST。

  4. 向已经消逝的连接中发送数据

    和上面的举例相同。

  5. 处理半打开连接

    一方关闭了连接,另一方却没有收到结束报文(如网络故障),此时另一方还维持着原来的连接。而一方即使重启,也没有该连接的任何信息。这种状态就叫做半打开连接。而此时另一方往处于半打开状态的连接写数据,则对方回应 RST 复位报文。此时会出现 connect reset by peer 错误。详见下文测试代码。

  6. 目的主机或网络路径中的防火墙拦截

    如果目的主机或者网络路径中显式的设置了对数据包的拦截,如使用 iptables 对主机的防火墙添加了一条规则,对于目的端口是 6000 的 TCP 报文,丢弃并回复 RST。

  7. TCP 接收缓冲区 Recv-Q 中的数据未完全被应用程序读取时关闭该 socket

    接收到的数据缓存在缓冲区 Recv-Q,它们等待被上层应用取走,如果缓冲区 Recv-Q 有数据未被应用取走,而此时调用 close 函数关闭 TCP 连接,那么 TCP 协议程序发送的就不是 FIN,而是 RST。此时会出现 Connection reset by peer 错误,详见下文测试代码。

  8. 请求超时后收到回复

    主机创建 socket,设置 SO_RCVTIMEOUT 选项为100ms,向对端发送 SYN,超过100ms后才收到 ACK+SYN,那么主机的协议栈会直接回复一个 RST。

  9. SO_LINGER

    socket 设置 SO_LINGER 选项,socket 调用 close 函数时,会直接丢弃缓冲区 Send_Q 未发完的数据,并发送 RST。

  10. Linux 下启用 TIME_WAIT 快速回收

    修改 /etc/sysctl.conf 中内核参数:net.ipv4.tcp_tw_recycle = 1,当收到的 SYN 包的 timestamp 比上次的小时,就会发 RST。

  11. 移动链路

    移动网络下,国内是有5分钟后就回收信令,也就是 IM 产品,如果心跳>5分钟后服务器再给客户端发消息,就会收到 RST。也要查移动网络下 IM 保持<5min 心跳。

  12. GFW

    防火长城(Great Firewall of China,简称GFW)是中国政府在互联网空间中发起的一项大规模干预措施,旨在审查并控制中国地区的互联网使用,以遏制虚假信息、不良内容和外部信息流入中国境内。防火长城被普遍认为是政府和监管机构利用技术工具监控国家居民上网行为的全球最大系统,而它的技术基础上,有多种关键的审查与监管工具,如域名解析服务(DNS)、网络流量检测和内容过滤系统(CFMS)等。

  13. 负载等设备

    负载设备需要维护连接转发策略,长时间无流量,连接也会被清除,而且很多都不告诉两层机器,新的包过来时才通告 RST。

    Apple push 服务也有这个问题,而且是不可预期的偶发性连接被 RST;RST 前第一个消息 write 是成功的,而第二条写才会告诉你连接被重置,

    曾经被它折腾没辙,因此打开每2秒一次 tcp keepalive,固定5分钟 TCP 连接回收,而且发现连接出错时,重发之前10s内消息。

  14. 超过超时重传次数

  15. seq 不正确

  16. keepalive 超时

    公网服务 tcp keepalive 最好别打开;移动网络下会增加网络负担,切容易掉线;非移动网络核心 ISP 设备也不一定都支持 keepalive,曾经也发现过广州那边有个核心节点就不支持。

  17. 数据错误,不是按照既定序列号发送数据

测试代码

  1. 上述第6种情况【TCP 接收缓冲区 Recv-Q 中的数据未完全被应用程序读取时关闭该 socket】

    客户端测试代码:

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <stdlib.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <string.h>
    #include <errno.h>
    #include <fcntl.h>
    
    
    int main(void)
    {
    	int len;
        int sockFd;
        char sendBuf[256];
    	struct sockaddr_in addr;
    	
    	bzero(&addr, sizeof(addr));
    	addr.sin_family = AF_INET;
    	inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
    	addr.sin_port = htons(8888);
    
        if ((sockFd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        {
    		perror("socket error");
            return -1;
        }
    
        if (connect(sockFd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
        {
    		perror("connect error");
            close(sockFd);
            return -1;
        }
    
    	memset(sendBuf, 0xFF, sizeof(sendBuf));
    	send(sockFd, sendBuf, sizeof(sendBuf), 0);
    
    	len = recv(sockFd, sendBuf, sizeof(sendBuf), 0);
    	if (len >= 0)
    	{
    		printf("len: %d\n", len);
    	}
    	else
    	{
    		printf("[line:%d] errno: %d, strerror(errno): %s\n", __LINE__, errno, strerror(errno));		
    	}
        
        close(sockFd);
        
        return 0;
    }
    

    服务端测试代码:

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <stdlib.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <string.h>
    #include <errno.h>
    #include <fcntl.h>
    
    
    int main(void)
    {
    	int readLen;
    	int sockFd;
    	int clientFd;
    	char recvBuf[128] = {0};
    	struct sockaddr_in saddr;
    
    	if ((sockFd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        {
    		perror("socket error");
            return -1;
        }
    
    	bzero((void*)&saddr, sizeof(saddr));
    	saddr.sin_family = AF_INET;
    	saddr.sin_addr.s_addr = htonl(INADDR_ANY);
    	saddr.sin_port = htons(8888);
    	if (bind(sockFd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
        {
    		perror("bind error");
            close(sockFd);
            return -1;
        }
        
        if (listen(sockFd, 5) < 0)
        {
    		perror("listen error");
            close(sockFd);
            return -1;
        }
    	
    	printf("accept waiting, sockFd: %d\n", sockFd);
    	if ((clientFd = accept(sockFd, NULL, NULL)) == -1)
    	{
    		perror("accept error");
    		close(sockFd);
    		return -1;
    	}
    	
    	while (1)
    	{
    		memset(recvBuf, 0, sizeof(recvBuf));
    
    		readLen = recv(clientFd, recvBuf, sizeof(recvBuf), 0);
    		if (readLen > 0)
    		{
    			printf("readLen: %d\n", readLen);
    		}
    		else if (readLen == 0)
    		{
    			printf("client fd is closed!\n");
    			close(clientFd);
    			break;
    		}
    		else 
    		{
    			printf("[line:%d] errno: %d, strerror(errno): %s\n", __LINE__, errno, strerror(errno));
    			close(clientFd);
    			break;
    		}
    
    		close(clientFd);
    		break;
    	}
    	
    	close(sockFd);
    	
    	return 0;
    }
    

    服务端输出:
    在这里插入图片描述

    客户端输出:
    在这里插入图片描述

    wireshark 抓包结果:
    在这里插入图片描述

    该举例中,客户端发送256字节的数据到服务端,服务端只接收了128字节的数据就关闭了套接字,此时服务端的 TCP 接收缓冲区中还剩128字节未读取,所以服务端发送 RST 到客户端。

  2. 上述第5种情况【处理半打开连接】

    客户端测试代码:

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <stdlib.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <string.h>
    #include <errno.h>
    #include <fcntl.h>
    
    
    int main(void)
    {
    	int len;
        int sockFd;
        char sendBuf[256];
    	struct sockaddr_in addr;
    	
    	bzero(&addr, sizeof(addr));
    	addr.sin_family = AF_INET;
    	inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
    	addr.sin_port = htons(8888);
    
        if ((sockFd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        {
    		perror("socket error");
            return -1;
        }
    
        if (connect(sockFd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
        {
    		perror("connect error");
            close(sockFd);
            return -1;
        }
    
    	memset(sendBuf, 0xFF, sizeof(sendBuf));
    	send(sockFd, sendBuf, sizeof(sendBuf), 0);
    
    	sleep(1);
    
    	send(sockFd, sendBuf, sizeof(sendBuf), 0);
        
        close(sockFd);
        
        return 0;
    }
    

    服务端测试代码:

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <stdlib.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <string.h>
    #include <errno.h>
    #include <fcntl.h>
    
    
    int main(void)
    {
    	int readLen;
    	int sockFd;
    	int clientFd;
    	char recvBuf[256] = {0};
    	struct sockaddr_in saddr;
    
    	if ((sockFd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        {
    		perror("socket error");
            return -1;
        }
    
    	bzero((void*)&saddr, sizeof(saddr));
    	saddr.sin_family = AF_INET;
    	saddr.sin_addr.s_addr = htonl(INADDR_ANY);
    	saddr.sin_port = htons(8888);
    	if (bind(sockFd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
        {
    		perror("bind error");
            close(sockFd);
            return -1;
        }
        
        if (listen(sockFd, 5) < 0)
        {
    		perror("listen error");
            close(sockFd);
            return -1;
        }
    	
    	printf("accept waiting, sockFd: %d\n", sockFd);
    	if ((clientFd = accept(sockFd, NULL, NULL)) == -1)
    	{
    		perror("accept error");
    		close(sockFd);
    		return -1;
    	}
    	
    	while (1)
    	{
    		memset(recvBuf, 0, sizeof(recvBuf));
    
    		readLen = recv(clientFd, recvBuf, sizeof(recvBuf), 0);
    		if (readLen > 0)
    		{
    			printf("readLen: %d\n", readLen);
    		}
    		else if (readLen == 0)
    		{
    			printf("client fd is closed!\n");
    			close(clientFd);
    			break;
    		}
    		else 
    		{
    			printf("[line:%d] errno: %d, strerror(errno): %s\n", __LINE__, errno, strerror(errno));
    			close(clientFd);
    			break;
    		}
    
    		close(clientFd);
    		break;
    	}
    	
    	close(sockFd);
    
    	return 0;
    }
    

    wireshark 抓包结果: 在这里插入图片描述

    该举例中,客户端发送数据到服务端,服务端将数据接收后就关闭了套接字,随后,客户端又发送数据到服务端,因为此时服务端已将套接字关闭,所以服务端会发送 RST 到客户端。

参考

[1] https://zhuanlan.zhihu.com/p/361714600
[2] https://baijiahao.baidu.com/s?id=1632327385547303797&wfr=spider&for=pc
[3] https://www.cnblogs.com/JohnABC/p/6323046.html
[4] https://www.pianshen.com/article/8750375150/

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

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

相关文章

企业产品操作手册,真的很有必要吗?

产品操作手册是一种对于产品使用者而言非常重要的工具。它是一份向用户介绍产品使用方法和功能的文档&#xff0c;可以提供关于产品的详细信息&#xff0c;解答用户的疑问&#xff0c;并帮助用户快速上手使用产品。 产品操作手册的必要性&#xff1a; 一、提高用户使用体验 …

Matlab论文插图绘制模板第95期—双向柱状图

在之前的文章中&#xff0c;分享了很多Matlab柱状图的绘制模板&#xff1a; 进一步&#xff0c;再来看一种特殊的柱状图&#xff1a;双向柱状图。 先来看一下成品效果&#xff1a; 特别提示&#xff1a;本期内容『数据代码』已上传资源群中&#xff0c;加群的朋友请自行下载。…

抖音seo源码如何开发部署?

前言&#xff1a;抖音seo源码&#xff0c;抖音矩阵系统源码搭建&#xff0c;抖音矩阵同步分发。抖音seo源码部署是需要对接到这些正规接口再来做开发的&#xff0c;目前账号矩阵程序开发的功能&#xff0c;围绕一键管理多个账号&#xff0c;做到定时投放&#xff0c;关键词自动…

腾讯云服务器常用端口号大全以及端口开启方法

腾讯云服务器常用端口号如80、21、22、8080等端口&#xff0c;出于安全考虑一些常用端口默认是关闭的&#xff0c;腾讯云服务器端口如何打开呢&#xff1f;云服务器CVM在安全组中开启端口&#xff0c;轻量应用服务器在防火墙中可以打开端口&#xff0c;腾讯云百科来详细说下腾讯…

解析云盘存储的优缺点:安全靠谱还是存在风险?

云盘是一种基于云计算技术的在线存储服务&#xff0c;用户可以通过互联网将文件上传到云端&#xff0c;并可以随时随地通过网络访问这些文件。 相较于传统的本地存储&#xff0c;云盘具有以下优势&#xff1a; 1.数据安全性更高&#xff1a;云盘使用专业的云计算技术和安全措施…

vue 截取字符串的方法

vue中的字符串方法&#xff0c;我目前使用最多的是下面两种方法&#xff0c;因为 vue的字符串方法支持断言操作。 1、 vue中截取字符串的方法如下&#xff1a; 2、 vue中截取字符串的方法&#xff0c;这个方法也是需要依赖于 vue库提供的支持。 3、 vue中截取字符串的方法&…

腾讯云服务器CVM和轻量应用服务器区别

关于腾讯云轻量应用服务器和云服务器CVM的区别&#xff0c;之前腾讯云百科写过一篇文章来对比&#xff0c;如今2023阿腾云又更新了一篇新的区别对比文章&#xff0c;比之前的要更加详细&#xff0c;包括轻量服务器的使用限制、CPU型号、公网限制月流量、内网连通性、硬盘存储等…

f1c200s---编译uboot

目录 前言安装必要工具安装交叉编译链工具 下载Uboot切换分支查看配置文件 配置Uboot修改Makefile配置文件 编译uboot出现/bin/sh: 1: python: not found错误出现multiple definition of yylloc错误出现FATAL ERROR: Unable to parse input tree错误出现/usr/bin/env: python2:…

微服务架构综合实战 一文让你了解什么是微服务 使用PHP 搭建微服务框架 最全微服务架构讲解以及演示

本文将带你从基础的微服务架构设计、网络协议、注册中心、配置中心、网关层面 渐进式讲解其微服务。 一、微服务架构设计方案 架构演进 在将微服务之前 我们看看目前的架构 单体架构 按照模块划分&#xff0c;公用一个数据库 垂直拆分架构 按业务功能划分单独的子系统&…

为什么pnpm比npm、yarn使用更好

performant npm &#xff0c;意味高性能的 npm。pnpm由 npm/yarn 衍生而来&#xff0c;解决了 npm/yarn 内部潜在的bug&#xff0c;极大的优化了性能&#xff0c;扩展了使用场景。被誉为"最先进的包管理工具"。 我们按照包管理工具的发展历史开始讲起&#xff1a; n…

切片工具tippecanoe的全网最详细的解释

1.下载和安装 tippecanoe工具是mapbox官方提供的一个服务端切片工具,因此它是运行在服务器上的,它比较友好的支持mac和linux机器。对于windows来讲,就比较麻烦了。 首先对于mac系统,你只需配置好自己的homebrew,保证homebrew能够正常下载东西。 然后只需要一个命令: …

Jmeter 压测工具进行压力测试

需求&#xff1a;接口需要进行压力测试&#xff0c;有减库存的场景&#xff0c;要求并发不能超库存&#xff0c;接口鉴权类似token方式校验。 一、jemter 下载安装Java Downloads | Oracle &#xff0c;下载安装可以自行翻帖子&#xff0c;很多教程&#xff0c;本次实验用的是…

用Python体素化3D网格和点云

推荐&#xff1a;用 NSDT设计器 快速搭建可编程3D场景。 3D 数据的持续学习正在成为机器学习和理解我们周围世界的越来越重要的部分。 随着新的 3D 数据提取硬件&#xff08;如深度相机和 LiDAR&#xff09;在闭路电视、相机和智能手机中变得司空见惯&#xff0c;越来越多的人正…

法线贴图的类型

1、切线空间法线贴图 这是当今最常见的法线贴图&#xff0c;也是我们在本系列中讨论的法线贴图。它根据模型顶点的法线方向修改模型的法线方向&#xff08;因此我们必须控制lowpoly的顶点法线&#xff09;。 请记住&#xff0c;切线法线贴图是使用顶点指示的方向进行计算的。这…

【WPF】数据绑定,资源字典

数据绑定 将数据与视图分开,创建MainViewModel .cs 作为数据源的处理 MainViewModel using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Threading…

华为OD机试真题 Java 实现【基站维修工程师】【2023Q1 200分】,附详细解题思路

一、题目描述 小王是一名基站维护工程师,负责某区域的基站维护。 某地方有n个基站(1<n<10),已知各基站之间的距离s(0<s<500),并且基站x到基站y的距离,与基站y到基站x的距离并不一定会相同。 小王从基站1出发,途径每个基站1次,然后返回基站1,需要请你…

Swift 中的 Actors 使用以及如何防止数据竞争

文章目录 前言Actors 的基本原理Actor 是引用类型&#xff0c;但与类相比仍然有所不同 为什么会出现数据竞争如何防止数据竞争使用 async/await 访问数据防止不必要的暂停非隔离(nonisolated)访问为什么在使用 Actors 时仍会出现数据竞争&#xff1f;总结 前言 Actors 是 Swif…

FFMPEG常用命令 音视频合并

目录 一、音频合并 1.获取音频时长 2.合并两段音频 3.合并音频插入空白 二、视频加背景图 三、音视频合成 1.保留视频声音 2.不保留视频声音 四、合并视频 本文将用几个实例&#xff0c;介绍ffmpeg命令的综合使用&#xff0c;主要涉及音频处理、视频处理和音视频合成。…

如何搭建自己的CentOS系统

CentOS是一个完全免费的操作系统&#xff0c;这对于开发人员来说非常有吸引力。他们可以使用CentOS来开发和测试应用程序&#xff0c;而不需要支付任何费用。那么作为程序员如果搭建自己的CentOS服务器呢&#xff1f; 搭建自己的CentOS系统需要以下步骤&#xff1a; 1、下载Ce…

借助KafkaTool在海量Kafka数据快速精准定位/查询目标数据

如何在海量的Kafka数据定位/查询精准到秒级的数据&#xff1f; 解决方案&#xff1a;Kafka命令 KafkaTool 1. 使用Kafka命令定位Offset sh ./kafka-run-class.sh kafka.tools.GetOffsetShell --broker-list $ip:$port -topic ${topic.name} -time $timestamp参数说明&#…