TCP协议的RST标志
下文中的内容多数来自【参考】中的文章,这边进行一个整理和总结,后续会慢慢增加出现各个 RST 包的测试代码,便于理解。
TCP的 “断开连接” 标志
-
RST 标志
Reset,复位标志,用于非正常地关闭连接。它是 TCP 协议首部里的一个标志位。发送 RST 包关闭连接时,直接丢弃缓冲区的包并发送 RST 包,而接收端收到 RST 包后,也不必发送 ACK 包来确认。
TCP 套接字在任何状态下,只要收到 RST 包,即可进入 CLOSED 初始状态,不会有任何回应。至于是否通知上层应用,要根据应用程序是阻塞模式还是非阻塞模式:
- 阻塞模型下,内核无法主动通知应用层出错,只有应用层主动调用 read() 或者 write() 这样的 IO 系统调用时,内核才会利用出错来通知应用层对端 RST。
- 非阻塞模型下,select 或者 epoll 会返回 sockfd 可读,应用层对其进行读取时,read() 会报错 RST。
-
FIN 标志
发端完成发送任务标识。用来释放一个连接。FIN=1 表明此报文段的发送端的数据已经发送完毕,并要求释放连接。
-
RST 和 FIN 的区别
- 正常地关闭连接用 FIN 标志位,但 FIN 标志位不能用来处理异常情况;
- RST 会导致连接立即终止,而在 FIN 中会得到确认。
TCP 出现 RST 包的情况
-
连接未监听的端口
连接一个未监听的端口,则被连接方会发送一个 RST。也就是说主机传输层 TCP 程序接收到一个 SYN 包,而这个 SYN 包目的端口并没有 socket 监听,那么主机的协议栈会直接回复一个 RST。
-
向已关闭的连接发送数据
顾名思义,主机传输层 TCP 协议程序接收到一条 TCP 数据段,而目的端口并没有 socket 监听,那么主机的协议栈会直接回复一个 RST。
-
向已关闭的连接发送 FIN
主机传输层 TCP 协议程序接收到一条 FIN,而目的端口并没有 socket 监听,那么主机的协议栈会直接回复一个 RST。
-
向已经消逝的连接中发送数据
和上面的举例相同。
-
处理半打开连接
一方关闭了连接,另一方却没有收到结束报文(如网络故障),此时另一方还维持着原来的连接。而一方即使重启,也没有该连接的任何信息。这种状态就叫做半打开连接。而此时另一方往处于半打开状态的连接写数据,则对方回应 RST 复位报文。此时会出现 connect reset by peer 错误。详见下文测试代码。
-
目的主机或网络路径中的防火墙拦截
如果目的主机或者网络路径中显式的设置了对数据包的拦截,如使用 iptables 对主机的防火墙添加了一条规则,对于目的端口是 6000 的 TCP 报文,丢弃并回复 RST。
-
TCP 接收缓冲区 Recv-Q 中的数据未完全被应用程序读取时关闭该 socket
接收到的数据缓存在缓冲区 Recv-Q,它们等待被上层应用取走,如果缓冲区 Recv-Q 有数据未被应用取走,而此时调用 close 函数关闭 TCP 连接,那么 TCP 协议程序发送的就不是 FIN,而是 RST。此时会出现 Connection reset by peer 错误,详见下文测试代码。
-
请求超时后收到回复
主机创建 socket,设置 SO_RCVTIMEOUT 选项为100ms,向对端发送 SYN,超过100ms后才收到 ACK+SYN,那么主机的协议栈会直接回复一个 RST。
-
SO_LINGER
socket 设置 SO_LINGER 选项,socket 调用 close 函数时,会直接丢弃缓冲区 Send_Q 未发完的数据,并发送 RST。
-
Linux 下启用 TIME_WAIT 快速回收
修改 /etc/sysctl.conf 中内核参数:net.ipv4.tcp_tw_recycle = 1,当收到的 SYN 包的 timestamp 比上次的小时,就会发 RST。
-
移动链路
移动网络下,国内是有5分钟后就回收信令,也就是 IM 产品,如果心跳>5分钟后服务器再给客户端发消息,就会收到 RST。也要查移动网络下 IM 保持<5min 心跳。
-
GFW
防火长城(Great Firewall of China,简称GFW)是中国政府在互联网空间中发起的一项大规模干预措施,旨在审查并控制中国地区的互联网使用,以遏制虚假信息、不良内容和外部信息流入中国境内。防火长城被普遍认为是政府和监管机构利用技术工具监控国家居民上网行为的全球最大系统,而它的技术基础上,有多种关键的审查与监管工具,如域名解析服务(DNS)、网络流量检测和内容过滤系统(CFMS)等。
-
负载等设备
负载设备需要维护连接转发策略,长时间无流量,连接也会被清除,而且很多都不告诉两层机器,新的包过来时才通告 RST。
Apple push 服务也有这个问题,而且是不可预期的偶发性连接被 RST;RST 前第一个消息 write 是成功的,而第二条写才会告诉你连接被重置,
曾经被它折腾没辙,因此打开每2秒一次 tcp keepalive,固定5分钟 TCP 连接回收,而且发现连接出错时,重发之前10s内消息。
-
超过超时重传次数
-
seq 不正确
-
keepalive 超时
公网服务 tcp keepalive 最好别打开;移动网络下会增加网络负担,切容易掉线;非移动网络核心 ISP 设备也不一定都支持 keepalive,曾经也发现过广州那边有个核心节点就不支持。
-
数据错误,不是按照既定序列号发送数据
测试代码
-
上述第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 到客户端。
-
上述第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/
相关文章:
TCP协议的RST标志
下文中的内容多数来自【参考】中的文章,这边进行一个整理和总结,后续会慢慢增加出现各个 RST 包的测试代码,便于理解。 TCP的 “断开连接” 标志 RST 标志 Reset,复位标志,用于非正常地关闭连接。它是 TCP 协议首部里…...
【软件质量与软件测试 白盒测试与黑盒测试】
第十章 黑盒测试 10.1 等价类划分: 10.1.1 划分等价类 等价类是指所有数据中的一组,它们具有相同的测试结果或相同的响应。等价类划分是将输入数据分为多个等价类的过程。 10.1.2 划分等价类的方法 划分等价类方法主要包括以下几种: 特…...
JavaScript教程(高级)
面向对象编程介绍 两大编程思想 (1)、 面向过程编程: (缩写 POP)( Process-oriented programming)面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现&am…...
C++进阶 —— 范围for(C++11新特性)
目录 一,范围for介绍 二,范围for注意事项 一,范围for介绍 范围for(range-based for loop)是C11新引入的特性,可遍历各种序列结构的容器(如数组、vector、list等);每次循…...
ELK +Filebeat日志分析系统
一、 ELK日志分析系统概述 1、ELK简介 ELK是三个开源软件的缩写,分别表示:Elasticsearch , Logstash, Kibana , 它们都是开源软件。新增了一个FileBeat,它是一个轻量级的日志收集处理工具(Agent),Filebeat占用资源少,…...
万字解析PELT算法!
Linux是一个通用操作系统的内核,她的目标是星辰大海,上到网络服务器,下至嵌入式设备都能运行良好。做一款好的linux进程调度器是一项非常具有挑战性的任务,因为设计约束太多了: 它必须是公平的快速响应系统的throughp…...
腾讯云服务器端口怎么全开?教程来了
腾讯云服务器端口怎么全开?云服务器CVM在安全组中设置开通,轻量应用服务器在防火墙中设置,腾讯云百科来详细说下腾讯云服务器端口全开放教程: 目录 腾讯云服务器端口全部开通教程 云服务器CVM端口全开放教程 轻量应用服务器开…...
深入理解Java虚拟机:JVM高级特性与最佳实践-总结-13
深入理解Java虚拟机:JVM高级特性与最佳实践-总结-13 Java内存模型与线程Java内存模型原子性、可见性与有序性先行发生原则 Java内存模型与线程 Java内存模型 原子性、可见性与有序性 Java内存模型是围绕着在并发过程中如何处理原子性、可见性和有序性这三个特征来…...
租售keysight E8257D 50G模拟信号发生器 销售/回收
是德(Keysight) E8257D 模拟信号发生器 Keysight E8257D (Agilent) PSG 模拟信号发生器提供业界领先的输出功率、电平精度和高达 67 GHz 的相位噪声性能(工作频率可达 70 GHz)。Agilent PSG 模拟信号发生器的高输出功率和卓越的电…...
【C++】什么是函数模板/类模板?
文章目录 一、函数模板1.什么是函数模板?2.函数模板格式3.函数模板原理4.函数模板实例化(1)隐式实例化(2)显示实例化 二.类模板1.类模板定义格式2.类模板的实例化 总结 一、函数模板 1.什么是函数模板? 函…...
为什么是ChatGPT引发了AI浪潮?
目录 BERT和GPT简介 BERT和GPT核心差异 GPT的优势 GPT的劣势 总结 随着近期ChatGPT的火热,引发各行各业都开始讨论AI,以及AI可以如何应用到各个细分场景。为了不被时代“抛弃”,我也投入了相当的精力用于研究和探索。但在试验的过程中&…...
批处理文件(.bat)启动redis及任何软件(同理)
批处理文件 每次从文件根目录用配置文件格式来启动redis太麻烦了 可以在桌面上使用批处理文件(.bat)启动Redis,请按照以下步骤进行操作: 打开文本编辑器,如记事本。 在编辑器中输入以下内容: 将文件保存…...
深度学习求解稀疏最优控制问题的并行化算法
稀疏最优控制问题 问题改编自论文An FE-Inexact Heterogeneous ADMM for Elliptic Optimal Control Problems with L1-Control Cost { min y ( μ ) , u ( μ )...
牛客网项目—开发社区首页
视频连接:开发社区首页_哔哩哔哩_bilibili 代码地址:Community: msf begin 仿牛客论坛项目 (gitee.com) 本文是对仿牛客论坛项目的学习,学习本文之前需要了解Java开发的常用框架,例如SpringBoot、Mybatis等等。如果你也在学习牛…...
uniapp水文【uniapp】
文章目录 1、前言2、历史3、发展4、功能5、优缺点6、总结7、附录7.1、高频使用7.2、使用注意 1、前言 Uniapp是一种跨平台的移动应用开发框架,它允许开发者使用一套代码库,同时生成iOS、Android等多个平台的应用程序。这种技术方案可以大大降低开发成本…...
Java函数式接口
3 函数式接口 3.1 函数式接口概述 函数式接口:有且仅有一个抽象方法的接口 Java中的函数式编程体现就是Lambda表达式,所以函数式接口就是可以适用于Lambda使用的接口只有确保接口中有且仅有一个抽象方法, Java中的Lambda才能顺利地进行推导…...
安装libevent库
安装libevent库 yum install libevent libevent-devel 自动安装Memcached yum install memcached 源码安装 下载1.6.19版本 wget https://www.memcached.org/files/memcached-1.6.19.tar.gz (若证书过期yum install -y ca-certificates) 解压源码 tar -zxvf…...
vue 截取字符串的方法
vue中的字符串方法,我目前使用最多的是下面两种方法,因为 vue的字符串方法支持断言操作。 1、 vue中截取字符串的方法如下: 2、 vue中截取字符串的方法,这个方法也是需要依赖于 vue库提供的支持。 3、 vue中截取字符串的方法&…...
可数集和不可数集
有限集和无限集 后继集 设 S S S是任一集合,称 S S ∪ { S } S^ S\cup \left\{ S\right\} SS∪{S}为 S S S的后继集 自然数集 自然数集 N \mathbb{N} N的归纳定义是: (1) ∅ ∈ N \empty \in \mathbb{N} ∅∈N (…...
<Linux>《Linux 之 ps 命令详解大全(含实用命令)》
《Linux 之 ps 命令详解大全(含实用命令)》 1 常用命令1.1 显示所有当前进程1.2 显示所有当前进程1.3 显示所有当前进程1.4 根据用户过滤进程1.5 根据 CPU 使用来升序排序1.6 根据用户过滤进程1.7 查询全10个使用cpu和内存最高的应用1.8 通过进程名和PID…...
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...
基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解
JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用,结合SQLite数据库实现联系人管理功能,并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能,同时可以最小化到系统…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...
Redis:现代应用开发的高效内存数据存储利器
一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发,其初衷是为了满足他自己的一个项目需求,即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源,Redis凭借其简单易用、…...
