百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

应用层如何强制发送 RST 报文进行断开连接

nanshan 2025-07-24 20:22 8 浏览 0 评论

在 TCP 协议中,默认情况下,当我们调用 close() 函数关闭套接口时,TCP 走四次挥手进行断开链路,但是要是若缓冲区还有数据未发送到对端时,系统将尝试把这些数据发送给对端。四次挥手的过程导致我们在 TIME_WAIT 状态下无法复用端口。有些情况下我们不需要 TIME_WAIT, 而是想快速断开连接,从而避免 socket 的堆积。

这个时候我们可以使用 SO_LINGER 套接字选项

struct linger {
int l_onoff;
int l_linger;
}

1) 若 l_onoff 为0, 表示关闭该选项。l_linger 值被忽略,也即是走TCP 的默认设置。

2)若 l_onoff 为非 0 且 l_linger 为 0,那么当 close 某个连接时 TCP 将终止该连接。也即是TCP将丢弃保留在套接字发送缓冲区中的任何数据,并发送RST报文给对端,不再走四次挥手,从而避免了 TCP 的 TIME_WAIT 状态。但是依然存在以下可能性:在 2 MSL 秒内创建该连接的另一个化身,导致来自刚被终止的连接上的旧的重复分节被不正确的传递到新的化身上。

3)若 l_onoff 为非 0 值且 l_linger 也为非 0 值,那么当套接字关闭时内核将拖延一段时间关闭,也即是若在套接字的发送缓冲区中还有残留数据,那么进程将投入睡眠,直到数据发送完且均被对端确认或者滞留时间到。若套接字被设置成非阻塞型,那么它将不等待 close 完成,即是滞留时间不为 0 也是如此。当使用 SO_LINGER 选项时,应用程序检查 close 的返回值很重要,因为若在数据发送完并被确认前延滞时间到的话,close 将返回 EWOULDBLOCK 错误,且套接字发送缓冲区中的任何残留数据都被丢弃。

通过下面实现进行验证。

首先 server 端使用 nc 进行监听一个TCP 指定端口。

客户端使用如下代码

#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>


int main(int argc, char *argv[])
{
struct sockaddr_in peer;
struct linger linger;
int ret;
int sock = socket(AF_INET, SOCK_STREAM, 0);

memset(&peer, 0, sizeof(peer));

peer.sin_family = AF_INET;
inet_pton(AF_INET, argv[1], &peer.sin_addr);
peer.sin_port = htons(atoi(argv[2])); 

memset(&linger, 0, sizeof(linger));
linger.l_onoff = 1;
linger.l_linger = 0;

ret = setsockopt(sock, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger));
if (ret) {
printf("Fail to set linger\n");
exit(1);
}

ret = connect(sock, (const struct sockaddr *)&peer, sizeof(peer));

if (ret) {
printf("Fail to connect.\n", strerror(errno));
exit(1);
}

printf("Connect successfully\n");



close(sock);

printf("Done\n");

return 0;
}

通过抓包分析来看,调用 close 后,客户端直接发送了 RST 报文端开了连接。

19:22:13.101476 IP 17.15.220.199 > localhost.localdomain : Flags [S], seq 12771346 ..
19:22:13.101509 IP localhost.localdomain > 17.15.220.199 : Flags [S .], seq 1277234 ..
19:22:13.101732 IP 17.15.220.199 > localhost.localdomain : Flags [.], ack ...
19:22:13.101912 IP 17.15.220.199 > localhost.localdomain : Flags [R .] ...

在 tcp_close 中查看具体实现

