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主动端开,端口都是同一个不影响端口,但是占用机器资源。此类影响,可以从内存方面分析。

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

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

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • snmp trap服务_SNMP服务

    snmp trap服务_SNMP服务浅谈Linux中开发SNMPTrapSNMPTrap是一种标准的报告机制,广泛应用在各种网络管理软件中。本文详细介绍了SNMPTrap的基本概念,以及Linux下面net-snmp对SNMPTrap的开发与应用。1.SNMPTrap简介SNMP简单网络管理协议(SimpleNetworkManagementProtocol)是一种应用层协议,是TCP/IP协议族的一部分。它使网络设备之间能…

    2022年8月22日
    4
  • pycharm社区版安装教程 2019_pycharm安装教程2020社区版

    pycharm社区版安装教程 2019_pycharm安装教程2020社区版首先进入JetBrain的官网(国内正常访问):https://www.jetbrains.com/第一眼看到的界面如下图所示:然后找到我们的Pycharm专题页:进入Pycharm的专题页面之后,点击下载按钮(这里有两个按钮,点任何一个都行):然后进入到真正的下载页面你会发现有两个版本的Pycharm,一个是Professional版本(收费),另外一个是Community版本是永久免费的,而且后续升级什么的也都是免费的,我们下载这个就行了,Comm…

    2022年8月26日
    2
  • MySQL之权限管理

    MySQL之权限管理MySQL之权限管理

    2022年4月24日
    50
  • 基于麦克风阵列的声源定位_python播放声音模块

    基于麦克风阵列的声源定位_python播放声音模块上一篇文章说到odas_web界面非常难安装,并且运行也很卡。所以我自己用python写了一个界面程序,用来接收odas处理完的结果。这个界面程序与odas之间是通过socket连接的,界面作为服务器,odas作为客户端,由于有两路数据,所以各有两个服务器和客户端。但是实际绘制在界面上的是SSL的结果,不是SST的结果。其实我也试过SST的结果,从直观的感受而言,效果会比SSL差一些,实时性不是很高,我的理解SST的好处是可以跟踪音源是否有活动。

    2022年9月2日
    5
  • kaptcha验证码使用

    kaptcha验证码使用参照文章完善:http://blog.csdn.net/pandakong/article/details/8799534效果图:官方地址:https://code.google.com/p/kaptcha/w/listkaptcha是一个非常实用的验证码生成工具。有了它,你可以生成各种样式的验证码,因为它是可配置的。kaptcha工作的原理是调用c

    2022年6月18日
    18
  • IIS启动就报错:发生意外错误0x8ffe2740

    IIS启动就报错:发生意外错误0x8ffe2740IIS启动就报错:发生意外错误0x8ffe2740现象:  IIS的WEB服务器(停止),启动不了。启动就报错:发生意外错误0x8ffe2740。分析:  如果系统中存在端口冲突就有可能发生本情况.IIS默认使用80端口进行HTTP通信.如果除IIS外的应用程序正在运行并且正在相同的IP…

    2022年7月26日
    6

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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