PROC文件tcp_fin_timeout默认为60秒,内核中相应的变量为init_net.ipv4.sysctl_tcp_fin_timeout,不过其以jiffies表示,默认值为TCP_FIN_TIMEOUT,即(60 * HZ)。此值表示一个不再被应用层使用(执行了close调用)的TCP连接处于FIN_WAIT_2状态的时长,如果在此时间内未能接收到对端的FIN结束报文,内核将复位此连接。但是除此之外,如果应用层是执行shutdown(SHUT_WR)操作关闭了套接口的发送,TCP连接还可进行接收操作,此种情况下的TCP连接处于FIN_WAIT_2状态不受tcp_fin_timeout的时间限制,将会永久的等待对端去关闭连接或者本地使用close关闭。

$ cat /proc/sys/net/ipv4/tcp_fin_timeout
60
$
static struct ctl_table ipv4_net_table[] = {{.procname   = "tcp_fin_timeout",.data       = &init_net.ipv4.sysctl_tcp_fin_timeout,},
}#define TCP_TIMEWAIT_LEN (60*HZ)
#define TCP_FIN_TIMEOUT TCP_TIMEWAIT_LEN static int __net_init tcp_sk_init(struct net *net)
{net->ipv4.sysctl_tcp_retries1 = TCP_RETR1;net->ipv4.sysctl_tcp_retries2 = TCP_RETR2;net->ipv4.sysctl_tcp_orphan_retries = 0;net->ipv4.sysctl_tcp_fin_timeout = TCP_FIN_TIMEOUT;
}

linger2时长

sysctl_tcp_fin_timeout是针对所有套接口的全局配置,内核针对单个套接口的FIN_WAIT_2超时时间提供了linger2套接口选项,其值优先级高于全局的sysctl_tcp_fin_timeout。应用层可通过setsockopt系统调用设置套接口的linger2值,用户层以秒值下发,内核中将其装换为jiffies为单位的值保存在TCP套接口结构的linger2成员中。linger2的值不能够大于sysctl_tcp_fin_timeout的时间值,否则将其置为0。另外,如果用户层下发的值小于0,linger2设置为-1。TCP_LINGER2的设置逻辑如下:

static int do_tcp_setsockopt(struct sock *sk, int level, int optname, char __user *optval, unsigned int optlen)
{struct tcp_sock *tp = tcp_sk(sk);switch (optname) {case TCP_LINGER2:if (val < 0)tp->linger2 = -1;else if (val > net->ipv4.sysctl_tcp_fin_timeout / HZ)tp->linger2 = 0;elsetp->linger2 = val * HZ;break;}
}

内核中获取FIN_WAIT_2超时时间由函数tcp_fin_time实现。由其代码可见如果linger2的时间值不为零,取其值,否则,使用sysctl_tcp_fin_timeout的时间值。但是,最终的FIN_WAIT_2超时时间还与当前连接的超时重传时间RTO有关,其不能大于RTO的3.5倍(rto << 2) - (rto >> 1)的结果值。RTO*3.5表示的时长可允许对端的FIN报文重传2次。

static inline int tcp_fin_time(const struct sock *sk)
{int fin_timeout = tcp_sk(sk)->linger2 ? : sock_net(sk)->ipv4.sysctl_tcp_fin_timeout;const int rto = inet_csk(sk)->icsk_rto;if (fin_timeout < (rto << 2) - (rto >> 1))fin_timeout = (rto << 2) - (rto >> 1);return fin_timeout;
}

超时定时器设置

FIN_WAIT_2状态的超时定时器设置分两种情况,其一是由应用层的shutdown系统调用所引发;其二是由close系统调用引发。先看第一种情况,shutdown可关闭接收或者发送方向的流量(仅关闭发送方向时触发FIN报文发送),导致套接口处于半关闭状态,并且套接口状态走到FIN_WAIT_1(关闭接收方向套接口状态不变),等待对端响应ACK报文。如果对端不响应ACK报文,本端FIN报文会进行超时重传,直到出错处理。

反之,当接收到对端回应的ACK报文时,处理流程进入函数tcp_rcv_state_process的TCP_FIN_WAIT1分支。由于此时应用层并没有close套接口,其SOCK_DEAD标志未设置,仅是将套接口的状态设置为TCP_FIN_WAIT2,退出处理流程,留待本端应用层close系统调用去结束连接;或者对端发送FIN报文来结束连接。

套接口的SOCK_DEAD标志没有置位还有一种可能是,应用层调用了close接口,但是设置了套接口的SOCK_LINGER选项,注意其与TCP_LINGER2不同,设置了此选项后,tcp_close函数不会立即置位SOCK_DEAD,而是等待sk_lingertime规定的时长,所以即使应用层调用了tcp_close操作,如果还在sk_lingertime时长内,SOCK_DEAD标志也还没有设置。通常应用层未设置SOCK_LINGER选项,这不是默认情况。如果是应用层进程在退出时自动调用了套接口的close函数,内核将忽略SOCK_LINGER选项设置不执行等待。

int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb)
{switch (sk->sk_state) {case TCP_FIN_WAIT1: {tcp_set_state(sk, TCP_FIN_WAIT2);sk->sk_shutdown |= SEND_SHUTDOWN;if (!sock_flag(sk, SOCK_DEAD)) {sk->sk_state_change(sk);break;}

但是,如果本地的应用层直接使用close调用完全关闭双向的连接而不是shutdown,看一下tcp_rcv_state_process函数接下来的处理。如果用户层设置的套接口linger2值小于零,内核将不会在FIN_WAIT_2状态等待,直接销毁套接口。如果FIN_WAIT_2的超时时间(tmo)大于TCP_TIMEWAIT_LEN(60秒)的时长,启动keepalive定时器,定时时长为二者之差(tmo - TCP_TIMEWAIT_LEN)。以上对tcp_fin_time函数的介绍可知,默认情况下其值等于TCP_TIMEWAIT_LEN的值,内核默认不执行此分支。

如果完全关闭的套接口在FIN_WAIT_1状态时,接收到的是一个带有FIN标志的报文(FIN+ACK报文)或者此处的套接口还未被应用层释放。启动keepalive定时器,定时时长为FIN_WAIT_2的超时时间。需要注意的是sock_owned_by_user能够成立的条件十分苛刻:仅在tcp_close函数释放套接口owner前,release_sock函数的spin_lock_bh未获得锁而等待时发生。

对于HTTP服务端来说,如果客户端发送了FIN报文结束连接,HTTP服务端通常回复FIN+ACK报文,实现3个报文结束连接。所以通常情况下客户端会启动tmo时长的keepalive定时器。

        if (tp->linger2 < 0) {tcp_done(sk);NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONDATA);return 1;}tmo = tcp_fin_time(sk);if (tmo > TCP_TIMEWAIT_LEN) {inet_csk_reset_keepalive_timer(sk, tmo - TCP_TIMEWAIT_LEN);} else if (th->fin || sock_owned_by_user(sk)) {inet_csk_reset_keepalive_timer(sk, tmo);} else {tcp_time_wait(sk, TCP_FIN_WAIT2, tmo);goto discard;}break;}
}

除了以上情况之外,如果FIN_WAIT_2的超时时间小于等于TCP_TIMEWAIT_LEN的值,并且接收到的报文不带有FIN标志(仅ACK),此种情况为TCPIP协议中定义的FIN_WAIT_2状态。启动TIME_WAIT定时器。定时时长设置为FIN_WAIT_2的超时时间,如果其小于当前连接的重传超时时间RTO的话,使用RTO时间,以允许FIN报文至少重传一次。tcp_time_wait函数最后销毁TCP套接口。

void tcp_time_wait(struct sock *sk, int state, int timeo)
{struct inet_timewait_sock *tw;if (timeo < rto)timeo = rto;inet_twsk_schedule(tw, timeo);tcp_done(sk);
}