/*
内核并并不关心有多少数据未被用户进程读取,内核关心的是有没有数据未被读取,
若有数据未被读取而丢弃(data_was_unread>0),则给对方发送rst报文
若没有数据未被用户进程读取,也即是全部数据都被用户进程读取了(data_was_unread==0),则相对对端发送fin报文
*/
if (data_was_unread) {
/* Unread data was tossed, zap the connection. */
NET_INC_STATS_USER(LINUX_MIB_TCPABORTONCLOSE);

/*发送rst报文前设置状态为TCP_CLOSE,这时没有TIME_WAIT状态,没有FIN_WAIT_1状态,说明此时时不正常关闭的。
所以可得,在编写程序时,在关闭连接前,一定要保证所有接收到的数据被读取,否则连接会不正常关闭*/
tcp_set_state(sk, TCP_CLOSE); 
//发送rst报文,之所以不是fin报文,是因为关闭时还有未读的数据属于异常情况,fin表示一切正常情况
tcp_send_active_reset(sk, GFP_KERNEL);
} else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) {
/* Check zero linger _after_ checking for unread data. */
/*调用tcp_disconnect断开、删除并释放已建立连接但未被accept的传输控制块,同时
删除并释放已接收在接收队列(包括失序队列)上的段以及发送队列上的段*/
sk->sk_prot->disconnect(sk, 0);// tcp_disconnect
NET_INC_STATS_USER(LINUX_MIB_TCPABORTONDATA);
} else if (tcp_close_state(sk)) { //若未读字节数为0,则调用tcp_close_state根据sk当前状态来设置sk下一状态,比如当前状态为TCP_ESTABLISHED,则下一状态为TCP_FIN_WAIT1,该方法的返回确定是否发送fin报文给对方
/*

从上面的代码段可以看到,当有数据还未读取时,说明是异常关闭,直接发送 RST 报文给对端。若接收缓冲区中数据都已经读取完了,判断 SOCK_LINGER 套接字选项,若 l_linger 为 0,则调用 tcp_disconnect 给对端发送 RST 报文,同时释放接收和发送队列上的数据。

相关推荐

轻量级分析利器再升级:解读 DuckDB 1.3.0 新特性

DuckDB团队近日正式发布了最新版本——DuckDB1.3.0,代号“Ossivalis”。此次版本以金眼鸭的远古祖先BucephalaOssivalis命名,象征项目在演化和成长过...

C++跨平台编译的终极奥义:用Docker把环境差异按在地上摩擦

"代码在本地跑得飞起,一上服务器就coredump?"——每个C++程序员都经历过的《编译器的复仇》事件!大家好,我是Henry,废话少说,今天来简单谈一下跨平台编译的那些事儿,...

全网最全-Version Script以及__asm__((&quot;.symver xxx&quot;))使用总结

首先提醒一点,一切的前提建立在你的名字必须要mangling,不然无论你写的versionscript还是__asm__都不会起任何效果VersionScript简单用法:这是一个典型例子,这个例...

Ubuntu 25.04 Beta发布:Linux 6.14内核

IT之家3月28日消息,Canonical昨日(3月27日)放出了Beta版Ubuntu25.04系统镜像,代号“PluckyPuffin”,稳定版预估将于2025年...

不同平台CRT的区别?什么是UCRT?如何看libc源代码?

若文章对您有帮助,欢迎关注程序员小迷。助您在编程路上越走越好!CRT运行时库C标准规定例如输入输出函数、字符串函数、内存操作等接口,一般采用C运行时库实现。微软的CRT微软有两套CRT,早期的MS...

信创力量,中兴绽放——中兴新支点桌面操作系统安装与使用全攻略

原文链接:「链接」Hello,大家好啊,今天给大家带来一篇中兴新支点桌面操作系统安装使用的文章,欢迎大家分享点赞,点个在看和关注吧!中兴新支点桌面操作系统是一款基于Linux内核、面向政企和信创环...

Linux下安装常用软件都有哪些?做了一个汇总列表,你看还缺啥?

1.安装列表MySQL5.7.11Java1.8ApacheMaven3.6+tomcat8.5gitRedisNginxpythondocker2.安装mysql1.拷贝mysql安装文件到...

一篇文章解决Linux系统安全问题排查,另配实操环境

实操地址:https://www.skillup.host/1/linux/safe/command.md#Linux安全检查排查指南##1.系统账户安全检查###1.1检查异常账户``...

程序员必备的学习笔记《TCP/IP详解(一)》

为什么会有TCP/IP协议在世界上各地,各种各样的电脑运行着各自不同的操作系统为大家服务,这些电脑在表达同一种信息的时候所使用的方法是千差万别。就好像圣经中上帝打乱了各地人的口音,让他们无法合作一样...

《Linux常用命令》(linux的常用命令总结)

一、文件与目录操作1.目录导航pwd:显示当前工作目录路径示例:pwd关键词:当前路径、工作目录cd:切换目录示例:cd/home/user#切换到绝对路径cd..#...

Kubernetes 教程之跟着官方文档从零搭建 K8S

前言本文将带领读者一起,参照者Kubernetes官方文档,对其安装部署进行讲解.Kubernetes更新迭代很快,书上、网上等教程可能并不能适用于新版本,但官方文档能.阅读这篇文章你...

电脑网卡坏了怎么修复(电脑网卡坏了怎么修复win7系统)

当电脑网卡出现故障时,无论是有线网络还是无线网络,都可能无法正常连接。下面从软件、硬件等方面,分步骤为你介绍排查与修复的解决方案。一、初步排查:锁定问题源头检查网络环境将手机、平板等其他设备连接至同一...

如何查询电脑/手机的物理地址(如何找手机的物理地址)

一、要查询电脑的物理地址(也称为MAC地址),可以按照以下步骤进行操作:1.打开命令提示符(Windows)或终端(Mac):-在Windows上,点击“开始”按钮,搜索“命令提示符”,然后点击打...

IPv4 无网络访问权限全流程解决方案

当设备出现IPv4无网络访问权限问题时,多由网络配置错误、连接故障或服务异常导致。以下提供系统化的排查步骤与解决方案,帮助用户快速定位并修复问题。一、基础故障快速检查1.物理连接确认有线网络:检...

Python教程(十九):文件操作(python操作文件夹)

昨天,我们学习了列表推导式,掌握了Python中最优雅的数据处理方式。今天,我们将学习文件操作—Python中读写文件的基础技能。文件操作是编程中的核心技能,无论是读取配置文件、保存用户数据,还是...

取消回复欢迎 发表评论: