TCP协议的RST标志

2023-11-11

下文中的内容多数来自【参考】中的文章,这边进行一个整理和总结,后续会慢慢增加出现各个 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/

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

TCP协议的RST标志 的相关文章

  • Qt支持https请求

    Qt支持https请求需要配置openssl环境 Qt默认是不支持SSl认证的 检测Qt支持的协议 QNetworkAccessManager manager new QNetworkAccessManager this qDebug lt
  • 毕业两年后的经历总结——一个奋斗中的前端蜂鸟

    转眼间 已经毕业两年了 不得不感叹时间过的真快啊 学生时代的美好 终于只能永久停留在记忆长河中不再复返 韶光易逝 但唯一不变的 是变化 回望过去 有过困惑 有过迷茫 也有过焦虑 刚开始工作时 也遇到了很多很多的困难 这时的我对未来比较迷茫
  • 二进制数字(2ASK)载波调制解调仿真设计(matlab仿真)

    一 实验目的 了解二进制数字信号2ASK波形特点 掌握2ASK调制解调系统的构成 基带信号 载波和2ASK已调信号之间的关系 掌握利用matlab对2ASK进行仿真的分析方法 二 实验任务 利用matlab实现对2ASK信号调制与解调的仿真

随机推荐

  • 微服务架构设计的12个要点

    一 负载均衡 二 API网关 三 无状态化与独立有状态集群 四 异步处理机制 五 数据集群 分库分表 六 缓存 七 服务拆分与服务发现 八 服务编排与弹性伸缩 九 统一配置中心 十 统一日志中心 十一 熔断 限流 降级 十二 全方位的监控
  • 微信小程序获取用户信息(getUserProfile接口回收后)——通过头像昵称填写获取用户头像和昵称

    背景 最近在用uniapp写微信小程序授权登录的时候 发现项目在微信开发者工具中调试是正常的 但是在真机运行时 返回的用户数据中昵称变成了微信用户 头像变成了默认的灰底头像 接着去百度了一下发现出现这个问题的原因是getUserProfil
  • Delphi XE10实现移动端支付宝支付接口(含源码)

    Demo下载地址 https pan baidu com s 14i84u 加入支付宝开发者平台 https open alipay com 签约移动支付服务 访问https b alipay com order productSet ht
  • TensorFlow(Python

    回归任务旨在从输入训练数据中预测连续变量 而分类任务旨在将输入数据分为两个或多个类别 例如 预测某一天是否会下雨的模型是一项分类任务 因为模型的结果将分为两类 下雨或不下雨 然而 预测给定日期的降雨量的模型将是回归任务的一个示例 因为模型的
  • 服务器上传excel文件并读取数据,ASP.NET中上传并读取Excel文件数据示例

    在CSDN中 经常有人问如何打开Excel数据库文件 本文通过一个简单的例子 实现读取Excel数据文件 首先 创建一个Web应用程序项目 在Web页中添加一个DataGrid控件 一个文件控件和一个按钮控件 在代码视图中首先导入OleDb
  • Appium自动化框架从0到1之 框架结构组成

    从0到1搭框架 框架背景 框架功能 框架视图 框架背景 可能会利用一周的时间 我们来写一个Appium自动化框架的搭建 从0到1 跟着小鱼一起 完善Android 的自动化框架体系 框架模式 PO 语言 python3 7 Appium 1
  • mac 利器

    mac 利器 攒了很久的钱 环境彻底迁移到mac os 下 总结下自己使用的一些工具 从win到mac的一些工具迁移可以看老的文章 win下的mac利器替代品 这一篇主要集中在mac下的工具体会和使用心得距离上一次更新 应该很久了 上一次是
  • Flutter之修改AppBar的高度

    全局 设置AppBar的主题中的toolbarHeight override Widget build BuildContext context return GetMaterialApp title Flutter Demo debugS
  • <vulnhub>-记一次实验Load of The Root

    Load of The Root 一 信息收集 1 主机探测 探测到主机IP 192 168 88 159 2 端口扫描 扫描到端口只开放了一个22端口 尝试使用SSH连接 Easy as 1 2 3 端口碰撞 端口上的防火墙通过产生一组预
  • MyBatis-Plus-Generator代码生成器(Version 3.5.1+)使用

    这几天研究了一下MyBatis Plus Generator新版本3 5 1 的使用 做个笔记方便自己使用 maven配置
  • C/C++数字字符串与数字之间的相互转化

    目录 1 数字字符串转化为数字 1 1 C语言方法 1 2 C 方法 2 数字转化为数字字符串 2 1 C语言方法 2 2 C 方法 1 数字字符串转化为数字 1 1 C语言方法 1 sscanf sscanf读取格式化的字符串中的数据 s
  • libcublas.so.9.2: cannot open shared object file: No such file...问题原因及解决方法

    sudo ldconfig usr local cuda 9 0 lib64
  • late_initcall()与module_init()

    内核初始化的模块 当它们静态链接到内核时 的init例程被包装在一个initcall 宏中 该宏指示应该在启动顺序中运行它们的时间 请参阅包含文件 include linux init h以获取宏列表及其排序
  • Windows子系统for Linux Ubuntu安装位置转移

    默认情况下 Windows安装了Linux子系统后 以Ubuntu18 04为例 安装位置是在C Users XXXXXXXX AppData Local Packages CanonicalGroupLimited Ubuntu18 04
  • 【环境空气质量评价挑战赛】baseline——使用lightgbm+特征工程。

    赛题 一 赛事背景 随着工业化和城镇化的快速发展 环境问题日益突出 空气污染是全球最重要的环境问题之一 影响着人们的健康 生产和生活 为了改善空气质量 我国加大监测和环保力度 增加空气质量监测站点 实施蓝天保卫战 并将空气质量水平与污染治理
  • Polkit (简体中文)

    翻译状态 本文是 Polkit 的翻译 上次翻译日期 2018 10 21 如果英文版本有所更改 则您可以帮助同步翻译 来自 polkit 主页 polkit 是一个应用程序级别的工具集 通过定义和审核权限规则 实现不同优先级进程间的通讯
  • 漂亮的登录界面中的css示意图

    效果图 示意图 html div class login Box img src images user png alt h3 登录 h3 div
  • ajax不刷新页面,只重新加载js文件的方法

    ajax不刷新页面 只重新加载js文件的方法 重新加载js文件 给js定个id 重新加载 jquery的就直接使用getScript 重新加载js文件 function loadJs file var head head remove sc
  • 数学基础(一)——最小二乘法

    最小二乘法 LS 算法 是统计分析中最常用的逼近计算的一种算法 其交替计算结果使得最终结果尽可能地逼近真实结果 LS 算法是一种数学优化技术 也是一种机器学习常用算法 它通过最小化误差的平方和寻找数据的最佳函数匹配 利用最小二乘法可以简便地
  • TCP协议的RST标志

    下文中的内容多数来自 参考 中的文章 这边进行一个整理和总结 后续会慢慢增加出现各个 RST 包的测试代码 便于理解 TCP的 断开连接 标志 RST 标志 Reset 复位标志 用于非正常地关闭连接 它是 TCP 协议首部里的一个标志位