最后,接着半连接的套接口流程。如果应用层使用close系统调用主动关闭此半连接时,套接口的状态已经处于TCP_FIN_WAIT2了(tcp_rcv_state_process函数中设置)。如果TCP套接口的linger2小于零,直接发送重置reset报文;否则,根据FIN_WAIT_2的超时时间与TCP_TIMEWAIT_LEN的大小关系,启动keepalive定时器或者time_wait定时器,防止对端一直不发送FIN结束报文导致本端套接口不能释放。

但是,如果本地应用层不close此半连接,并且对端也不结束连接,不发送FIN报文,此连接将一直存在。

void tcp_close(struct sock *sk, long timeout)
{if (sk->sk_state == TCP_FIN_WAIT2) {struct tcp_sock *tp = tcp_sk(sk);if (tp->linger2 < 0) {tcp_set_state(sk, TCP_CLOSE);tcp_send_active_reset(sk, GFP_ATOMIC);__NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONLINGER);} else {const int tmo = tcp_fin_time(sk);if (tmo > TCP_TIMEWAIT_LEN) {inet_csk_reset_keepalive_timer(sk, tmo - TCP_TIMEWAIT_LEN);} else {tcp_time_wait(sk, TCP_FIN_WAIT2, tmo);goto out;}}}
}

定时器处理

如上所述套接口的FIN_WAIT_2状态没有单独的定时器,其使用TCP的保活定时器keepalive和time_wait定时器实现计时。保活定时器超时处理函数为tcp_keepalive_timer,如果TCP套接口的linger2大于等于零,并且tcp_fin_time得到的超时时间大于TCP_TIMEWAIT_LEN(60秒)时间时,启动time_wait定时器,定时时长设置为二者差值;否则,直接发送重置reset报文到对端。默认情况下tcp_fin_time时间等于TCP_TIMEWAIT_LEN时间,故在FIN_WAIT_2状态超时的处理为发送连接重置reset报文。

static void tcp_keepalive_timer (struct timer_list *t)
{struct sock *sk = from_timer(sk, t, sk_timer);struct tcp_sock *tp = tcp_sk(sk);if (sk->sk_state == TCP_FIN_WAIT2 && sock_flag(sk, SOCK_DEAD)) {if (tp->linger2 >= 0) {const int tmo = tcp_fin_time(sk) - TCP_TIMEWAIT_LEN;if (tmo > 0) {tcp_time_wait(sk, TCP_FIN_WAIT2, tmo);goto out;}}tcp_send_active_reset(sk, GFP_ATOMIC);goto death;}
}

定时器time_wait的超时处理函数为tw_timer_handler,清理time_wait套接口。

static void tw_timer_handler(struct timer_list *t)
{struct inet_timewait_sock *tw = from_timer(tw, t, tw_timer);if (tw->tw_kill)__NET_INC_STATS(twsk_net(tw), LINUX_MIB_TIMEWAITKILLED);else__NET_INC_STATS(twsk_net(tw), LINUX_MIB_TIMEWAITED);inet_twsk_kill(tw);
}

内核版本 4.15.0

