linux tcp的timewait如何解决

linux tcp的timewait如何解决本文从内核的角度看timewait是如何解决的。贴代码,和网上看到的挺多冲突的!

大家好,又见面了,我是你们的朋友全栈君。

开头

本文从内核的角度看timewait是如何解决的。贴代码,和网上看到的挺多冲突的!

1. timewait是什么

timewait在tcp结束后主动关闭一方的等待时候的行为。图片中的服务和客户端描述不是非常准确,这里客户端是主动关闭一方。(在web服务器模型下,web服务器也可主动关闭客户端,这个时候web服务器就变成了四次握手的客户端)。

tcp的四次握手

2. timewait在客户端的问题

这里的客户端,不是四次握手的客户端,而是指发起tcp请求的一方。发起一方需要绑定本地端口,本地端口的绑定方式非常暴力:直接是从配置的偶数值开始遍历查找可用端口:(偶数端口和奇数端口内核的一个patch

  1. 在占用端口中,查看是否可以回收利用:如果可以回收,则返回;如果不能,继续查找下一个端口。
  2. 未被占中,直接返回

如果发起大量的客户端请求,并且不能回收,系统调用connect时长增加,甚至直接因端口耗尽直接调用失败。

int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{ 

// 本地端口绑定初始化
err = inet_hash_connect(tcp_death_row, sk);
}
int inet_hash_connect(struct inet_timewait_death_row *death_row,
struct sock *sk)
{ 

return __inet_hash_connect(death_row, sk, port_offset,
__inet_check_established);
}
// inet_hash_connect调用下面函数
int __inet_hash_connect(struct inet_timewait_death_row *death_row,
struct sock *sk, u32 port_offset,
int (*check_established)(struct inet_timewait_death_row *,
struct sock *, __u16, struct inet_timewait_sock **))
{ 

inet_get_local_port_range(net, &low, &high);
other_parity_scan:
port = low + offset;
for (i = 0; i < remaining; i += 2, port += 2) { 

inet_bind_bucket_for_each(tb, &head->chain) { 

if (net_eq(ib_net(tb), net) && tb->l3mdev == l3mdev &&
tb->port == port) { 

if (tb->fastreuse >= 0 ||
tb->fastreuseport >= 0)
goto next_port;
WARN_ON(hlist_empty(&tb->owners));
// 这里主要是调用tcp_twsk_unique,保证开启了timestamp就可以连接
if (!check_established(death_row, sk,
port, &tw))
goto ok;
goto next_port;
}
}
tb = inet_bind_bucket_create(hinfo->bind_bucket_cachep,
net, head, port, l3mdev);
if (!tb) { 

spin_unlock_bh(&head->lock);
return -ENOMEM;
}
tb->fastreuse = -1;
tb->fastreuseport = -1;
goto ok;
next_port:
spin_unlock_bh(&head->lock);
cond_resched();
}
}

3. timewait如何解决

端口重用的逻辑从__inet_check_established->tcp_twsk_unique(源码),总结下逻辑:

  • 当本次连接和上次四元组不同时,可以立即复用端口,不用开启任何选项 .
  • 当和上一次四元组一样时,需要满足timewait可重用条件,则可以复用,否则不能用该端口。

timewait满足的条件:

  • 开启timestamp
  • twp为null或者reuse开启时间戳满足要求;客户端的主动连接跟踪代码twp赋值为null所以天然满足。

所以需要解决timewait的客户端问题有三个办法:

  1. 上游节点分散处理,尽量保证四元祖不一样
  2. 开启timestamp
  3. 限制timewait的数量,sysctl_max_tw_buckets

timewait端口重用的逻辑:

static int __inet_check_established(struct inet_timewait_death_row *death_row,
struct sock *sk, __u16 lport,
struct inet_timewait_sock **twp)
{ 

struct inet_hashinfo *hinfo = death_row->hashinfo;
struct inet_sock *inet = inet_sk(sk);
__be32 daddr = inet->inet_rcv_saddr;
__be32 saddr = inet->inet_daddr;
int dif = sk->sk_bound_dev_if;
struct net *net = sock_net(sk);
int sdif = l3mdev_master_ifindex_by_index(net, dif);
INET_ADDR_COOKIE(acookie, saddr, daddr);
const __portpair ports = INET_COMBINED_PORTS(inet->inet_dport, lport);
unsigned int hash = inet_ehashfn(net, daddr, lport,
saddr, inet->inet_dport);
struct inet_timewait_sock *tw = NULL;
sk_nulls_for_each(sk2, node, &head->chain) { 

if (sk2->sk_hash != hash)
continue;
if (likely(INET_MATCH(sk2, net, acookie,
saddr, daddr, ports, dif, sdif))) { 

if (sk2->sk_state == TCP_TIME_WAIT) { 

tw = inet_twsk(sk2);
if (twsk_unique(sk, sk2, twp))
break;
}
goto not_unique;
}
}
return 0;
not_unique:
spin_unlock(lock);
return -EADDRNOTAVAIL;
}
static inline int twsk_unique(struct sock *sk, struct sock *sktw, void *twp)
{ 

if (sk->sk_prot->twsk_prot->twsk_unique != NULL)
return sk->sk_prot->twsk_prot->twsk_unique(sk, sktw, twp);
return 0;
}
int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp)
{ 

if (tcptw->tw_ts_recent_stamp &&
(!twp || (reuse && time_after32(ktime_get_seconds(),
tcptw->tw_ts_recent_stamp)))) { 

if (likely(!tp->repair)) { 

u32 seq = tcptw->tw_snd_nxt + 65535 + 2;
if (!seq)
seq = 1;
WRITE_ONCE(tp->write_seq, seq);
tp->rx_opt.ts_recent	   = tcptw->tw_ts_recent;
tp->rx_opt.ts_recent_stamp = tcptw->tw_ts_recent_stamp;
}
sock_hold(sktw);
return 1;
}
}

