0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

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

冬至子 来源:Linux码农 作者:Linux码农 2022-11-16 17:53 次阅读

在 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          
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


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 报文,同时释放接收和发送队列上的数据。

审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • RST
    RST
    +关注

    关注

    0

    文章

    31

    浏览量

    7377
  • TCP协议
    +关注

    关注

    1

    文章

    91

    浏览量

    12056
收藏 人收藏

    评论

    相关推荐

    ESP32C6 WiFi报文出现大量重传是什么原因导致的?

    与 STA的距离很近,不存在信号差的情况,请问可能是什么原因导致的? 此外,虽然应用层每隔100ms请求发一次WiFi报文,但从wireshark捕获的报文看,报文
    发表于 06-06 07:55

    w5500 作为tcp server,客户端异常发送RST,ACK】断开连接问题

    测试1 w5500 作为tcp server,上位机labveiw程序作为clinet,正常第二次交互结束后,客户端发送一个【RST,ACK】报文断开
    发表于 10-14 14:01

    CC2540应用层的接受数据的buffer在哪

    CC2540应用层的接受数据的buffer在哪,因为,我想外扩CPU,需要做个透明传输。还望指点主从设备接受发送buffer分别在源程序哪里
    发表于 02-23 10:00

    14-TCP 协议(连接异常与RST

    release)。也可以通过发送 RST 段给对端来释放连接,这种方式称为异常释放(abortive release)。异常终止连接有两个特点:丢弃任何尚未
    发表于 07-24 10:01

    【学习打卡】OpenHarmony的应用层说明

    OpenHarmony的应用层包括系统应用和第三方非系统应用。什么是应用层应用层其实就是开放系统互连 ( OSI ) 通信模型的顶层。它确保应用程序可以有效地与不同计算机系统和网络
    发表于 07-14 08:44

    CH582如何强制发送广播?

    RT,原来使用PHY直接发,现在功能升级,使用了外设模式。通过广播发送状态,当状态改变的时候如何强制发送?不然广播间隔较长,要等到下次广播。还有能否动态修改广播长度?如
    发表于 08-01 07:52

    LWIP TCP报文基础知识及其LWIP中TCP协议的实现

    接收完成后才将数据递交到应用层。2 TCP 报文段结构TCP 报文段依赖 IP 协议进行发送,因此 TCP
    发表于 10-18 14:54

    tcp连接断开连接图解

    假设Client端发起中断连接请求,也就是发送FIN报文。Server端接到FIN报文后,意思是说“我Client端没有数据要发给你了”,但是如果你还有数据没有
    发表于 12-08 13:53 1.5w次阅读
    tcp<b class='flag-5'>连接</b>与<b class='flag-5'>断开</b><b class='flag-5'>连接</b>图解

    关于tcp协议栈中rst报文的seq跳变问题

    导致内核协议栈发送了一个rst报文,而rst报文选取seq的时候,并不是选取的确定已经发送的se
    的头像 发表于 07-27 15:26 4999次阅读
    关于tcp协议栈中<b class='flag-5'>rst</b><b class='flag-5'>报文</b>的seq跳变问题

    如何解释TCP报文的内容

    TCP协议有着自己的数据包格式,这里把TCP的数据包称为报文段(segment),TCP报文段封装在IP数据报中发送,TCP报文段由TCP首部和TCP数据区组成,首部区域包含了
    的头像 发表于 08-31 09:12 2643次阅读

    为什么系统会自动回复RST报文

    从结果中可以看到 10.223.12.10 在接收到对端回应的 syn + ack 后,系统会自动给对端回应一个 RST 复位报文,导致二者的链路断开
    的头像 发表于 11-16 17:19 1986次阅读

    应用层知多少?(总结在末尾)

    的语法、语义;进程何时、如何发送报⽂,以及对报⽂进⾏响应的规则。应用层功能与协议:域名服务:DNS;⽂件传输:FTP;电⼦邮件:SMTP、POP3;远程登陆:TE
    的头像 发表于 08-26 11:16 1309次阅读
    <b class='flag-5'>应用层</b>知多少?(总结在末尾)

    物联网的技术架构及应用层是什么?

    物联网的技术架构包括感知、网络、平台应用层应用层是物联网的顶层,它的主要功能是将感知
    的头像 发表于 07-15 08:56 3560次阅读

    基于TCP应用层协议

    ; 我们把携带RST标识的称为复位报文段 SYN: 请求建立连接; 我们把携带SYN标识的称为同步报文段 FIN: 通知对方, 本端要关闭了, 我们称携
    的头像 发表于 11-11 11:23 891次阅读
    基于TCP<b class='flag-5'>应用层</b>协议

    CAN报文为什么会发送失败?

    CAN总线调试过程中出现报文发送失败。很多工程师都对此只知其一不知其二,今天我们就以CAN报文发送失败的问题来做一次探讨。在了解CAN报文
    的头像 发表于 04-12 08:25 1827次阅读
    CAN<b class='flag-5'>报文</b>为什么会<b class='flag-5'>发送</b>失败?