TCP套接口的FIN_WAIT_2状态超时相关推荐

  1. TCP套接口的sk_backlog接收队列

    在接收到数据包之后,如果判断此套接口当前正被用户进程所使用,数据包将被保存到套接口结构的sk_backlog成员的head所定义的skb缓存列表中,tail指向链表的末尾,len变量记录了当前链表中所 ...

  2. TCP套接口热迁移REPAIR模式

    要实现TCP套接口的热迁移,必须能够实现在迁移之前保存套接口的当前状态,迁移之后还原套接口的状态.Linux内核中为支持TCP套接口热迁移实现了REPAIR模式以及相关的操作.迁移流程如下,首先启用R ...

  3. TCP套接口的最大SYN队列长度

    通过PROC文件查看队列长度,可见对于4G内存的系统,tcp_max_syn_backlog的值为128:对于8G内存的系统,其值为256. # cat /proc/sys/net/ipv4/tcp_ ...

  4. 网络编程学习笔记(TCP套接口选项)

    其套接口级别为IPPROTO_TCP TCP_KEEPALIVE: 指定TCP开始发送保持存活探测分节前以秒为单位的连接空闲时间.此选项在SO_KEEPALIVE套接口选项打开时才有效 TCP_MAX ...

  5. 网络编程学习笔记(套接口超时)

    有三种方法给套接口上的I/O操作设置 超时: 1.调用 alarm,在到达指定时间 时产生 SIGALRM信号,可能与进程中其他已有的alarm调用 冲突 2.使用select阻塞在等待I/O上,se ...

  6. 网络分析流量FIN_WAIT_2状态解释

    一.概述 在HTTP应用中,存在一个问题,SERVER由于某种原因关闭连接,如KEEPALIVE的超时,这样,作为主动关闭的SERVER一方就会进入 FIN_WAIT2状态,但TCP/IP协议栈有个问 ...

  7. 网络编程学习笔记(基本套接口选项)

    SO_BROADCAST套接口选项: 此选项使能或禁止进程发送广播消息的能力.只有数据报套接口支持广播,并且还必须是在支持广播消息的网络上(例如以太网.令牌网).不能在一个点对点链路上进行广播. SO ...

  8. FIN_WAIT_2状态解释

    在HTTP应用中,存在一个问题,SERVER由于某种原因关闭连接,如KEEPALIVE的超时,这样,作为主动关闭的SERVER一方就会进入 FIN_WAIT2状态,但TCP/IP协议栈有个问题,FIN ...

  9. 网络的FIN_WAIT_2状态解释和分析

    网络的FIN_WAIT_2状态解释和分析 一.总结 一句话总结:出现fin_wait_2一般为客户端,如果为服务端出现,则表明是服务端主动发起的断开 还是要系统的学,这样很有问题,学不到什么,系统看书 ...

最新文章

  1. 一口气看完45个寄存器,CPU核心技术大揭秘
  2. struts.xml mysql_mybatis3.3 + struts2.3.24 + mysql5.1.22开发环境搭建及相关说明
  3. centos安装与配置R语言
  4. lr java脚本_【上海校区】 LR Java脚本编写方法
  5. python文件数据类型_Python核心数据类型——文件
  6. Cordova原理一
  7. python 根据关键字 切割pdf_用python拆分pdf
  8. java编程软件安装
  9. Jrebel激活服务,Jrebel激活,Jrebel激活码,Jrebel破解
  10. 计算机教室不安风扇,多媒体教室设备常见故障及解决办法
  11. awgn matlab,Matlab实现加性高斯白噪声信道(AWGN)下的digital调制格式识别分类
  12. VS2010中水晶报表插件下载安装方法 详细出处参考:http://www.jb51.net/softjc/88860.html
  13. appium元素坐标定位TouchAction
  14. Tracer 记录 Controller 日志
  15. 浅谈单元测试之(一):单元测试的意义
  16. uni-app自定义页面导航内容
  17. 生物信息学入门 GEO芯片数据差异表达分析时需要log2处理的原因
  18. Bhuman应用篇——守门员防守之SpecialAction
  19. 使用轻量应用服务器部署Docsify在线文档平台
  20. 系统重构是个什么玩意儿

热门文章

  1. 修改Vue项目网页标题和ico
  2. vulnhub:THOTH TECH:1靶机
  3. 阿里云漏洞验证需要付费的解决方法
  4. com.netflix.discovery.shared.transport.TransportException: Cannot execute request on any known serve
  5. DB2数据库安装与配置
  6. libxml2对XML文件的创建、解析、查找、修改
  7. MFC CFile 读写文件
  8. IndexedDB踩坑必须注意点!!!
  9. OC xcode 两个view重叠,先后层次关系的调整
  10. react18中使用react-hook-form