限制timewait代码:

void tcp_fin(struct sock *sk)
{ 

case TCP_FIN_WAIT2:
/* Received a FIN -- send ACK and enter TIME_WAIT. */
tcp_send_ack(sk);
tcp_time_wait(sk, TCP_TIME_WAIT, 0);
break;
}
struct inet_timewait_sock *inet_twsk_alloc(const struct sock *sk,
struct inet_timewait_death_row *dr,
const int state)
{ 

struct inet_timewait_sock *tw;
if (atomic_read(&dr->tw_count) >= dr->sysctl_max_tw_buckets)
return NULL;
}

4 服务器端timewait有什么影响

服务器(非tcp四次握手的服务器,指如web服务器)的timewait主动端开,端口都是同一个不影响端口,但是占用机器资源。此类影响,可以从内存方面分析。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/132781.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 一键批量打印EXCEL、WORD文档

    一键批量打印EXCEL、WORD文档

    2021年10月10日
  • 500-内部服务器错误_win7无法启动server服务错误1083

    500-内部服务器错误_win7无法启动server服务错误1083我们在操作win10系统电脑的时候,常常会遇到win10系统提示http500内部服务器错误的情况,想必大家都遇到过win10系统提示http500内部服务器错误的情况吧,那么应该怎么处理win10系统提示http500内部服务器错误呢?我们依照在ie浏览器中点击右上角的设置按钮,在弹出的菜单中点击“internet选项”按钮;在internet选项界面切换到【高级】选卡;这样的步骤就行…

  • 数据分析sql面试必会6题经典_SQL常见面试题[通俗易懂]

    数据分析sql面试必会6题经典_SQL常见面试题[通俗易懂]SQL主要是用来到数据库里查询数据,这是数据分析过程的第一步。你要分析数据,首先要获取数据。所以,这是转行到数据分析岗位的必备技能。感谢@猴子老师的“猴子聊人物”公众号,我入门SQL就是在猴子的“转行数据分析师闯关教程”里学到的。柯本:新手如何学习SQL​zhuanlan.zhihu.com柯本:《MySQL必知必会》学习小结​zhuanlan.zhihu.com一、SQL性能优化题SQL语句…

  • Java—重写与重载的区别

    Java—重写与重载的区别Java—重写与重载的区别这几周开始看Java的知识,发现有一个有趣的现象就是,前两天刚看过的知识点,过一天又忘掉了。而且很多东西堆在脑子里像浆糊一样。所以边学习边总结是很重要的,今天想写一篇关于重写和重载的博客,为什么?因为面试会问啊,这是基础中比较重要的地方,但我百度了几篇博客之后发现写的都差强人意,各有缺点,但是!!访问量都特别高,所以我决定自己好好总结一篇自己的博客,也算是给自己的学习…

  • Spring中所使用的设计模式

    Spring中所使用的设计模式

  • git项目怎么用_git详细教程

    git项目怎么用_git详细教程项目开发git的基本使用流程项目开发git仓库使用流程一、新建项目以及首次推送1、git上新建项目2、在项目下创建属于该项目的代码仓库(选择私有仓库)3、完成创建后在自己本地的项目文件夹下使用“gitinit”初始化该文件夹4、“gitadd.”将工程内的所有文件放入暂存区5、”gitcommit-m“xxxxxxx””这次提交的信息,”xxxxxx”提交备注尽可能的写的详细,方面后续查找问题6、“gitremoteaddoriginxxxxxxxxxxxxx

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号